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
125 andreas 26
 
114 andreas 27
#include "readtp4.h"
28
#include "expand.h"
29
#include "terror.h"
30
 
125 andreas 31
#if __cplusplus < 201402L
32
#   error "This module requires at least C++14 standard!"
119 andreas 33
#else
125 andreas 34
#   if __cplusplus < 201703L
35
#       include <experimental/filesystem>
36
        namespace fs = std::experimental::filesystem;
37
#       warning "Support for C++14 and experimental filesystem will be removed in a future version!"
38
#   else
119 andreas 39
#       include <filesystem>
125 andreas 40
#       ifdef __ANDROID__
41
            namespace fs = std::__fs::filesystem;
42
#       else
43
            namespace fs = std::filesystem;
44
#       endif
119 andreas 45
#   endif
46
#endif
125 andreas 47
 
114 andreas 48
using namespace reader;
49
using namespace std;
50
 
51
ReadTP4::~ReadTP4()
52
{
117 andreas 53
    DECL_TRACER("ReadTP4::~ReadTP4()");
54
 
114 andreas 55
    deleteIndex();
56
}
57
 
117 andreas 58
bool ReadTP4::isReady()
59
{
60
    DECL_TRACER("ReadTP4::isReady()");
61
 
62
    if (access(fname.c_str(), R_OK) != 0)
63
        return false;
64
 
65
    if (access(target.c_str(), F_OK) == 0)
66
    {
67
        if (access(target.c_str(), R_OK | W_OK | X_OK) != 0)
68
            return false;
69
 
70
        return true;
71
    }
72
 
73
    return true;
74
}
75
 
114 andreas 76
bool ReadTP4::doRead()
77
{
78
    if (fname.length() == 0)
79
    {
80
        MSG_ERROR("Missing input file!");
81
        return false;
82
    }
83
 
84
    ifstream fn(fname, ios::in | ios::binary);
85
 
86
    if (!fn.is_open())
87
    {
88
        MSG_ERROR("Could not open file " << fname << "!");
89
        return false;
90
    }
91
 
92
    struct HEADER head;
93
    struct BLOCK block;
94
    struct USAGE_BLOCK usageBlock;
95
    struct FILE_HEAD fhead;
96
 
97
    unsigned char memblock[1024];
98
 
99
    try
100
    {
101
        fn.read(reinterpret_cast<char*>(&head), SIZE_HEADER);
102
 
103
        if (memcmp(head.abyFileID, "\0FSFILE\0", 8) != 0)
104
        {
105
            MSG_ERROR("File " << fname << " is not an FSF file!");
106
            fn.close();
107
            return false;
108
        }
109
 
110
        fn.read(reinterpret_cast<char*>(&memblock[0]), SIZE_BLOCK);
111
        fillBlock(block, memblock);
112
        fillFileHead(fhead, memblock);
113
 
114
        uint32_t nextBlock = head.listStartBlock;
115
 
116
        // first we read the index
117
        while (nextBlock > 0)
118
        {
119
            fn.seekg(calcBlockPos(nextBlock), ios::beg);
120
            fn.read(reinterpret_cast<char*>(memblock), SIZE_BLOCK);
121
            fillUsageBlock(usageBlock, memblock);
122
            fillFileHead(fhead, memblock);
123
 
124
            if (fhead.thisBlock != nextBlock)
125
            {
126
                MSG_ERROR("No valid block position (" << to_string(fn.tellg()) << " [" << toHex(fn.tellg(), 8) << "])");
127
                break;
128
            }
129
 
130
            appendUBlock(&usageBlock);
131
            nextBlock = fhead.nextBlock;
132
        }
133
 
134
        // Now we read the files
135
        struct INDEX *act = idx;
136
 
137
        while (act != nullptr)
138
        {
139
            string ofile;
140
 
141
            string f(cp1250ToUTF8((char *)act->ublock->filePath));
142
            struct MANIFEST mf;
143
            mf.size = act->ublock->sizeBytes;
144
            mf.tmCreate = act->ublock->tmCreate;
145
            mf.tmModify = act->ublock->tmModify;
146
            mf.fname.assign(f);
147
            manifest.push_back(mf);
148
            string dir;
149
 
150
            if (f.find(".png") != string::npos || f.find(".jpg") != string::npos ||
151
                    f.find(".gif") != string::npos || f.find(".tiff") != string::npos)
152
                dir = "/images";
153
            else if (f.find(".wav") != string::npos || f.find(".mp3") != string::npos)
154
                dir = "/sounds";
155
            else if (f.find(".ttf") != string::npos)
156
                dir = "/fonts";
157
 
158
            if (!fs::exists(target + dir))
159
                fs::create_directories(target + dir);
160
 
161
            ofile = target + dir + "/" + f;
162
 
163
            MSG_DEBUG("Writing file " << ofile);
164
            ofstream of(ofile, ios::out | ios::binary);
165
 
166
            if (!of)
167
            {
168
                MSG_ERROR("Error opening target file" << ofile << "!");
169
                fn.close();
170
                return false;
171
            }
172
 
173
            nextBlock = act->ublock->startBlock;
174
            bool compressed = false;
175
 
176
            for (uint32_t i = 0; i < act->ublock->sizeBlocks; i++)
177
            {
178
                fn.seekg(calcBlockPos(nextBlock), ios::beg);
179
                fn.read(reinterpret_cast<char*>(memblock), SIZE_BLOCK);
180
                fillBlock(block, memblock);
181
 
182
                if (i == 0 && block.abyData[0] == 0x1f && block.abyData[1] == 0x8b)
183
                    compressed = true;
184
 
185
                nextBlock = block.nextBlock;
186
                of.write(reinterpret_cast<char*>(block.abyData), block.bytesUsed);
187
            }
188
 
189
            of.close();
190
 
191
            if (compressed)
192
            {
193
                MSG_DEBUG("Decompressing file " << ofile << " ...");
194
                Expand exp(ofile);
195
                int ret = exp.unzip();
196
 
197
                if (ret != 0)
198
                    MSG_WARNING("File " << ofile << " was not decompressed!");
199
            }
200
 
201
            compressed = false;
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
}