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