diff --git a/include/Exception.hpp b/include/Exception.hpp index ae6d614..cea139e 100644 --- a/include/Exception.hpp +++ b/include/Exception.hpp @@ -18,6 +18,10 @@ #include +#define __STRX(x) #x +#define __STR(x) __STRX(x) +#define __LINE_STRING__ __STR(__LINE__) + namespace zelda { namespace error @@ -53,4 +57,10 @@ protected: } // error } // zelda +#define THROW_EXCEPTION(msg) \ + do \ + { \ + throw zelda::error::Exception(__LINE_STRING__ " " __FILE__ " " msg); \ + } while(0) + #endif diff --git a/include/InvalidDataException.hpp b/include/InvalidDataException.hpp index b403d5f..d585a3f 100644 --- a/include/InvalidDataException.hpp +++ b/include/InvalidDataException.hpp @@ -2,6 +2,7 @@ #define INVALIDDATAEXCEPTION_HPP #include "Exception.hpp" +#include namespace zelda { @@ -20,10 +21,17 @@ class InvalidDataException : public Exception { public: inline InvalidDataException(const std::string& error) - : Exception("InvalidDataException: " + error) + : Exception(error) { } }; + } } + +#define THROW_INVALID_DATA(msg) \ + do \ + { \ + throw zelda::error::InvalidDataException(__LINE_STRING__ " " __FILE__ " " msg); \ + } while(0) #endif // INVALIDDATAEXCEPTION_HPP diff --git a/src/SkywardSwordFileReader.cpp b/src/SkywardSwordFileReader.cpp index 8257e07..afc2886 100644 --- a/src/SkywardSwordFileReader.cpp +++ b/src/SkywardSwordFileReader.cpp @@ -24,32 +24,41 @@ SkywardSwordFileReader::SkywardSwordFileReader(const std::string& filename) SkywardSwordFile* SkywardSwordFileReader::read() { SkywardSwordFile* 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 != SkywardSwordFile::USMagic && magic != SkywardSwordFile::JAMagic && magic != SkywardSwordFile::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 SkywardSwordFile; - file->setRegion((magic == SkywardSwordFile::USMagic ? NTSCURegion : (magic == SkywardSwordFile::JAMagic ? NTSCJRegion : PALRegion))); - for (int i = 0; i < 3; i++) + try { - SkywardSwordQuest* q = new SkywardSwordQuest((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); + if (base::length() != 0xFBE0) + throw zelda::error::InvalidOperationException("SSFileReader::read -> File not the expected size of 0xFBE0"); + + Uint32 magic = base::readUInt32(); + + if (magic != SkywardSwordFile::USMagic && magic != SkywardSwordFile::JAMagic && magic != SkywardSwordFile::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 SkywardSwordFile; + file->setRegion((magic == SkywardSwordFile::USMagic ? NTSCURegion : (magic == SkywardSwordFile::JAMagic ? NTSCJRegion : PALRegion))); + for (int i = 0; i < 3; i++) + { + SkywardSwordQuest* q = new SkywardSwordQuest((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); + } + } + catch(...) + { + delete file; + file = NULL; + throw; } return file; diff --git a/src/SpriteFileReader.cpp b/src/SpriteFileReader.cpp index 4016ea6..cf12735 100644 --- a/src/SpriteFileReader.cpp +++ b/src/SpriteFileReader.cpp @@ -23,124 +23,134 @@ SpriteFileReader::SpriteFileReader(const std::string& filepath) Sakura::SpriteFile* SpriteFileReader::readFile() { - Uint32 magic = base::readUInt32(); - - if (magic != Sakura::SpriteFile::Magic) - throw zelda::error::InvalidOperationException("Not a valid Sakura Sprite container"); - - Uint32 version = base::readUInt32(); - - // TODO: Make this more verbose - if (version != Sakura::SpriteFile::Version) - throw zelda::error::InvalidOperationException("Unsupported version"); - - // After reading in the magic and version we need to load some - // metadata about the file. - // Such as the texture count, it's dimensions, and it's origin. - // After that we have the number of sprites contained in this - // sprite container. - Uint16 textureCount = base::readUInt16(); // Having it as a Uint16 gives us the ability to have up to 65536 different states - // This is probably overkill, but it's better safe than sorry. - Uint32 width = base::readUInt32(); - Uint32 height = base::readUInt32(); - float originX = base::readFloat(); - float originY = base::readFloat(); - Uint16 spriteCount = base::readUInt16(); - - // Lets go ahead and create or new container. - Sakura::SpriteFile* ret = new Sakura::SpriteFile(width, height, originX, originY); - - // The next four bytes are reserved to keep the header 32 byte aligned. - // This isn't necessary for most systems, but it's eventually planned - // to migrate this code to Big Endian based systems, such as the wii - // which require data to be 32 byte aligned, or it causes some issues. - // It's also convenient to have this, for later expansion. - Uint32 reserved = base::readUInt32(); - UNUSED(reserved); - - // Next we have to load the textures - // If we tried to add them one at a time to the sprite container - // it will be slow as hell, so we store them in a vector locally - // then give that vector the the container, this bypasses the de-reference - // for each texture - std::vector textures; - - for (Uint16 i = 0; i < textureCount; i++) + Sakura::SpriteFile* ret = NULL; + try { - Sakura::STexture* texture = new Sakura::STexture; - texture->Filepath = base::readString(); - texture->Preload = base::readBool(); - textures.push_back(texture); - } + Uint32 magic = base::readUInt32(); - ret->setTextures(textures); + if (magic != Sakura::SpriteFile::Magic) + throw zelda::error::InvalidOperationException("Not a valid Sakura Sprite container"); - // Now for the sprites - // The sprites are a bit more difficult, they are stored in an unordered_map - // with it's name as the key, this means we can't have two sprites with the same name - // Normally this isn't a problem, but someone may decide to copy and paste a sprite - // and forget to change the name, that needs to be handled, but it's outside the scope - // of this reader. - std::unordered_map sprites; + Uint32 version = base::readUInt32(); - for (Uint16 i = 0; i < spriteCount; i++) - { - Sakura::Sprite* sprite = new Sakura::Sprite(ret); - sprite->setName(base::readString()); - Uint16 partCount = base::readUInt16(); - Uint16 stateCount = base::readUInt16(); + // TODO: Make this more verbose + if (version != Sakura::SpriteFile::Version) + throw zelda::error::InvalidOperationException("Unsupported version"); - // Each state id corresponds to a texture held in the parent class - std::vector stateIds; - for (int j = 0; j < stateCount; j++) - stateIds.push_back(base::readUInt16()); + // After reading in the magic and version we need to load some + // metadata about the file. + // Such as the texture count, it's dimensions, and it's origin. + // After that we have the number of sprites contained in this + // sprite container. + Uint16 textureCount = base::readUInt16(); // Having it as a Uint16 gives us the ability to have up to 65536 different states + // This is probably overkill, but it's better safe than sorry. + Uint32 width = base::readUInt32(); + Uint32 height = base::readUInt32(); + float originX = base::readFloat(); + float originY = base::readFloat(); + Uint16 spriteCount = base::readUInt16(); + // Lets go ahead and create or new container. + ret = new Sakura::SpriteFile(width, height, originX, originY); - sprite->setStateIds(stateIds); + // The next four bytes are reserved to keep the header 32 byte aligned. + // This isn't necessary for most systems, but it's eventually planned + // to migrate this code to Big Endian based systems, such as the wii + // which require data to be 32 byte aligned, or it causes some issues. + // It's also convenient to have this, for later expansion. + Uint32 reserved = base::readUInt32(); + UNUSED(reserved); - // Now to read the sprite parts. - // The parts allow us to build retro style sprites very easily - // making it possible to use one texture atlas for all possible - // frame combinations, this reduces the amount of memory overhead - // and the storage footprint, while Sakura supports packs and zips - // it's still a bad idea to have a metric ton of texture resources - // littering the place - std::vector parts; - for (Uint8 j = 0; j < partCount; j++) + // Next we have to load the textures + // If we tried to add them one at a time to the sprite container + // it will be slow as hell, so we store them in a vector locally + // then give that vector the the container, this bypasses the de-reference + // for each texture + std::vector textures; + + for (Uint16 i = 0; i < textureCount; i++) { - Sakura::SpritePart* part = new Sakura::SpritePart(sprite); - part->setName(base::readString()); - part->setCollision(base::readBool()); - - Uint32 frameCount = base::readUInt32(); - std::vector frames; - - for (Uint32 k = 0; k < frameCount; k++) - { - float xOff = base::readFloat(); - float yOff = base::readFloat(); - float texXOff = base::readFloat(); - float texYOff = base::readFloat(); - Uint32 width = base::readUInt32(); - Uint32 height = base::readUInt32(); - float frameTime = base::readFloat(); - bool flippedH = base::readBool(); - bool flippedV = base::readBool(); - - frames.push_back(new Sakura::SpriteFrame(xOff, yOff, texXOff, texYOff, width, height, frameTime, flippedH, flippedV)); - } - part->setFrames(frames); - parts.push_back(part); + Sakura::STexture* texture = new Sakura::STexture; + texture->Filepath = base::readString(); + texture->Preload = base::readBool(); + textures.push_back(texture); } - sprite->setParts(parts); - if (sprite->name() != std::string()) - sprites[sprite->name()] = sprite; - else - throw zelda::error::IOException("SSpriteFileReader::readFile -> Sprite names cannot be empty"); - } + ret->setTextures(textures); - ret->setSprites(sprites); + // Now for the sprites + // The sprites are a bit more difficult, they are stored in an unordered_map + // with it's name as the key, this means we can't have two sprites with the same name + // Normally this isn't a problem, but someone may decide to copy and paste a sprite + // and forget to change the name, that needs to be handled, but it's outside the scope + // of this reader. + std::unordered_map sprites; + + for (Uint16 i = 0; i < spriteCount; i++) + { + Sakura::Sprite* sprite = new Sakura::Sprite(ret); + sprite->setName(base::readString()); + Uint16 partCount = base::readUInt16(); + Uint16 stateCount = base::readUInt16(); + + // Each state id corresponds to a texture held in the parent class + std::vector stateIds; + for (int j = 0; j < stateCount; j++) + stateIds.push_back(base::readUInt16()); + + + sprite->setStateIds(stateIds); + + // Now to read the sprite parts. + // The parts allow us to build retro style sprites very easily + // making it possible to use one texture atlas for all possible + // frame combinations, this reduces the amount of memory overhead + // and the storage footprint, while Sakura supports packs and zips + // it's still a bad idea to have a metric ton of texture resources + // littering the place + std::vector parts; + for (Uint8 j = 0; j < partCount; j++) + { + Sakura::SpritePart* part = new Sakura::SpritePart(sprite); + part->setName(base::readString()); + part->setCollision(base::readBool()); + + Uint32 frameCount = base::readUInt32(); + std::vector frames; + + for (Uint32 k = 0; k < frameCount; k++) + { + float xOff = base::readFloat(); + float yOff = base::readFloat(); + float texXOff = base::readFloat(); + float texYOff = base::readFloat(); + Uint32 width = base::readUInt32(); + Uint32 height = base::readUInt32(); + float frameTime = base::readFloat(); + bool flippedH = base::readBool(); + bool flippedV = base::readBool(); + + frames.push_back(new Sakura::SpriteFrame(xOff, yOff, texXOff, texYOff, width, height, frameTime, flippedH, flippedV)); + } + part->setFrames(frames); + parts.push_back(part); + } + + sprite->setParts(parts); + if (sprite->name() != std::string()) + sprites[sprite->name()] = sprite; + else + throw zelda::error::IOException("SSpriteFileReader::readFile -> Sprite names cannot be empty"); + } + + ret->setSprites(sprites); + } + catch(...) + { + delete ret; + ret = NULL; + throw; + } return ret; } diff --git a/src/WiiSaveReader.cpp b/src/WiiSaveReader.cpp index 6afc361..77cb578 100644 --- a/src/WiiSaveReader.cpp +++ b/src/WiiSaveReader.cpp @@ -54,50 +54,60 @@ WiiSaveReader::WiiSaveReader(const std::string& filename) WiiSave* WiiSaveReader::readSave() { WiiSave* ret = new WiiSave; - if (length() < 0xF0C0) - throw error::InvalidOperationException("WiiSaveReader::readSave -> Not a valid WiiSave"); - - WiiBanner* banner = this->readBanner(); - if (!banner) - throw error::InvalidOperationException("WiiSaveReader::readSave -> Invalid banner"); - - ret->setBanner(banner); - Uint32 bkVer = base::readUInt32(); - - if (bkVer != 0x00000070) - throw error::InvalidOperationException("WiiSaveReader::readSave -> Invalid BacKup header size"); - - Uint32 bkMagic = base::readUInt32(); - bkMagic = bkMagic; - if (bkMagic != 0x426B0001) - throw error::InvalidOperationException("WiiSaveReader::readSave -> Invalid BacKup header magic"); - - Uint32 ngId = base::readUInt32(); - ngId = ngId; - - Uint32 numFiles = base::readUInt32(); - - /*int fileSize =*/ base::readUInt32(); - base::seek(8); // skip unknown data; - - Uint32 totalSize = base::readUInt32(); - base::seek(64); // Unknown (Most likely padding) - base::seek(8); - base::seek(6); - base::seek(2); - base::seek(0x10); - - std::unordered_map files; - for (Uint32 i = 0; i < numFiles; ++i) + try { - WiiFile* file = readFile(); - if (file) - files["/"+file->filename()] = file; + if (length() < 0xF0C0) + throw error::InvalidOperationException("WiiSaveReader::readSave -> Not a valid WiiSave"); + + WiiBanner* banner = this->readBanner(); + if (!banner) + throw error::InvalidOperationException("WiiSaveReader::readSave -> Invalid banner"); + + ret->setBanner(banner); + Uint32 bkVer = base::readUInt32(); + + if (bkVer != 0x00000070) + throw error::InvalidOperationException("WiiSaveReader::readSave -> Invalid BacKup header size"); + + Uint32 bkMagic = base::readUInt32(); + bkMagic = bkMagic; + if (bkMagic != 0x426B0001) + throw error::InvalidOperationException("WiiSaveReader::readSave -> Invalid BacKup header magic"); + + Uint32 ngId = base::readUInt32(); + ngId = ngId; + + Uint32 numFiles = base::readUInt32(); + + /*int fileSize =*/ base::readUInt32(); + base::seek(8); // skip unknown data; + + Uint32 totalSize = base::readUInt32(); + base::seek(64); // Unknown (Most likely padding) + base::seek(8); + base::seek(6); + base::seek(2); + base::seek(0x10); + + std::unordered_map files; + for (Uint32 i = 0; i < numFiles; ++i) + { + WiiFile* file = readFile(); + if (file) + files["/"+file->filename()] = file; + } + + ret->setFiles(files); + + readCerts(totalSize); + } + catch(...) + { + delete ret; + ret = NULL; + throw; } - ret->setFiles(files); - - readCerts(totalSize); return ret; } @@ -154,7 +164,7 @@ WiiBanner* WiiSaveReader::readBanner() gameId = base::readUInt64(); bannerSize = base::readUInt32(); permissions = base::readByte(); -/* unk =*/ base::readByte(); + /* unk =*/ base::readByte(); base::seek(0x10); // skip padding base::seek(2);