summaryrefslogtreecommitdiff
path: root/MapTool/wii/archiveu8.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--MapTool/wii/archiveu8.cpp263
1 files changed, 263 insertions, 0 deletions
diff --git a/MapTool/wii/archiveu8.cpp b/MapTool/wii/archiveu8.cpp
new file mode 100644
index 0000000..d27dfde
--- /dev/null
+++ b/MapTool/wii/archiveu8.cpp
@@ -0,0 +1,263 @@
+/*******************************************************************************
+ This file is part of LayoutStudio (http://github.com/Treeki/LayoutStudio)
+ Copyright (c) 2010 Treeki (treeki@gmail.com)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, version 2.0.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License 2.0 for more details.
+
+ You should have received a copy of the GNU General Public License 2.0
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*******************************************************************************/
+
+#include "archiveu8.h"
+
+
+
+
+
+
+WiiArchiveU8::WiiArchiveU8() { }
+
+WiiArchiveU8::WiiArchiveU8(QDataStream &stream) {
+ U8ReadInfo info;
+ info.startPos = stream.device()->pos();
+
+ quint32 magic;
+ stream >> (quint32&)magic;
+
+ if (magic != 0x55AA382D)
+ qWarning() << "WiiArchiveU8: tried to load an archive without the U8 magic";
+
+ quint32 fstStart, fstSize, dataOffset;
+ stream >> (quint32&)fstStart;
+ stream >> (quint32&)fstSize;
+ stream >> (quint32&)dataOffset;
+
+ // read the FST
+ stream.device()->seek(info.startPos + fstStart);
+
+ // read the root node
+ quint32 rootNodeLastChild;
+
+ stream.skipRawData(8); // ignore type, nameOffset and dataOffset
+ stream >> (quint32&)rootNodeLastChild;
+
+ // but before we do this, read the string table
+ qint64 savePos = stream.device()->pos();
+ stream.device()->seek(savePos + ((rootNodeLastChild - 1) * 12));
+
+ int stringTableLength = fstSize - (rootNodeLastChild * 12);
+
+ QByteArray rawStringTable;
+ rawStringTable.resize(stringTableLength);
+ stream.readRawData(rawStringTable.data(), stringTableLength);
+
+ info.stringTable = QString::fromAscii(rawStringTable.constData(), stringTableLength);
+
+ // now read the root node
+ stream.device()->seek(savePos);
+
+ qDebug() << "Reading root node: last child is" << rootNodeLastChild;
+ info.currentNode = 1;
+ this->readDir(stream, this->root, rootNodeLastChild, info);
+}
+
+
+void WiiArchiveU8::readDir(QDataStream &in, WiiDirectory &dir, int lastChild, U8ReadInfo &info) {
+ // read every node in this directory
+
+ while (info.currentNode < lastChild) {
+ info.currentNode++;
+
+ //qDebug() << "Reading @ pos" << in.device()->pos();
+
+ quint32 value, dataOffset, size;
+ in >> (quint32&)value;
+ in >> (quint32&)dataOffset;
+ in >> (quint32&)size;
+
+ int nameOffset = value & 0xFFFFFF;
+ int type = value >> 24;
+
+ WiiFSObject *newObj;
+ if (type == 0)
+ newObj = new WiiFile;
+ else if (type == 1)
+ newObj = new WiiDirectory;
+ else
+ qFatal("WiiArchiveU8::readDir: Unknown fs obj type %d", type);
+
+ // get the name
+ int nameSize = info.stringTable.indexOf(QChar::Null, nameOffset) - nameOffset;
+ newObj->name = info.stringTable.mid(nameOffset, nameSize);
+
+ qDebug() << "Reading node" << info.currentNode << newObj->name << "- type:" << type << "size:" << size << "offset:" << dataOffset;
+
+ // read the contents
+ if (newObj->isFile()) {
+ // get the file data
+ qint64 savePos = in.device()->pos();
+ in.device()->seek(info.startPos + dataOffset);
+
+ WiiFile *file = (WiiFile*)newObj;
+ file->data.resize(size);
+ in.readRawData(file->data.data(), size);
+
+ in.device()->seek(savePos);
+
+ } else if (newObj->isDirectory()) {
+ // read the children
+ this->readDir(in, *((WiiDirectory*)newObj), size, info);
+
+ }
+
+ qDebug() << "Adding" << newObj->name << "to" << dir.name;
+ dir.addChild(newObj);
+ };
+}
+
+
+
+void WiiArchiveU8::writeToDataStream(QDataStream &out) const {
+ U8WriteInfo info;
+
+ // first off, before we do anything else, create the string table
+ this->addNodeToStringTable(this->root, info.stringTableBuilder);
+
+ QByteArray stringTable = info.stringTableBuilder.pack();
+
+ // calculate various fun offsets/sizes
+ int nodeCount = 0;
+ this->countNode(this->root, &nodeCount);
+
+ info.startPos = out.device()->pos();
+
+ quint32 fstStart = 0x20;
+ quint32 nodeDataSize = nodeCount * 12;
+ quint32 stringTableSize = stringTable.length();
+ quint32 fstSize = nodeDataSize + stringTableSize;
+ quint32 dataOffset = AlignUp(fstStart + fstSize, 0x20);
+
+ qDebug() << "Writing: fstStart" << fstStart << "nodeCount" << nodeCount;
+ qDebug() << "nodeDataSize" << nodeDataSize << "stringTableSize" << stringTableSize;
+ qDebug() << "fstSize" << fstSize << "dataOffset" << dataOffset;
+
+ // now write the header
+ out << (quint32)0x55AA382D;
+ out << (quint32)fstStart;
+ out << (quint32)fstSize;
+ out << (quint32)dataOffset;
+
+ WritePadding(0x10, out);
+
+ // write root node
+ info.currentNode = 1; // root node is 1
+ info.currentRecursionLevel = 0;
+ info.nextDataOffset = dataOffset;
+
+ out << (quint32)(0x01000000 << info.stringTableBuilder.add(""));
+ out << (quint32)0;
+ out << (quint32)nodeCount;
+
+ this->writeDir(out, this->root, info);
+
+ // write string table
+ out.writeRawData(stringTable.constData(), stringTable.length());
+
+ // write data (after padding)
+ WritePadding(dataOffset - fstSize - fstStart, out);
+
+ this->writeNodeData(out, this->root);
+
+ // looks like we are finally done
+}
+
+
+void WiiArchiveU8::addNodeToStringTable(const WiiFSObject &node, WiiStringTableBuilder &table) const {
+ table.add(node.name);
+
+ if (node.isDirectory()) {
+ WiiDirectory *dir = (WiiDirectory*)&node;
+
+ foreach (WiiFSObject *p, dir->children)
+ this->addNodeToStringTable(*p, table);
+ }
+}
+
+
+void WiiArchiveU8::countNode(const WiiFSObject &node, int *countPtr) const {
+ (*countPtr)++;
+
+ if (node.isDirectory()) {
+ WiiDirectory *dir = (WiiDirectory*)&node;
+
+ foreach (WiiFSObject *p, dir->children)
+ this->countNode(*p, countPtr);
+ }
+}
+
+
+void WiiArchiveU8::writeDir(QDataStream &out, const WiiDirectory &dir, U8WriteInfo &info) const {
+ foreach (WiiFSObject *p, dir.children) {
+ info.currentNode++;
+
+ if (p->isDirectory()) {
+ // write directory
+ WiiDirectory *thisDir = (WiiDirectory*)p;
+
+ out << (quint32)(0x01000000 | info.stringTableBuilder.add(thisDir->name));
+ out << (quint32)info.currentRecursionLevel;
+
+ qint64 lastChildFieldPos = out.device()->pos();
+ out << (quint32)0; // placeholder
+
+ info.currentRecursionLevel++;
+
+ this->writeDir(out, *thisDir, info);
+
+ info.currentRecursionLevel--;
+
+ // write lastChild field
+ qint64 dirEndPos = out.device()->pos();
+
+ out.device()->seek(lastChildFieldPos);
+ out << (quint32)info.currentNode;
+ out.device()->seek(dirEndPos);
+
+ } else if (p->isFile()) {
+ // write file
+ WiiFile *thisFile = (WiiFile*)p;
+
+ out << (quint32)info.stringTableBuilder.add(thisFile->name);
+ out << (quint32)info.nextDataOffset;
+ out << (quint32)thisFile->data.size();
+
+ info.nextDataOffset = AlignUp(info.nextDataOffset + thisFile->data.size(), 0x20);
+ }
+ }
+}
+
+
+void WiiArchiveU8::writeNodeData(QDataStream &out, const WiiFSObject &node) const {
+ if (node.isDirectory()) {
+ // write all the children's data
+ WiiDirectory *thisDir = (WiiDirectory*)&node;
+
+ foreach (WiiFSObject *p, thisDir->children)
+ this->writeNodeData(out, *p);
+
+ } else if (node.isFile()) {
+ // write this file's data
+ WiiFile *thisFile = (WiiFile*)&node;
+
+ int len = thisFile->data.length();
+ out.writeRawData(thisFile->data.constData(), len);
+ WritePadding(AlignUp(len, 0x20) - len, out);
+ }
+}