From baf16a6fbdd31686f7945c68b28bb79e51a09378 Mon Sep 17 00:00:00 2001 From: Antidote Date: Sat, 27 Jul 2013 18:42:11 -0700 Subject: [PATCH] * Added SkywardSword classes * Added missing readUBytes function in zelda::io::Stream --- include/BinaryReader.hpp | 1 + include/SSFile.hpp | 41 ++++++++++++++++++++++++++++ include/SSFileReader.hpp | 24 ++++++++++++++++ include/SSFileWriter.hpp | 25 +++++++++++++++++ include/SSQuest.hpp | 28 +++++++++++++++++++ include/Stream.hpp | 6 ++++ include/Types.hpp | 7 +++++ libzelda.pro | 14 ++++++++-- src/SSFile.cpp | 57 ++++++++++++++++++++++++++++++++++++++ src/SSFileReader.cpp | 59 ++++++++++++++++++++++++++++++++++++++++ src/SSFileWriter.cpp | 51 ++++++++++++++++++++++++++++++++++ src/SSQuest.cpp | 34 +++++++++++++++++++++++ src/Stream.cpp | 5 ++++ src/ZQuestFile.cpp | 5 +++- src/ZQuestFileReader.cpp | 1 + src/ZQuestFileWriter.cpp | 4 +-- 16 files changed, 357 insertions(+), 5 deletions(-) create mode 100644 include/SSFile.hpp create mode 100644 include/SSFileReader.hpp create mode 100644 include/SSFileWriter.hpp create mode 100644 include/SSQuest.hpp create mode 100644 src/SSFile.cpp create mode 100644 src/SSFileReader.cpp create mode 100644 src/SSFileWriter.cpp create mode 100644 src/SSQuest.cpp diff --git a/include/BinaryReader.hpp b/include/BinaryReader.hpp index a48ff49..2357c1c 100644 --- a/include/BinaryReader.hpp +++ b/include/BinaryReader.hpp @@ -177,6 +177,7 @@ protected: #define BINARYREADER_BASE \ private: \ typedef zelda::io::BinaryReader base; + #endif // BINARYREADER_BASE #endif // __BINARYREADER_HPP__ diff --git a/include/SSFile.hpp b/include/SSFile.hpp new file mode 100644 index 0000000..35dba35 --- /dev/null +++ b/include/SSFile.hpp @@ -0,0 +1,41 @@ +#ifndef __SSFILE_HPP__ +#define __SSFILE_HPP__ + +#include "Types.hpp" + +// standard lib +#include + +namespace zelda +{ +class SSQuest; + +class SSFile +{ +public: + enum MagicNumbers + { + USMagic = 0x534F5545, + JAMagic = 0x534F554A, + EUMagic = 0x534F5550 + }; + + SSFile(); + SSFile(std::vector quests); + ~SSFile(); + + void addQuest(SSQuest* q); + SSQuest* quest(Uint32 id); + std::vector questList() const; + + void setRegion(Region region); + Region region() const; +private: + Region m_region; + // A vector is a bit overkill + std::vector m_quests; + Uint32 m_numQuests; +}; + +} +#endif // __SSFILE_HPP__ diff --git a/include/SSFileReader.hpp b/include/SSFileReader.hpp new file mode 100644 index 0000000..c0f2684 --- /dev/null +++ b/include/SSFileReader.hpp @@ -0,0 +1,24 @@ +#ifndef __SSFILEREADER_HPP__ +#define __SSFILEREADER_HPP__ + +#include "BinaryReader.hpp" + +namespace zelda +{ +class SSFile; +namespace io +{ +class SSFileReader : public BinaryReader +{ + BINARYREADER_BASE +public: + + SSFileReader(Uint8* data, Uint64 length); + SSFileReader(const std::string& filename); + + SSFile* read(); +}; +} // io +} // zelda + +#endif // __SSFILEREADER_HPP__ diff --git a/include/SSFileWriter.hpp b/include/SSFileWriter.hpp new file mode 100644 index 0000000..dbdc792 --- /dev/null +++ b/include/SSFileWriter.hpp @@ -0,0 +1,25 @@ +#ifndef __SSFILEWRITER_HPP__ +#define __SSFILEWRITER_HPP__ + +#include "BinaryWriter.hpp" + +namespace zelda +{ +class SSFile; + +namespace io +{ + +class SSFileWriter : public BinaryWriter +{ + // Why does this fuck up my formatting in Qt Creator? + BINARYWRITER_BASE +public: + SSFileWriter(Uint8* data, Uint64 len); + SSFileWriter(const std::string& filename); + + void write(SSFile* file); +}; +} +} +#endif // __SSFILEWRITER_HPP__ diff --git a/include/SSQuest.hpp b/include/SSQuest.hpp new file mode 100644 index 0000000..fed9b89 --- /dev/null +++ b/include/SSQuest.hpp @@ -0,0 +1,28 @@ +#ifndef SSQUEST_HPP +#define SSQUEST_HPP + +#include "ZQuestFile.hpp" + +namespace zelda +{ + +// TODO: Handle game specific data +class SSQuest : public ZQuestFile +{ +public: + SSQuest(Uint8* data, Uint32 len); + + // TODO: Is len really needed? + void setSkipData(const Uint8* data, Uint32 len = 0x24); + Uint8* skipData() const; + + Uint32 skipLength() const; + +private: + Uint8* m_skipData; + Uint32 m_skipLength; +}; + + +} // zelda +#endif // SSQUEST_HPP diff --git a/include/Stream.hpp b/include/Stream.hpp index 4ccd9c6..38ad3a8 100644 --- a/include/Stream.hpp +++ b/include/Stream.hpp @@ -122,6 +122,12 @@ public: * \throw IOException */ virtual Int8 readByte(); + /*! \brief Reads a byte at the current position and advances the current position. + * + * \return Uint8* The buffer at the current position from the given length. + * \throw IOException + */ + virtual Uint8* readUBytes(Int64 length); /*! \brief Reads a byte at the current position and advances the current position. * diff --git a/include/Types.hpp b/include/Types.hpp index 538c2bc..1b7693d 100644 --- a/include/Types.hpp +++ b/include/Types.hpp @@ -30,6 +30,13 @@ enum Endian LittleEndian, //!< Specifies that the Stream is Little Endian (LSB) BigEndian //!< Specifies that the Stream is Big Endian (MSB) }; + +enum Region +{ + NTSCURegion, + NTSCJRegion, + PALRegion +}; } // zelda #endif diff --git a/libzelda.pro b/libzelda.pro index 22f4fba..a63375c 100644 --- a/libzelda.pro +++ b/libzelda.pro @@ -1,10 +1,12 @@ CONFIG += staticlib TEMPLATE=lib +DESTDIR = ./ CONFIG(debug, debug|release){ DEFINES += DEBUG TARGET=zelda-d } + CONFIG(release, release|debug){ DEFINES -= DEBUG TARGET=zelda @@ -52,7 +54,11 @@ HEADERS += \ include/ZQuestFileReader.hpp \ include/Compression.hpp \ include/WiiImage.hpp \ - include/ZQuestFile.hpp + include/ZQuestFile.hpp \ + include/SSQuest.hpp \ + include/SSFileWriter.hpp \ + include/SSFileReader.hpp \ + include/SSFile.hpp SOURCES += \ src/utility.cpp \ @@ -81,7 +87,11 @@ SOURCES += \ src/ZQuestFileReader.cpp \ src/Compression.cpp \ src/WiiImage.cpp \ - src/ZQuestFile.cpp + src/ZQuestFile.cpp \ + src/SSQuest.cpp \ + src/SSFileWriter.cpp \ + src/SSFileReader.cpp \ + src/SSFile.cpp system("exec doxygen libzelda.conf") #system("cd doc/latex && make") diff --git a/src/SSFile.cpp b/src/SSFile.cpp new file mode 100644 index 0000000..7e43e99 --- /dev/null +++ b/src/SSFile.cpp @@ -0,0 +1,57 @@ +#include "SSFile.hpp" +#include "SSQuest.hpp" +#include "InvalidOperationException.hpp" + +namespace zelda +{ + +SSFile::SSFile() + : m_numQuests(0) +{ +} + +SSFile::SSFile(std::vector quests) + : m_numQuests(0) +{ + m_quests = quests; +} + +SSFile::~SSFile() +{ +} + +void SSFile::addQuest(zelda::SSQuest *q) +{ + // Do not allow more than 3 quests + if (m_quests.size() >= 3) + return; + + m_quests.push_back(q); +} + +SSQuest *SSFile::quest(Uint32 id) +{ + if (id > m_quests.size() - 1) + throw zelda::error::InvalidOperationException("SSFile::quest -> id cannot be " + "greater than the number of quests"); + + return m_quests[id]; +} + +std::vector SSFile::questList() const +{ + return m_quests; +} + +void SSFile::setRegion(Region region) +{ + m_region = region; +} + +Region SSFile::region() const +{ + return m_region; +} + + +} // zelda diff --git a/src/SSFileReader.cpp b/src/SSFileReader.cpp new file mode 100644 index 0000000..c9f2b3d --- /dev/null +++ b/src/SSFileReader.cpp @@ -0,0 +1,59 @@ +#include "SSFileReader.hpp" +#include "SSFile.hpp" +#include "SSQuest.hpp" +#include "InvalidOperationException.hpp" +#include + +namespace zelda +{ +namespace io +{ + +SSFileReader::SSFileReader(Uint8* data, Uint64 length) + : base(data, length) +{ + base::setEndianess(BigEndian); +} + +SSFileReader::SSFileReader(const std::string& filename) + : base(filename) +{ + base::setEndianess(BigEndian); +} + +SSFile* SSFileReader::read() +{ + SSFile* file = NULL; + if (base::length() != 0xFBE0) + throw zelda::error::InvalidOperationException("SSFileReader::read -> File not the expected size of 0xFBE0"); + + Uint32 magic = base::readUInt32(); + + if (magic != SSFile::USMagic && magic != SSFile::JAMagic && magic != SSFile::EUMagic) + throw zelda::error::InvalidOperationException("SSFileReader::read -> Not a valid Skyward Sword save file"); + + base::seek(0x01C, base::Beginning); + Uint32 headerSize = base::readUInt32(); // Seems to be (headerSize - 1) + + if (headerSize != 0x1D) + throw zelda::error::InvalidOperationException("SSFileHeader::read -> Invalid header size, Corrupted data?"); + + // Time to read in each slot + file = new SSFile; + file->setRegion((magic == SSFile::USMagic ? NTSCURegion : (magic == SSFile::JAMagic ? NTSCJRegion : PALRegion))); + for (int i = 0; i < 3; i++) + { + SSQuest* q = new SSQuest((Uint8*)base::readBytes(0x53C0), 0x53C0); + Uint64 pos = base::position(); + // seek to the skip data for this particular quest + base::seek(0xFB60 + (i * 0x24), base::Beginning); + q->setSkipData(base::readUBytes(0x24)); + base::seek(pos, base::Beginning); + file->addQuest(q); + } + + return file; +} + +} // io +} // zelda diff --git a/src/SSFileWriter.cpp b/src/SSFileWriter.cpp new file mode 100644 index 0000000..9db8cf9 --- /dev/null +++ b/src/SSFileWriter.cpp @@ -0,0 +1,51 @@ +#include "SSFileWriter.hpp" +#include "SSFile.hpp" +#include "SSQuest.hpp" + +namespace zelda +{ +namespace io +{ + +SSFileWriter::SSFileWriter(Uint8 *data, Uint64 len) + : base(data, len) +{ + base::setEndianess(BigEndian); +} + +SSFileWriter::SSFileWriter(const std::string &filename) + : base(filename) +{ + base::setEndianess(BigEndian); +} + +void SSFileWriter::write(SSFile *file) +{ + Uint32 magic = (file->region() == NTSCURegion ? SSFile::USMagic : + (file->region() == NTSCJRegion ? SSFile::JAMagic : SSFile::EUMagic)); + + base::writeUInt32(magic); + base::seek(0x1C, base::Beginning); + base::writeUInt32(0x1D); + + std::vector quests = file->questList(); + int i = 0; + for (SSQuest* q : quests) + { + // Write the save data + base::writeUBytes(q->data(), q->length()); + Uint64 pos = base::position(); + // Write the slots skip data + base::seek(0xFB60 + (i * 0x24), base::Beginning); + base::writeUBytes(q->skipData(), q->skipLength()); + base::seek(pos, base::Beginning); + i++; + } + + // write those padding bytes + base::seek(0xFBE0, base::Beginning); + save(); +} + +} // io +} // zelda diff --git a/src/SSQuest.cpp b/src/SSQuest.cpp new file mode 100644 index 0000000..2e33c2e --- /dev/null +++ b/src/SSQuest.cpp @@ -0,0 +1,34 @@ +#include "SSQuest.hpp" + +namespace zelda +{ +SSQuest::SSQuest(Uint8 *data, Uint32 len) + : ZQuestFile(ZQuestFile::SS, BigEndian, data, len), + m_skipData(NULL), + m_skipLength(0) +{ +} + +void SSQuest::setSkipData(const Uint8 *data, Uint32 len) +{ + if (m_skipData) + { + delete[] m_skipData; + m_skipData = NULL; + } + + m_skipData = (Uint8*)data; + m_skipLength = len; +} + +Uint8 *SSQuest::skipData() const +{ + return m_skipData; +} + +Uint32 SSQuest::skipLength() const +{ + return m_skipLength; +} + +} // zelda diff --git a/src/Stream.cpp b/src/Stream.cpp index 194ef23..decdae5 100644 --- a/src/Stream.cpp +++ b/src/Stream.cpp @@ -180,6 +180,11 @@ Int8 Stream::readByte() return *(Int8*)(m_data + m_position++); } +Uint8 *Stream::readUBytes(Int64 length) +{ + return (Uint8*)readBytes(length); +} + Int8* Stream::readBytes(Int64 length) { if (m_bitPosition > 0) diff --git a/src/ZQuestFile.cpp b/src/ZQuestFile.cpp index 8a83126..1e637b0 100644 --- a/src/ZQuestFile.cpp +++ b/src/ZQuestFile.cpp @@ -81,6 +81,8 @@ void ZQuestFile::setData(Uint8* data, Uint32 length) { delete[] m_data; m_data = new Uint8[length]; + // Why is this memcpy needed? + // I get SIGABRT without it, need to research memcpy(m_data, data, length); m_length = length; } @@ -107,6 +109,7 @@ std::string ZQuestFile::gameString() const void ZQuestFile::initGameStrings() { + // Populate game strings m_gameStrings.push_back("No Game"); m_gameStrings.push_back("Legend Of Zelda"); m_gameStrings.push_back("Adventure of Link"); @@ -125,7 +128,7 @@ void ZQuestFile::initGameStrings() m_gameStrings.push_back("Phantom Hourglass"); m_gameStrings.push_back("Spirit Tracks"); m_gameStrings.push_back("Skyward Sword"); - m_gameStrings.push_back("A Link Between Worlds (Unreleased)"); + m_gameStrings.push_back("A Link Between Worlds (Unreleased)"); // Probably should have this, but..... } } diff --git a/src/ZQuestFileReader.cpp b/src/ZQuestFileReader.cpp index 63e035a..d8b81c1 100644 --- a/src/ZQuestFileReader.cpp +++ b/src/ZQuestFileReader.cpp @@ -66,6 +66,7 @@ ZQuestFile *ZQuestFileReader::read() { delete[] dst; delete[] data; + // TODO: Make proper exception throw error::InvalidOperationException("ZQuestFileReader::read -> Error decompressing data"); } diff --git a/src/ZQuestFileWriter.cpp b/src/ZQuestFileWriter.cpp index ecc0f2f..ff85e5d 100644 --- a/src/ZQuestFileWriter.cpp +++ b/src/ZQuestFileWriter.cpp @@ -72,12 +72,12 @@ void ZQuestFileWriter::write(ZQuestFile* quest, bool compress) base::writeUInt32(quest->length()); base::writeUInt32(quest->game()); - base::writeUInt16(quest->endian() == BigEndian ? 0xFEFF : 0xFFFE); + base::writeUInt16(quest->endian() == BigEndian ? 0xFFFE : 0xFEFF); base::seek(0x0A); base::writeUBytes(questData, compLen); base::save(); - // Delete compressed data to preven memory leaks + // Delete compressed data to prevent memory leaks if (questData != quest->data()) { delete[] questData;