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