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