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