Subversion Repositories tpanel

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
114 andreas 1
/*
2
 * Copyright (C) 2022 by Andreas Theofilu <andreas@theosys.at>
3
 *
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU General Public License as published by
6
 * the Free Software Foundation; either version 3 of the License, or
7
 * (at your option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, write to the Free Software Foundation,
16
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
17
 */
18
 
19
#include <iostream>
20
#include <fstream>
21
#include <cstring>
22
#include <iomanip>
236 andreas 23
#include <algorithm>
235 andreas 24
#ifdef __LINUX__
114 andreas 25
#include <bits/stdc++.h>
119 andreas 26
#endif
125 andreas 27
 
114 andreas 28
#include "readtp4.h"
29
#include "expand.h"
30
#include "terror.h"
31
 
125 andreas 32
#if __cplusplus < 201402L
33
#   error "This module requires at least C++14 standard!"
119 andreas 34
#else
125 andreas 35
#   if __cplusplus < 201703L
36
#       include <experimental/filesystem>
37
        namespace fs = std::experimental::filesystem;
38
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
39
#   else
119 andreas 40
#       include <filesystem>
125 andreas 41
#       ifdef __ANDROID__
42
            namespace fs = std::__fs::filesystem;
43
#       else
44
            namespace fs = std::filesystem;
45
#       endif
119 andreas 46
#   endif
47
#endif
125 andreas 48
 
114 andreas 49
using namespace reader;
50
using namespace std;
51
 
52
ReadTP4::~ReadTP4()
53
{
117 andreas 54
    DECL_TRACER("ReadTP4::~ReadTP4()");
55
 
114 andreas 56
    deleteIndex();
57
}
58
 
117 andreas 59
bool ReadTP4::isReady()
60
{
61
    DECL_TRACER("ReadTP4::isReady()");
62
 
63
    if (access(fname.c_str(), R_OK) != 0)
64
        return false;
65
 
66
    if (access(target.c_str(), F_OK) == 0)
67
    {
68
        if (access(target.c_str(), R_OK | W_OK | X_OK) != 0)
69
            return false;
70
 
71
        return true;
72
    }
73
 
74
    return true;
75
}
76
 
114 andreas 77
bool ReadTP4::doRead()
78
{
79
    if (fname.length() == 0)
80
    {
81
        MSG_ERROR("Missing input file!");
82
        return false;
83
    }
84
 
85
    ifstream fn(fname, ios::in | ios::binary);
86
 
87
    if (!fn.is_open())
88
    {
89
        MSG_ERROR("Could not open file " << fname << "!");
90
        return false;
91
    }
92
 
93
    struct HEADER head;
94
    struct BLOCK block;
95
    struct USAGE_BLOCK usageBlock;
96
    struct FILE_HEAD fhead;
97
 
98
    unsigned char memblock[1024];
99
 
100
    try
101
    {
102
        fn.read(reinterpret_cast<char*>(&head), SIZE_HEADER);
103
 
104
        if (memcmp(head.abyFileID, "\0FSFILE\0", 8) != 0)
105
        {
106
            MSG_ERROR("File " << fname << " is not an FSF file!");
107
            fn.close();
108
            return false;
109
        }
110
 
111
        fn.read(reinterpret_cast<char*>(&memblock[0]), SIZE_BLOCK);
112
        fillBlock(block, memblock);
113
        fillFileHead(fhead, memblock);
114
 
115
        uint32_t nextBlock = head.listStartBlock;
116
 
117
        // first we read the index
118
        while (nextBlock > 0)
119
        {
120
            fn.seekg(calcBlockPos(nextBlock), ios::beg);
121
            fn.read(reinterpret_cast<char*>(memblock), SIZE_BLOCK);
122
            fillUsageBlock(usageBlock, memblock);
123
            fillFileHead(fhead, memblock);
124
 
125
            if (fhead.thisBlock != nextBlock)
126
            {
127
                MSG_ERROR("No valid block position (" << to_string(fn.tellg()) << " [" << toHex(fn.tellg(), 8) << "])");
128
                break;
129
            }
130
 
131
            appendUBlock(&usageBlock);
132
            nextBlock = fhead.nextBlock;
133
        }
134
 
135
        // Now we read the files
136
        struct INDEX *act = idx;
137
 
138
        while (act != nullptr)
139
        {
140
            string ofile;
141
 
142
            string f(cp1250ToUTF8((char *)act->ublock->filePath));
143
            struct MANIFEST mf;
144
            mf.size = act->ublock->sizeBytes;
145
            mf.tmCreate = act->ublock->tmCreate;
146
            mf.tmModify = act->ublock->tmModify;
147
            mf.fname.assign(f);
148
            manifest.push_back(mf);
149
            string dir;
150
 
151
            if (f.find(".png") != string::npos || f.find(".jpg") != string::npos ||
152
                    f.find(".gif") != string::npos || f.find(".tiff") != string::npos)
153
                dir = "/images";
154
            else if (f.find(".wav") != string::npos || f.find(".mp3") != string::npos)
155
                dir = "/sounds";
156
            else if (f.find(".ttf") != string::npos)
157
                dir = "/fonts";
158
 
159
            if (!fs::exists(target + dir))
160
                fs::create_directories(target + dir);
161
 
162
            ofile = target + dir + "/" + f;
163
 
164
            MSG_DEBUG("Writing file " << ofile);
165
            ofstream of(ofile, ios::out | ios::binary);
166
 
167
            if (!of)
168
            {
169
                MSG_ERROR("Error opening target file" << ofile << "!");
170
                fn.close();
171
                return false;
172
            }
173
 
174
            nextBlock = act->ublock->startBlock;
175
            bool compressed = false;
176
 
177
            for (uint32_t i = 0; i < act->ublock->sizeBlocks; i++)
178
            {
179
                fn.seekg(calcBlockPos(nextBlock), ios::beg);
180
                fn.read(reinterpret_cast<char*>(memblock), SIZE_BLOCK);
181
                fillBlock(block, memblock);
182
 
183
                if (i == 0 && block.abyData[0] == 0x1f && block.abyData[1] == 0x8b)
184
                    compressed = true;
185
 
186
                nextBlock = block.nextBlock;
187
                of.write(reinterpret_cast<char*>(block.abyData), block.bytesUsed);
188
            }
189
 
190
            of.close();
191
 
192
            if (compressed)
193
            {
194
                MSG_DEBUG("Decompressing file " << ofile << " ...");
195
                Expand exp(ofile);
196
                int ret = exp.unzip();
197
 
198
                if (ret != 0)
199
                    MSG_WARNING("File " << ofile << " was not decompressed!");
200
            }
201
 
202
            act = act->next;
203
        }
204
 
205
        ofstream mfStream;
206
        string manFileName = target + "/manifest.xma";
207
 
208
        mfStream.open(manFileName, ios::out | ios::binary | ios::trunc);
209
        sort(manifest.begin(), manifest.end(), compareManifest);
210
        size_t num = manifest.size();
211
        size_t cnt = 0;
212
 
213
        for (auto itr = manifest.begin(); itr != manifest.end(); ++itr)
214
        {
215
            mfStream << itr->size << "|" << itr->tmCreate << "|" << itr->tmModify << "|" << itr->fname;
216
            cnt++;
217
 
218
            if (cnt == num)
219
                mfStream << "\r";
220
            else
221
                mfStream << "\r\n";
222
        }
223
 
224
        mfStream.close();
225
    }
226
    catch (exception& e)
227
    {
228
        MSG_ERROR("ReadTP4::doRead: " << e.what());
229
        fn.close();
230
        return false;
231
    }
232
 
233
    fn.close();
234
    return true;
235
}
236
 
237
void ReadTP4::fillBlock(struct BLOCK &bl, const unsigned char *buf)
238
{
239
    bl.thisBlock = makeDWord(buf);
240
    bl.prevBlock = makeDWord(buf + 4);
241
    bl.nextBlock = makeDWord(buf + 8);
242
    bl.bytesUsed = makeWord(buf + 12);
243
    memcpy(&bl.abyData, buf + 14, 512);
244
}
245
 
246
void ReadTP4::fillUsageBlock(struct USAGE_BLOCK &ub, const unsigned char *buf)
247
{
248
    ub.thisBlock = makeDWord(buf);
249
    ub.prevBlock = makeDWord(buf + 4);
250
    ub.nextBlock = makeDWord(buf + 8);
251
    ub.bytesUsed = makeWord(buf + 12);
252
    memcpy(&ub.filePath, buf + 14, 260);
253
    ub.tmCreate = makeDWord(buf + 274);
254
    ub.tmModify = makeDWord(buf + 278);
255
    ub.flags = makeDWord(buf + 282);
256
    ub.startBlock = makeDWord(buf + 286);
257
    ub.sizeBlocks = makeDWord(buf + 290);
258
    ub.sizeBytes = makeDWord(buf + 294);
259
}
260
 
261
void ReadTP4::fillFileHead(struct FILE_HEAD &fh, const unsigned char *buf)
262
{
263
    fh.thisBlock = makeDWord(buf);
264
    fh.prevBlock = makeDWord(buf + 4);
265
    fh.nextBlock = makeDWord(buf + 8);
266
    fh.blockLen = makeWord(buf + 12);
267
}
268
 
269
struct INDEX *ReadTP4::appendUBlock(const struct USAGE_BLOCK *ub)
270
{
271
    struct USAGE_BLOCK *nb;
272
    struct INDEX *act, *nidx;
273
 
274
    try
275
    {
276
        nb = new struct USAGE_BLOCK;
277
        memcpy(nb, ub, sizeof(struct USAGE_BLOCK));
278
 
279
        if (idx == nullptr)
280
        {
281
            act = new struct INDEX;
282
            act->prev = nullptr;
283
            act->next = nullptr;
284
            act->ublock = nb;
285
            idx = act;
286
        }
287
        else
288
        {
289
            act = idx;
290
 
291
            while (act->next != nullptr)
292
                act = act->next;
293
 
294
            nidx = new struct INDEX;
295
            nidx->prev = act;
296
            nidx->next = nullptr;
297
            nidx->ublock = nb;
298
            act->next = nidx;
299
            act = nidx;
300
        }
301
    }
302
    catch (exception& e)
303
    {
304
        MSG_ERROR("ReadTP4::appendUBlock: " << e.what());
305
        exit(3);
306
    }
307
 
308
    return act;
309
}
310
 
311
void ReadTP4::deleteIndex()
312
{
313
    struct INDEX *tmp, *act = idx;
314
 
315
    while (act != nullptr)
316
    {
317
        tmp = act->next;
318
 
319
        if (act->ublock != nullptr)
320
            delete act->ublock;
321
 
322
        delete act;
323
        act = tmp;
324
    }
325
 
326
    idx = nullptr;
327
}
328
 
329
size_t ReadTP4::calcBlockPos(uint32_t block)
330
{
331
    if (block == 0)
332
        return SIZE_HEADER;
333
 
334
    size_t first = SIZE_HEADER;
335
    return (first + (SIZE_BLOCK * block));
336
}
337
 
338
uint32_t ReadTP4::makeDWord(const unsigned char *buf)
339
{
340
    unsigned short b1, b2, b3, b4;
341
 
342
    b1 = *buf;
343
    b2 = *(buf + 1);
344
    b3 = *(buf + 2);
345
    b4 = *(buf + 3);
346
    uint32_t dword = ((b4 << 24) & 0xff000000) | ((b3 << 16) & 0x00ff0000) | ((b2 << 8) & 0x0000ff00) | b1;
347
    return dword;
348
}
349
 
350
uint16_t ReadTP4::makeWord(const unsigned char *buf)
351
{
352
    uint16_t word = ((*(buf + 1) << 8) & 0xff00) | *buf;
353
    return word;
354
}
355
 
356
string ReadTP4::toHex(int num, int width)
357
{
358
    string ret;
359
    std::stringstream stream;
360
    stream << std::setfill('0') << std::setw(width) << std::hex << num;
361
    ret = stream.str();
362
    return ret;
363
}
364
 
365
bool ReadTP4::compareManifest(struct MANIFEST& m1, struct MANIFEST& m2)
366
{
367
    size_t pos1 = m1.fname.find_last_of(".");
368
    size_t pos2 = m2.fname.find_last_of(".");
369
    string ext1, ext2;
370
    int weight1, weight2;
371
 
372
    if (pos1 != string::npos)
373
        ext1 = m1.fname.substr(pos1 + 1);
374
    else
375
        ext1 = m1.fname;
376
 
377
    if (pos2 != string::npos)
378
        ext2 = m2.fname.substr(pos2 + 1);
379
    else
380
        ext2 = m2.fname;
381
 
382
    if (ext1.compare("xma") == 0)
383
        weight1 = 1;
384
    else if (ext1.compare("xml") == 0)
385
        weight1 = 2;
386
    else if (ext1.compare("ttf") == 0)
387
        weight1 = 3;
388
    else if (ext1.compare("png") == 0 || ext1.compare("jpg") == 0 || ext1.compare("gif") == 0)
389
        weight1 = 4;
390
    else
391
        weight1 = 5;
392
 
393
    if (ext2.compare("xma") == 0)
394
        weight2 = 1;
395
    else if (ext2.compare("xml") == 0)
396
        weight2 = 2;
397
    else if (ext2.compare("ttf") == 0)
398
        weight2 = 3;
399
    else if (ext2.compare("png") == 0 || ext2.compare("jpg") == 0 || ext2.compare("gif") == 0)
400
        weight2 = 4;
401
    else
402
        weight2 = 5;
403
 
404
    if (weight1 == weight2)
405
        return m1.fname.compare(m2.fname) < 0;
406
 
407
    return weight1 < weight2;
408
}
409
 
410
string ReadTP4::cp1250ToUTF8(const string& str)
411
{
412
    string out;
413
 
414
    for (size_t j = 0; j < str.length(); j++)
415
    {
416
        int i = -1;
417
        unsigned char ch = str.at(j);
418
        short utf = -1;
419
 
420
        if (ch < 0x80)
421
        {
422
            do
423
            {
424
                i++;
425
 
426
                if (__cht[i].ch == ch)
427
                {
428
                    utf = __cht[i].byte;
429
                    break;
430
                }
431
            }
432
            while (__cht[i].ch != 0xff);
433
 
434
            if (utf < 0)
435
                utf = ch;
436
        }
437
        else
438
            utf = ch;
439
 
440
        if (utf > 0x00ff)
441
        {
442
            out.push_back((utf >> 8) & 0x00ff);
443
            out.push_back(utf & 0x00ff);
444
        }
445
        else if (ch > 0x7f)
446
        {
447
            out.push_back(0xc0 | ch >> 6);
448
            out.push_back(0x80 | (ch & 0x3f));
449
        }
450
        else
451
            out.push_back(ch);
452
    }
453
 
454
    return out;
455
}