* Generalize ZQuestFile and incremented version major to 2

* Fixed a bug in Stream::writeBit where it wasn't correctly setting the flag
This commit is contained in:
Antidote 2014-04-18 18:36:46 -07:00
parent 496906c5de
commit 1a389de296
5 changed files with 118 additions and 57 deletions

View File

@ -20,10 +20,13 @@
#include <string> #include <string>
#include <vector> #include <vector>
#define ZQUEST_VERSION_CHECK(major, minor, revision) \
(major | (minor << 8) | (revision << 16))
namespace zelda namespace zelda
{ {
/*! /*!
* \brief The ZQuest class * \brief ZQuestFile is an export format for save data.
*/ */
class ZQuestFile class ZQuestFile
{ {
@ -40,10 +43,6 @@ public:
* \brief The current revision of the ZQuest format * \brief The current revision of the ZQuest format
*/ */
static const Uint32 Revision; static const Uint32 Revision;
/*!
* \brief The current build of the ZQuest format
*/
static const Uint32 Build;
/*! /*!
* \brief The current version of the ZQuest format * \brief The current version of the ZQuest format
*/ */
@ -97,7 +96,7 @@ public:
* \param data * \param data
* \param length * \param length
*/ */
ZQuestFile(Game game, Endian endian, Uint8* data, Uint32 length); ZQuestFile(Game game, Endian endian, Uint8* data, Uint32 length, const std::string& gameString = std::string());
~ZQuestFile(); ~ZQuestFile();
/*! /*!
@ -143,20 +142,22 @@ public:
*/ */
Uint32 length() const; Uint32 length() const;
void setGameString(const std::string& gameString);
/*! /*!
* \brief gameString * \brief gameString
* \return * \return
*/ */
std::string gameString() const; std::string gameString() const;
static const std::vector<std::string> gameStringList();
private: private:
Game m_game; Game m_game;
Endian m_endian; std::string m_gameString;
Uint8* m_data; Endian m_endian;
Uint32 m_length; Uint8* m_data;
Uint32 m_length;
// Game strings support // Game strings support
std::vector<std::string> m_gameStrings;
void initGameStrings();
}; };
} // zelda } // zelda

View File

@ -110,7 +110,10 @@ void Stream::writeBit(bool val)
else if (m_position > m_length) else if (m_position > m_length)
throw error::IOException("Stream::writeBit() -> Position outside stream bounds"); throw error::IOException("Stream::writeBit() -> Position outside stream bounds");
*(Uint8*)(m_data + m_position) |= ((Uint32)val << m_bitPosition); if (val)
*(Uint8*)(m_data + m_position) |= (1 << m_bitPosition);
else
*(Uint8*)(m_data + m_position) &= ~(1 << m_bitPosition);
m_bitPosition++; m_bitPosition++;
if (m_bitPosition > 7) if (m_bitPosition > 7)
{ {

View File

@ -18,15 +18,38 @@
namespace zelda namespace zelda
{ {
std::vector<std::string> GameStrings;
void initGameStrings()
{
// Populate game strings
GameStrings.push_back("No Game");
GameStrings.push_back("Legend Of Zelda");
GameStrings.push_back("Adventure of Link");
GameStrings.push_back("A Link to the Past");
GameStrings.push_back("Links Awakening");
GameStrings.push_back("Ocarina of Time");
GameStrings.push_back("Ocarina of Time 3D");
GameStrings.push_back("Majora's Mask");
GameStrings.push_back("Oracle of Seasons");
GameStrings.push_back("Oracle of Ages");
GameStrings.push_back("For Swords");
GameStrings.push_back("Wind Waker");
GameStrings.push_back("Four Swords Adventures");
GameStrings.push_back("Minish Cap");
GameStrings.push_back("Twilight Princess");
GameStrings.push_back("Phantom Hourglass");
GameStrings.push_back("Spirit Tracks");
GameStrings.push_back("Skyward Sword");
GameStrings.push_back("A Link Between Worlds");
}
const Uint32 ZQuestFile::Major = 1; const Uint32 ZQuestFile::Major = 2;
const Uint32 ZQuestFile::Minor = 0; const Uint32 ZQuestFile::Minor = 0;
const Uint32 ZQuestFile::Revision = 0; const Uint32 ZQuestFile::Revision = 0;
const Uint32 ZQuestFile::Build = 0;
const Uint32 ZQuestFile::Version = Major | (Minor << 8) | (Revision << 16) | (Build << 24); const Uint32 ZQuestFile::Version = Major | (Minor << 8) | (Revision << 16);
const Uint32 ZQuestFile::Magic = 'Z' | ('Q' << 8) | ('S' << 16) | ('1' << 24); const Uint32 ZQuestFile::Magic = 'Z' | ('Q' << 8) | ('S' << 16) | (('0' + ZQuestFile::Major) << 24);
ZQuestFile::ZQuestFile() ZQuestFile::ZQuestFile()
: m_game(NoGame), : m_game(NoGame),
@ -37,13 +60,16 @@ ZQuestFile::ZQuestFile()
initGameStrings(); initGameStrings();
} }
ZQuestFile::ZQuestFile(ZQuestFile::Game game, Endian endian, Uint8* data, Uint32 length) ZQuestFile::ZQuestFile(ZQuestFile::Game game, Endian endian, Uint8* data, Uint32 length, const std::string& gameString)
: m_game(game), : m_game(game),
m_gameString(gameString),
m_endian(endian), m_endian(endian),
m_data(data), m_data(data),
m_length(length) m_length(length)
{ {
initGameStrings(); initGameStrings();
if (gameString.empty() && (m_game < GameStrings.size() - 1))
m_gameString = GameStrings[m_game];
} }
ZQuestFile::~ZQuestFile() ZQuestFile::~ZQuestFile()
@ -56,6 +82,10 @@ ZQuestFile::~ZQuestFile()
void ZQuestFile::setGame(ZQuestFile::Game game) void ZQuestFile::setGame(ZQuestFile::Game game)
{ {
m_game = game; m_game = game;
if (m_game > GameStrings.size() - 1)
return;
m_gameString = GameStrings[m_game];
} }
ZQuestFile::Game ZQuestFile::game() const ZQuestFile::Game ZQuestFile::game() const
@ -99,36 +129,20 @@ Uint32 ZQuestFile::length() const
return m_length; return m_length;
} }
void ZQuestFile::setGameString(const std::string& gameString)
{
m_gameString = gameString;
}
std::string ZQuestFile::gameString() const std::string ZQuestFile::gameString() const
{ {
if (m_game > m_gameStrings.size() - 1) return m_gameString;
return "Unsupported Game";
return m_gameStrings[m_game];
} }
void ZQuestFile::initGameStrings() const std::vector<std::string> ZQuestFile::gameStringList()
{ {
// Populate game strings if (GameStrings.size() <= 0)
m_gameStrings.push_back("No Game"); initGameStrings();
m_gameStrings.push_back("Legend Of Zelda"); return GameStrings;
m_gameStrings.push_back("Adventure of Link");
m_gameStrings.push_back("A Link to the Past");
m_gameStrings.push_back("Links Awakening");
m_gameStrings.push_back("Ocarina of Time");
m_gameStrings.push_back("Ocarina of Time 3D");
m_gameStrings.push_back("Majora's Mask");
m_gameStrings.push_back("Oracle of Seasons");
m_gameStrings.push_back("Oracle of Ages");
m_gameStrings.push_back("For Swords");
m_gameStrings.push_back("Wind Waker");
m_gameStrings.push_back("Four Swords Adventures");
m_gameStrings.push_back("Minish Cap");
m_gameStrings.push_back("Twilight Princess");
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)"); // Probably should have this, but.....
} }
} }

View File

@ -17,6 +17,11 @@
#include "ZQuestFile.hpp" #include "ZQuestFile.hpp"
#include "InvalidOperationException.hpp" #include "InvalidOperationException.hpp"
#include "Compression.hpp" #include "Compression.hpp"
#include "InvalidDataException.hpp"
#include "Checksums.hpp"
#include <iostream>
#include <iomanip>
#include <utility.hpp>
namespace zelda namespace zelda
{ {
@ -36,27 +41,64 @@ ZQuestFileReader::ZQuestFileReader(const std::string &filename)
ZQuestFile *ZQuestFileReader::read() ZQuestFile *ZQuestFileReader::read()
{ {
Uint32 magic, version, compressedLen, uncompressedLen; Uint32 magic, version, compressedLen, uncompressedLen;
ZQuestFile::Game game; ZQuestFile::Game game = ZQuestFile::NoGame;
std::string gameString;
Uint16 BOM; Uint16 BOM;
Uint32 checksum;
Uint8* data; Uint8* data;
magic = base::readUInt32(); magic = base::readUInt32();
if (magic != ZQuestFile::Magic) if ((magic & 0x00FFFFFF) != (ZQuestFile::Magic & 0x00FFFFFF))
throw error::InvalidOperationException("ZQuestFileReader::read -> Not a valid ZQuest file"); THROW_INVALID_DATA("Not a valid ZQuest file");
version = base::readUInt32(); version = base::readUInt32();
if (version != ZQuestFile::Version) if (version > ZQuestFile::Version)
throw error::InvalidOperationException("ZQuestFileReader::read -> Unsupported ZQuest version"); THROW_INVALID_DATA("Unsupported ZQuest version");
compressedLen = base::readUInt32(); compressedLen = base::readUInt32();
uncompressedLen = base::readUInt32(); uncompressedLen = base::readUInt32();
game = (ZQuestFile::Game)base::readUInt32();
BOM = base::readUInt16(); if (version >= ZQUEST_VERSION_CHECK(2, 0, 0))
base::seek(0x0A); {
gameString = ((const char*)base::readBytes(0x0A), 0x0A);
for (size_t i = 0; i < ZQuestFile::gameStringList().size(); i++)
{
if (!ZQuestFile::gameStringList().at(i).substr(0, 0x0A).compare(gameString))
{
gameString = ZQuestFile::gameStringList().at(i);
break;
}
}
BOM = base::readUInt16();
checksum = base::readUInt32();
}
else
{
game = (ZQuestFile::Game)base::readUInt32();
BOM = base::readUInt16();
std::cerr << "Test" << std::endl;
base::seek(0x0A);
}
data = (Uint8*)base::readBytes(compressedLen); // compressedLen is always the total file size data = (Uint8*)base::readBytes(compressedLen); // compressedLen is always the total file size
if (version >= ZQUEST_VERSION_CHECK(2, 0, 0))
{
if (checksum != zelda::Checksums::crc32(data, compressedLen))
{
delete[] data;
THROW_INVALID_DATA("Checksum mismatch, data corrupt");
}
}
else
{
std::clog << "ZQuest version 0x" << std::uppercase << std::setw(8) << std::setfill('0') << std::hex << zelda::utility::swapU32(version);
std::clog << " has no checksum field" << std::endl;
}
if (compressedLen != uncompressedLen) if (compressedLen != uncompressedLen)
{ {
Uint8* dst = new Uint8[uncompressedLen]; Uint8* dst = new Uint8[uncompressedLen];
@ -67,14 +109,14 @@ ZQuestFile *ZQuestFileReader::read()
delete[] dst; delete[] dst;
delete[] data; delete[] data;
// TODO: Make proper exception // TODO: Make proper exception
throw error::InvalidOperationException("ZQuestFileReader::read -> Error decompressing data"); THROW_INVALID_DATA("Error decompressing data");
} }
delete[] data; delete[] data;
data = dst; data = dst;
} }
return new ZQuestFile(game, BOM == 0xFEFF ? BigEndian : LittleEndian, data, uncompressedLen); return new ZQuestFile(game, BOM == 0xFEFF ? BigEndian : LittleEndian, data, uncompressedLen, gameString);
} }
} // io } // io

View File

@ -17,6 +17,7 @@
#include "InvalidOperationException.hpp" #include "InvalidOperationException.hpp"
#include "ZQuestFile.hpp" #include "ZQuestFile.hpp"
#include "Compression.hpp" #include "Compression.hpp"
#include "Checksums.hpp"
namespace zelda namespace zelda
{ {
@ -36,7 +37,7 @@ ZQuestFileWriter::ZQuestFileWriter(const std::string& filename)
void ZQuestFileWriter::write(ZQuestFile* quest, bool compress) void ZQuestFileWriter::write(ZQuestFile* quest, bool compress)
{ {
if (!quest) if (!quest)
throw error::InvalidOperationException("ZQuestFileWriter::write -> quest cannot be NULL"); THROW_INVALIDOPERATION_EXCEPTION("quest cannot be NULL");
base::writeUInt32(ZQuestFile::Magic); base::writeUInt32(ZQuestFile::Magic);
base::writeUInt32(ZQuestFile::Version); base::writeUInt32(ZQuestFile::Version);
@ -71,9 +72,9 @@ void ZQuestFileWriter::write(ZQuestFile* quest, bool compress)
} }
base::writeUInt32(quest->length()); base::writeUInt32(quest->length());
base::writeUInt32(quest->game()); base::writeBytes((Int8*)quest->gameString().substr(0, 0x0A).c_str(), 0x0A);
base::writeUInt16(quest->endian() == BigEndian ? 0xFFFE : 0xFEFF); base::writeUInt16(quest->endian() == BigEndian ? 0xFFFE : 0xFEFF);
base::seek(0x0A); base::writeUInt32(zelda::Checksums::crc32(questData, compLen));
base::writeUBytes(questData, compLen); base::writeUBytes(questData, compLen);
base::save(); base::save();