ff7tk 0.80.25
Work with Final Fantasy 7 game data
IsoArchive.h
Go to the documentation of this file.
1/****************************************************************************/
2// copyright 2009 - 2021 Arzel Jérôme <myst6re@gmail.com> //
3// copyright 2019 Chris Rizzitello <sithlord48@gmail.com> //
4// //
5// This file is part of FF7tk //
6// //
7// FF7tk is free software: you can redistribute it and/or modify //
8// it under the terms of the GNU General Public License as published by //
9// the Free Software Foundation, either version 3 of the License, or //
10// (at your option) any later version. //
11// //
12// FF7tk is distributed in the hope that it will be useful, //
13// but WITHOUT ANY WARRANTY; without even the implied warranty of //
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
15// GNU General Public License for more details. //
16/****************************************************************************/
17#pragma once
18
19#include <QIODevice>
20#include <QString>
21#include "Archive.h"
22#include "ff7tkformats_export.h"
23
24#define MAX_ISO_READ 10000
25#define MAX_FILENAME_LENGTH 207
26#define SEPARATOR_1 '\x2E' // .
27#define SEPARATOR_2 '\x3B' // ;
28#define SECTOR_SIZE 2352
29#define SECTOR_SIZE_HEADER 24
30#define SECTOR_SIZE_DATA 2048
31#define SECTOR_SIZE_FOOTER 280
32
33//#define ISOARCHIVE_DEBUG
34
35//d-characters : [\w ]
36//a-characters : [!"%&'\‍(\‍)*+,\-\./\w:;<=>? ] (+ SP)
37
38class IsoArchiveIO;
39class IsoFileIO;
40
41struct FF7TKFORMATS_EXPORT IsoTime {
42 char year[4];
43 char month[2];
44 char day[2];
45 char hour[2];
46 char minute[2];
47 char second[2];
48 char millis[2];
49 qint8 GMT;
50};
51
52struct FF7TKFORMATS_EXPORT PathTable {
53 quint8 length_di;
57 QString name;
58 quint32 position;
59};
60
61struct FF7TKFORMATS_EXPORT DirectoryRecordHead {
62 quint32 location_extent;// little endian //first sector in the directory
63 quint32 location_extent2;// big endian
64 quint32 data_length;// little endian
65 quint32 data_length2;// big endian
66 quint8 year;// since 1900
67 quint8 month;
68 quint8 day;
69 quint8 hour;
70 quint8 minute;
71 quint8 second;
72 qint8 GMT;
73 quint8 file_flags;// Multi-Extent/Reserved/Reserved/Protection/Record/Associated File/Directory/Existence
76 quint16 volume_sequence_number;// little endian
77 quint16 volume_sequence_number2;// big endian
78 quint8 length_fi;
79};
80
81struct FF7TKFORMATS_EXPORT DirectoryRecord {
82 quint8 length_dr;
85 QString name;
86 QString version;
87 // padding;
88};
89
90struct FF7TKFORMATS_EXPORT VolumeDescriptor1 {
91 quint8 type;// 0x01 or 0xff
92 quint8 id[5];// CD001
93 quint8 version;// 1 = international standard
94 quint8 unused1;
95 quint8 system_id[32];
96 quint8 volume_id[32];
97 quint8 unused2[8];
98 quint32 volume_space_size;// little endian // number of sectors
99 quint32 volume_space_size2;// big endian
100 quint8 unused3[32];
101 quint16 volume_set_size;// little endian
102 quint16 volume_set_size2;// big endian
103 quint16 volume_sequence_number;// little endian
104 quint16 volume_sequence_number2;// big endian
105 quint16 logical_block_size;// little endian
106 quint16 logical_block_size2;// big endian
107 quint32 path_table_size;// little endian
108 quint32 path_table_size2;// big endian
109 quint32 type_path_table;// little endian
110 quint32 opt_type_path_table;// little endian
111 quint32 type_path_table2;// big endian
112 quint32 opt_type_path_table2;// big endian
113};
114
115struct FF7TKFORMATS_EXPORT VolumeDescriptor2 {
116 quint8 volume_set_id[128];
117 quint8 publisher_id[128];
118 quint8 preparer_id[128];
119 quint8 application_id[128];
120 quint8 copyright_file_id[37];
121 quint8 abstract_file_id[37];
122 quint8 bibliographic_file_id[37];
128 quint8 unused4;
129 quint8 application_data[512];
130 quint8 unused5[653];
131};
132
133struct FF7TKFORMATS_EXPORT VolumeDescriptor {
137};
138
139class FF7TKFORMATS_EXPORT IsoFileOrDirectory
140{
141public:
142 virtual ~IsoFileOrDirectory();
143 const QString &name() const;
144 quint32 location() const;
145 quint32 locationAfter() const;
146 quint32 size() const;
147 quint32 sectorCount() const;
148 quint32 newLocation() const;
149 quint32 newSize() const;
150 quint32 newSectorCount() const;
151 void setName(const QString &name);
152 void setLocation(quint32 location);
153 virtual bool isDirectory() const = 0;
154 bool isFile() const;
155 virtual bool isModified() const;
156 virtual void applyModifications();
157 bool isSpecial() const;
159 void setPaddingAfter(quint8 after);
160 quint8 paddingAfter() const;
161protected:
162 Q_DISABLE_COPY(IsoFileOrDirectory)
163 IsoFileOrDirectory(const QString &name, quint32 location, quint32 size, qint64 structPosition);
164 QString _name;
165 quint32 _location, _size;
166 quint32 _newLocation, _newSize;
168};
169
170class FF7TKFORMATS_EXPORT IsoFile : public IsoFileOrDirectory
171{
172public:
173 IsoFile(const QString &name, quint32 location, quint32 size, qint64 structPosition, IsoArchiveIO *io);
174 virtual ~IsoFile() override;
175 bool isDirectory() const override;
176 QByteArray data(quint32 maxSize = 0) const;
177 QByteArray modifiedData(quint32 maxSize = 0) const;
178 bool extract(const QString &destination, quint32 maxSize = 0) const;
179 QIODevice *file() const;
180 QIODevice *modifiedFile() const;
181 bool setModifiedFile(QIODevice *io);
182 bool setModifiedFile(const QByteArray &data);
183 bool isModified() const override;
184 void applyModifications() override;
185private:
186 Q_DISABLE_COPY(IsoFile)
187 void setFile(QIODevice *io);
188 void cleanNewIO();
189 QIODevice *_io, *_newIO;
190 bool dataChanged, _newIOMustBeRemoved;
191};
192
193class FF7TKFORMATS_EXPORT IsoDirectory : public IsoFileOrDirectory
194{
195public:
196 IsoDirectory(const QString &name, quint32 location, quint32 size, qint64 structPosition);
197 virtual ~IsoDirectory() override;
198 bool isDirectory() const override;
199 const QMap<QString, IsoFileOrDirectory *> &filesAndDirectories() const;
200 QList<IsoFile *> files() const;
201 QList<IsoDirectory *> directories() const;
202 IsoFileOrDirectory *fileOrDirectory(const QString &path) const;
203 IsoFile *file(const QString &path) const;
204 IsoDirectory *directory(const QString &path) const;
205 void add(IsoFileOrDirectory *fileOrDirectory);
206private:
207 Q_DISABLE_COPY(IsoDirectory)
208 QMap<QString, IsoFileOrDirectory *> _filesAndDirectories;
209};
210
211class FF7TKFORMATS_EXPORT IsoArchiveIO : public QFile
212{
213 friend class IsoArchive;
214public:
215 IsoArchiveIO();
216 explicit IsoArchiveIO(const QString &name);
217 virtual ~IsoArchiveIO() override;
218
219 bool open(QIODevice::OpenMode mode) override;
220 qint64 posIso() const;
221 bool seekIso(qint64 off);
222 qint64 sizeIso() const;
223
224 qint64 readIso(char *data, qint64 maxSize);
225 QByteArray readIso(qint64 maxSize);
226
227 qint64 writeIso(const char *data, qint64 maxSize);
228 qint64 writeIso(const QByteArray &byteArray);
229
230 static inline QByteArray int2Header(quint32 id)
231 {
232 quint8 h1, h2, h3;
233
234 if (id < 4350) {
235 h1 = 0;
236 h2 = dec2Hex(quint8(id / 75 + 2));
237 h3 = dec2Hex(quint8(id - 75 * (hex2Dec(h2) - 2)));
238 } else {
239 h1 = dec2Hex(quint8((id + 150) / 4500));
240 h2 = dec2Hex(quint8((id + 150 - hex2Dec(h1)*4500) / 75));
241 h3 = dec2Hex(quint8(id + 150 - hex2Dec(h1)*4500 - hex2Dec(h2)*75));
242 }
243
244 return QByteArray().append(char(h1)).append(char(h2)).append(char(h3));
245 }
246 static inline QByteArray buildHeader(quint32 sector, quint8 type, quint8 mode = 2)
247 {
248 return QByteArray("\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00", 12)
249 .append(int2Header(sector)).append(char(mode))
250 .append("\x00\x00", 2).append(char(type)).append('\x00')
251 .append("\x00\x00", 2).append(char(type)).append('\x00');
252 }
253 static inline QByteArray buildFooter(quint32 sector)
254 {
255 Q_UNUSED(sector)
256 //TODO (if possible): Checksum EDC/ECC (Error Detection Code & Error Correction Code)
257 return QByteArray(SECTOR_SIZE_FOOTER, '\x00');
258 }
259 static inline void headerInfos(const QByteArray &header, quint8 *type, quint8 *mode = nullptr)
260 {
261 Q_ASSERT(header.size() != SECTOR_SIZE_HEADER);
262 if (type) {
263 *type = quint8(header.at(18));
264 }
265 if (mode) {
266 *mode = quint8(header.at(15));
267 }
268 }
269
270 QByteArray sector(quint32 num, quint16 maxSize = SECTOR_SIZE_DATA);
271 QByteArray sectorHeader(quint32 num);
272 QByteArray sectorFooter(quint32 num);
273 quint32 currentSector() const;
274 quint32 sectorCount() const;
275 static quint32 sectorCountData(quint32 dataSize);
276 bool seekToSector(quint32 num);
277 bool writeSector(const QByteArray &data, quint8 type, quint8 mode = 2);
278private:
279 Q_DISABLE_COPY(IsoArchiveIO)
280 static qint64 isoPos(qint64 pos);
281 static qint64 filePos(qint64 pos);
282
283 static inline quint8 hex2Dec(quint8 hex)
284 {
285 return 10 * (hex / 16) + hex % 16;
286 }
287 static inline quint8 dec2Hex(quint8 dec)
288 {
289 return 16 * (dec / 10) + dec % 10;
290 }
291};
292
293class FF7TKFORMATS_EXPORT IsoFileIO : public QIODevice
294{
295public:
296 IsoFileIO(IsoArchiveIO *io, const IsoFile *infos, QObject *parent = nullptr);
297 bool open(OpenMode mode) override;
298 qint64 size() const override;
299 bool canReadLine() const override;
300protected:
301 qint64 readData(char *data, qint64 maxSize) override;
302 qint64 writeData(const char *data, qint64 maxSize) override;
303private:
304 Q_DISABLE_COPY(IsoFileIO)
305 IsoArchiveIO *_io;
306 const IsoFile *_infos;
307};
308
309class FF7TKFORMATS_EXPORT IsoArchive
310{
311public:
312 IsoArchive();
313 explicit IsoArchive(const QString &name);
314 virtual ~IsoArchive();
315
316 virtual bool open(QIODevice::OpenMode mode);
317 inline virtual bool isOpen() const
318 {
319 return _io.isOpen() && _rootDirectory;
320 }
321 virtual void close();
322 inline QString fileName() const
323 {
324 return _io.fileName();
325 }
326 inline void setFileName(const QString &fileName)
327 {
328 _io.setFileName(fileName);
329 }
330 inline QString errorString() const
331 {
332 return _io.errorString();
333 }
334 inline const IsoArchiveIO &io() const
335 {
336 return _io;
337 }
338
339 bool pack(IsoArchive *destination, ArchiveObserver *control = nullptr, IsoDirectory *directory = nullptr);
340 void applyModifications(IsoDirectory *directory);
341
342 QByteArray file(const QString &path, quint32 maxSize = 0) const;
343 QIODevice *fileDevice(const QString &path) const;
344 QByteArray modifiedFile(const QString &path, quint32 maxSize = 0) const;
345 QIODevice *modifiedFileDevice(const QString &path) const;
346 bool extract(const QString &path, const QString &destination, quint32 maxSize = 0) const;
347 bool extractDir(const QString &path, const QString &destination) const;
348 void extractAll(const QString &destination) const;
349 qint32 diffCountSectors(const QString &path, quint32 newSize) const;
350
351 IsoDirectory *rootDirectory() const;
352// const QList<PathTable> &getPathTables1a() const;
353// const QList<PathTable> &getPathTables1b() const;
354// const QList<PathTable> &getPathTables2a() const;
355// const QList<PathTable> &getPathTables2b() const;
356 Archive::ArchiveError error() const;
357protected:
358 virtual bool reorganizeModifiedFilesAfter(QMap<quint32, const IsoFile *> &writeToTheMain, QList<const IsoFile *> &writeToTheEnd);
359 void setError(Archive::ArchiveError error, const QString &errorString = QString());
360private:
361 Q_DISABLE_COPY(IsoArchive)
362 bool openVolumeDescriptor(quint8 num = 0);
363 bool openRootDirectory(quint32 sector, quint32 dataSize = SECTOR_SIZE_DATA);
364 IsoDirectory *_openDirectoryRecord(IsoDirectory *directories, QList<quint32> &dirVisisted);
365// QList<PathTable> pathTable(quint32 sector, quint32 dataSize = SECTOR_SIZE_DATA);
366
367 // Returns index of file in "filesWithPadding" who have paddingAfter >= minSectorCount
368 static int findPadding(const QList<IsoFileOrDirectory *> &filesWithPadding, quint32 minSectorCount);
369 // Returns files with padding after
370 QList<IsoFileOrDirectory *> getIntegrity();
371 bool getIntegritySetPaddingAfter(IsoFileOrDirectory *prevFile, quint32 fileLocation);
372
373 void _extractAll(const QString &destination, IsoDirectory *directories, QString currentInternalDir = QString()) const;
374 void _getIntegrity(QMap<quint32, IsoFileOrDirectory *> &files, IsoDirectory *directory) const;
375 QMap<quint32, IsoFile *> getModifiedFiles(IsoDirectory *directory) const;
376 void getModifiedFiles(QMap<quint32, IsoFile *> &files, IsoDirectory *directory) const;
377 static void repairLocationSectors(IsoDirectory *directory, IsoArchive *newIso);
378
379 bool writeFile(QIODevice *in, quint32 sectorCount = 0, ArchiveObserver *control = nullptr);
380 bool copySectors(IsoArchiveIO *out, qint64 size, ArchiveObserver *control = nullptr, bool repair = false);
381
382 IsoArchiveIO _io;
383 VolumeDescriptor volume;
384 IsoDirectory *_rootDirectory;
386// QList<PathTable> pathTables1a;
387// QList<PathTable> pathTables1b;
388// QList<PathTable> pathTables2a;
389// QList<PathTable> pathTables2b;
390#ifdef ISOARCHIVE_DEBUG
391 static QString isoTimeToString(const IsoTime &time);
392 static QString volumeDescriptorToString(const VolumeDescriptor &vd);
393 static QString directoryRecordToString(const DirectoryRecord &dr);
394 static QString pathTableToString(const PathTable &pathTable, bool bigEndian = false);
395#endif
396};
#define SECTOR_SIZE_HEADER
Definition: IsoArchive.h:29
#define SECTOR_SIZE_FOOTER
Definition: IsoArchive.h:31
#define SECTOR_SIZE_DATA
Definition: IsoArchive.h:30
ArchiveError
Definition: Archive.h:37
Definition: IsoArchive.h:212
static QByteArray buildFooter(quint32 sector)
Definition: IsoArchive.h:253
static QByteArray buildHeader(quint32 sector, quint8 type, quint8 mode=2)
Definition: IsoArchive.h:246
static QByteArray int2Header(quint32 id)
Definition: IsoArchive.h:230
static void headerInfos(const QByteArray &header, quint8 *type, quint8 *mode=nullptr)
Definition: IsoArchive.h:259
Definition: IsoArchive.h:310
QString fileName() const
Definition: IsoArchive.h:322
virtual bool isOpen() const
Definition: IsoArchive.h:317
QString errorString() const
Definition: IsoArchive.h:330
void setFileName(const QString &fileName)
Definition: IsoArchive.h:326
const IsoArchiveIO & io() const
Definition: IsoArchive.h:334
virtual bool open(QIODevice::OpenMode mode)
Definition: IsoArchive.cpp:588
Definition: IsoArchive.h:194
Definition: IsoArchive.h:294
Definition: IsoArchive.h:140
quint32 _newLocation
Definition: IsoArchive.h:166
quint32 _location
Definition: IsoArchive.h:165
QString _name
Definition: IsoArchive.h:164
quint8 _paddingAfter
Definition: IsoArchive.h:167
virtual bool isDirectory() const =0
qint64 structPosition
Definition: IsoArchive.h:158
Definition: IsoArchive.h:171
Definition: Archive.h:25
Definition: IsoArchive.h:61
quint8 length_fi
Definition: IsoArchive.h:78
quint8 year
Definition: IsoArchive.h:66
quint32 data_length2
Definition: IsoArchive.h:65
quint8 hour
Definition: IsoArchive.h:69
quint32 location_extent
Definition: IsoArchive.h:62
quint16 volume_sequence_number
Definition: IsoArchive.h:76
quint8 second
Definition: IsoArchive.h:71
qint8 GMT
Definition: IsoArchive.h:72
quint8 interleave_grap_size
Definition: IsoArchive.h:75
quint8 month
Definition: IsoArchive.h:67
quint8 minute
Definition: IsoArchive.h:70
quint32 location_extent2
Definition: IsoArchive.h:63
quint8 file_unit_size
Definition: IsoArchive.h:74
quint8 day
Definition: IsoArchive.h:68
quint32 data_length
Definition: IsoArchive.h:64
quint16 volume_sequence_number2
Definition: IsoArchive.h:77
quint8 file_flags
Definition: IsoArchive.h:73
Definition: IsoArchive.h:81
DirectoryRecordHead drh
Definition: IsoArchive.h:84
QString version
Definition: IsoArchive.h:86
quint8 extended_attr_record_length
Definition: IsoArchive.h:83
QString name
Definition: IsoArchive.h:85
quint8 length_dr
Definition: IsoArchive.h:82
Definition: IsoArchive.h:41
qint8 GMT
Definition: IsoArchive.h:49
Definition: IsoArchive.h:52
QString name
Definition: IsoArchive.h:57
quint8 length_di
Definition: IsoArchive.h:53
quint32 location_extent
Definition: IsoArchive.h:55
quint16 parent_directory_number
Definition: IsoArchive.h:56
quint8 extended_attr_record_length
Definition: IsoArchive.h:54
quint32 position
Definition: IsoArchive.h:58
Definition: IsoArchive.h:90
quint8 type
Definition: IsoArchive.h:91
quint16 volume_set_size2
Definition: IsoArchive.h:102
quint32 opt_type_path_table2
Definition: IsoArchive.h:112
quint32 path_table_size
Definition: IsoArchive.h:107
quint32 path_table_size2
Definition: IsoArchive.h:108
quint32 type_path_table2
Definition: IsoArchive.h:111
quint16 volume_sequence_number
Definition: IsoArchive.h:103
quint16 volume_sequence_number2
Definition: IsoArchive.h:104
quint8 unused1
Definition: IsoArchive.h:94
quint32 type_path_table
Definition: IsoArchive.h:109
quint8 version
Definition: IsoArchive.h:93
quint32 opt_type_path_table
Definition: IsoArchive.h:110
quint16 volume_set_size
Definition: IsoArchive.h:101
quint32 volume_space_size
Definition: IsoArchive.h:98
quint16 logical_block_size
Definition: IsoArchive.h:105
quint32 volume_space_size2
Definition: IsoArchive.h:99
quint16 logical_block_size2
Definition: IsoArchive.h:106
Definition: IsoArchive.h:115
IsoTime modification_date
Definition: IsoArchive.h:124
quint8 unused4
Definition: IsoArchive.h:128
IsoTime creation_date
Definition: IsoArchive.h:123
quint8 file_structure_version
Definition: IsoArchive.h:127
IsoTime effective_date
Definition: IsoArchive.h:126
IsoTime expiration_date
Definition: IsoArchive.h:125
Definition: IsoArchive.h:133
VolumeDescriptor1 vd1
Definition: IsoArchive.h:134
DirectoryRecord dr
Definition: IsoArchive.h:135
VolumeDescriptor2 vd2
Definition: IsoArchive.h:136