diff --git a/Athena.pri b/Athena.pri index 0a1c35c..b26f73a 100644 --- a/Athena.pri +++ b/Athena.pri @@ -49,7 +49,8 @@ SOURCES += \ $$PWD/src/LZ77/LZLookupTable.cpp \ $$PWD/src/LZ77/LZType10.cpp \ $$PWD/src/LZ77/LZType11.cpp \ - $$PWD/src/LZ77/LZBase.cpp + $$PWD/src/LZ77/LZBase.cpp \ + $$PWD/src/Athena/MCSlot.cpp win32:SOURCES += $$PWD/src/win32_largefilewrapper.c HEADERS += \ @@ -111,7 +112,8 @@ HEADERS += \ $$PWD/include/LZ77/LZBase.hpp \ $$PWD/include/LZ77/LZLookupTable.hpp \ $$PWD/include/LZ77/LZType10.hpp \ - $$PWD/include/LZ77/LZType11.hpp + $$PWD/include/LZ77/LZType11.hpp \ + $$PWD/src/Athena/MCSlot.hpp win32:HEADERS += \ $$PWD/include/win32_largefilewrapper.h diff --git a/include/Athena/ALTTPQuest.hpp b/include/Athena/ALTTPQuest.hpp index 975d173..e5eaf5e 100644 --- a/include/Athena/ALTTPQuest.hpp +++ b/include/Athena/ALTTPQuest.hpp @@ -641,28 +641,28 @@ private: std::vector m_roomFlags; std::vector m_overworldEvents; ALTTPInventory* m_inventory; - atUint16 m_rupeeMax; - atUint16 m_rupeeCurrent; + atUint16 m_rupeeMax; + atUint16 m_rupeeCurrent; ALTTPDungeonItemFlags m_compasses; ALTTPDungeonItemFlags m_bigKeys; ALTTPDungeonItemFlags m_dungeonMaps; - atUint16 m_wishingPond; - atUint8 m_healthMax; - atUint8 m_health; - atUint8 m_magicPower; - atUint8 m_keys; - atUint8 m_bombUpgrades; - atUint8 m_arrowUpgrades; - atUint8 m_heartFiller; - atUint8 m_magicFiller; + atUint16 m_wishingPond; + atUint8 m_healthMax; + atUint8 m_health; + atUint8 m_magicPower; + atUint8 m_keys; + atUint8 m_bombUpgrades; + atUint8 m_arrowUpgrades; + atUint8 m_heartFiller; + atUint8 m_magicFiller; ALTTPPendants m_pendants; - atUint8 m_bombFiller; - atUint8 m_arrowFiller; - atUint8 m_arrows; + atUint8 m_bombFiller; + atUint8 m_arrowFiller; + atUint8 m_arrows; ALTTPAbilities m_abilityFlags; ALTTPCrystals m_crystals; ALTTPMagicUsage m_magicUsage; - std::vector m_dungeonKeys; + std::vector m_dungeonKeys; ALTTPProgressIndicator m_progressIndicator; ALTTPProgressFlags1 m_progressFlags1; ALTTPMapIcon m_mapIcon; @@ -670,16 +670,16 @@ private: ALTTPProgressFlags2 m_progressFlags2; ALTTPLightDarkWorldIndicator m_lightDarkWorldIndicator; ALTTPTagAlong m_tagAlong; - std::vector m_oldManFlags; - atUint8 m_bombFlag; - std::vector m_unknown1; - std::vector m_playerName; - atUint16 m_valid; - std::vector m_dungeonDeathTotals; - atUint16 m_unknown2; - atUint16 m_deathSaveCount; - atInt16 m_postGameDeathCounter; - atUint16 m_checksum; + std::vector m_oldManFlags; + atUint8 m_bombFlag; + std::vector m_unknown1; + std::vector m_playerName; + atUint16 m_valid; + std::vector m_dungeonDeathTotals; + atUint16 m_unknown2; + atUint16 m_deathSaveCount; + atInt16 m_postGameDeathCounter; + atUint16 m_checksum; }; } // zelda diff --git a/include/Athena/ALTTPStructs.hpp b/include/Athena/ALTTPStructs.hpp index da57f8f..9c4f5b1 100644 --- a/include/Athena/ALTTPStructs.hpp +++ b/include/Athena/ALTTPStructs.hpp @@ -106,22 +106,37 @@ struct ALTTPLightDarkWorldIndicator struct ALTTPDungeonItemFlags { - bool Unused1:1; - bool Unused2:1; - bool GanonsTower:1; - bool TurtleRock:1; - bool GargoylesDomain:1; - bool TowerOfHera:1; - bool IcePalace:1; - bool SkullWoods:1; - bool MiseryMire:1; - bool DarkPalace:1; - bool SwampPalace:1; - bool HyruleCastle2:1; // Doesn't exists in orignal game - bool DesertPalace:1; - bool EasternPalace:1; - bool HyruleCastle:1; // Doesn't exist in original game - bool SewerPassage:1; // Doesn't exist in original game + union + { + struct + { + bool Unused1:1; + bool Unused2:1; + bool GanonsTower:1; + bool TurtleRock:1; + bool GargoylesDomain:1; + bool TowerOfHera:1; + bool IcePalace:1; + bool SkullWoods:1; + }; + atUint8 flags1; + }; + + union + { + struct + { + bool MiseryMire:1; + bool DarkPalace:1; + bool SwampPalace:1; + bool HyruleCastle2:1; // unused in orignal game + bool DesertPalace:1; + bool EasternPalace:1; + bool HyruleCastle:1; // unused exist in original game + bool SewerPassage:1; // unused exist in original game + }; + atUint8 flags2; + }; }; struct ALTTPPendants diff --git a/include/Athena/Compression.hpp b/include/Athena/Compression.hpp index 215aed6..aba7602 100644 --- a/include/Athena/Compression.hpp +++ b/include/Athena/Compression.hpp @@ -32,7 +32,7 @@ atInt32 compressZlib(const atUint8* src, atUint32 srcLen, atUint8* dst, atUint32 atInt32 decompressLZO(const atUint8* source, atInt32 sourceSize, atUint8* dst, atInt32& dstSize); // Yaz0 encoding -atUint32 yaz0Decode(const atUint8* src, atUint8*& dst, atUint32 uncompressedSize); +atUint32 yaz0Decode(const atUint8* src, atUint8* dst, atUint32 uncompressedSize); atUint32 yaz0Encode(const atUint8* src, atUint32 srcSize, atUint8* data); atUint32 decompressLZ77(const atUint8* src, atUint32 srcLen, atUint8** dst); diff --git a/include/Athena/Global.hpp b/include/Athena/Global.hpp index 00188c5..d6d59a3 100644 --- a/include/Athena/Global.hpp +++ b/include/Athena/Global.hpp @@ -30,7 +30,7 @@ # elif defined(__func__) # define __PRETTY_FUNCTION__ __func__ # else -# define __PRETTY_FUNCTION__ "" +# define __PRETTY_FUNCTION__ "" # endif #endif diff --git a/include/Athena/MCFile.hpp b/include/Athena/MCFile.hpp index bdab882..88ce159 100644 --- a/include/Athena/MCFile.hpp +++ b/include/Athena/MCFile.hpp @@ -16,9 +16,12 @@ #ifndef __MCFILE_HPP__ #define __MCFILE_HPP__ +#include "Athena/Global.hpp" + namespace Athena { +class MCSlot; /*! \class MCFile * \brief The Minish Cap data container class class * @@ -28,8 +31,20 @@ namespace Athena class MCFile { public: + static constexpr char* VERSION_EU_JP = (char*)"AGBZELDA:THE MINISH CAP:ZELDA 3\0"; + static constexpr char* VERSION_US = (char*)"AGBZELDA:THE MINISH CAP:ZELDA 5\0"; + enum SlotType + { + New = 0x54494E49, + Valid = 0x4D435A33, + Deleted = 0x466C6544 + }; + MCFile(); + + static atUint8* unscramble(atUint8* data, atUint32 length); private: + MCSlot* m_slots[3]; }; } // zelda diff --git a/include/Athena/MCFileReader.hpp b/include/Athena/MCFileReader.hpp index d632ae9..7cdc7e7 100644 --- a/include/Athena/MCFileReader.hpp +++ b/include/Athena/MCFileReader.hpp @@ -34,7 +34,7 @@ namespace io * all work is done using a memory buffer, and not read directly from the disk. * \sa BinaryReader */ -class MCFileReader : protected BinaryReader +class MCFileReader : public BinaryReader { BINARYREADER_BASE(); public: diff --git a/include/Athena/MCFileWriter.hpp b/include/Athena/MCFileWriter.hpp index 3ea89fb..65709fa 100644 --- a/include/Athena/MCFileWriter.hpp +++ b/include/Athena/MCFileWriter.hpp @@ -61,11 +61,9 @@ public: */ void writeFile(MCFile* file); + static atUint16 calculateChecksum(atUint8* data, atUint32 length); private: atUint16 calculateSlotChecksum(atUint32 game); - atUint16 calculateChecksum(atUint8* data, atUint32 length); - atUint8* reverse(atUint8* data, atUint32 length); - void unscramble(); }; } // io diff --git a/include/Athena/SkywardSwordFile.hpp b/include/Athena/SkywardSwordFile.hpp index ca88153..f3bd962 100644 --- a/include/Athena/SkywardSwordFile.hpp +++ b/include/Athena/SkywardSwordFile.hpp @@ -53,6 +53,7 @@ public: void setRegion(Region region); Region region() const; + private: Region m_region; // A vector is a bit overkill diff --git a/include/Athena/SkywardSwordQuest.hpp b/include/Athena/SkywardSwordQuest.hpp index c8ec5cc..5b2bc13 100644 --- a/include/Athena/SkywardSwordQuest.hpp +++ b/include/Athena/SkywardSwordQuest.hpp @@ -1,4 +1,4 @@ -#if !defined(ATHENA_NO_SAVES) && !defined(ATHENA_NO_ZQUEST) +#if !defined(ATHENA_NO_SAVES) && !defined(ATHENA_NO_ZQUEST) // This file is part of libAthena. // // libAthena is free software: you can redistribute it and/or modify @@ -27,17 +27,47 @@ namespace Athena class SkywardSwordQuest : public ZQuestFile { public: + enum AmmoType + { + Arrows, + Bombs, + Seeds + }; + SkywardSwordQuest(atUint8* data, atUint32 len); - // TODO: Is len really needed? - void setSkipData(const atUint8* data, atUint32 len = 0x24); + void setPlayerName(const std::string& name); + std::string playerName() const; + + void setRupeeCount(atUint16 value); + atUint16 rupeeCount(); + void setAmmoCount(AmmoType type, atUint32 count); + atUint32 ammoCount(AmmoType type); + void setMaxHP(atUint16 val); + atUint16 maxHP(); + float maxHearts(); + void setSpawnHP(atUint16 val); + atUint16 spawnHP(); + float spawnHearts(); + void setCurrentHP(atUint16 val); + atUint16 currentHP(); + float currentHearts(); + std::string currentLocation(); + std::string currentArea(); + std::string currentLocationCopy(); + + void setSkipData(const atUint8* data); atUint8* skipData() const; - atUint32 skipLength() const; + atUint32 slotChecksum(); + atUint32 skipChecksum(); + void fixChecksums(); + + void setNew(bool isNew); + bool isNew() const; private: atUint8* m_skipData; - atUint32 m_skipLength; }; diff --git a/include/Athena/Utility.hpp b/include/Athena/Utility.hpp index a3c23fd..c418235 100644 --- a/include/Athena/Utility.hpp +++ b/include/Athena/Utility.hpp @@ -47,7 +47,7 @@ atUint32 BigUint32(atUint32& val); atInt64 LittleInt64(atInt64& val); atUint64 LittleUint64(atUint64& val); atInt64 BigInt64(atInt64& val); -atUint16 BigUint64(atUint16& val); +atUint64 BigUint64(atUint64& val); float LittleFloat(float& val); float BigFloat(float& val); @@ -66,6 +66,14 @@ bool parseBool(const std::string& boolean, bool* valid = NULL); int countChar(const std::string& str, const char chr, int* lastOccur = NULL); +// trim from start +std::string <rim(std::string &s); + +// trim from end +std::string &rtrim(std::string &s); + +// trim from both ends +std::string &trim(std::string &s); atUint64 fileSize(FILE* f); } // utility } // Athena diff --git a/include/Athena/WiiSave.hpp b/include/Athena/WiiSave.hpp index 2bb8a57..c88d1ad 100644 --- a/include/Athena/WiiSave.hpp +++ b/include/Athena/WiiSave.hpp @@ -58,7 +58,7 @@ public: * \param filename * \param file */ - void addFile(const std::string& filename, WiiFile* file); + void addFile(WiiFile* file); void setRoot(WiiFile* root); /*! * \brief file diff --git a/include/Athena/ZQuestFile.hpp b/include/Athena/ZQuestFile.hpp index f21e30e..33957a1 100644 --- a/include/Athena/ZQuestFile.hpp +++ b/include/Athena/ZQuestFile.hpp @@ -152,12 +152,12 @@ public: std::string gameString() const; static const std::vector gameStringList(); -private: +protected: Game m_game; std::string m_gameString; Endian m_endian; - atUint8* m_data; - atUint32 m_length; + atUint8* m_data; + atUint32 m_length; // Game strings support }; diff --git a/src/Athena/ALTTPFileReader.cpp b/src/Athena/ALTTPFileReader.cpp index 3f3f7ed..1e7d780 100644 --- a/src/Athena/ALTTPFileReader.cpp +++ b/src/Athena/ALTTPFileReader.cpp @@ -18,6 +18,7 @@ #include "Athena/ALTTPFile.hpp" #include "Athena/ALTTPQuest.hpp" #include +#include "Athena/Global.hpp" namespace Athena { @@ -80,9 +81,9 @@ ALTTPFile* ALTTPFileReader::readFile() quest->setHealthFiller(base::readByte()); quest->setMagicFiller(base::readByte()); ALTTPPendants pendants; - pendants.Courage = readBit(); - pendants.Wisdom = readBit(); - pendants.Power = readBit(); + pendants.Courage = base::readBit(); + pendants.Wisdom = base::readBit(); + pendants.Power = base::readBit(); pendants.Unused1 = false; pendants.Unused2 = false; pendants.Unused3 = false; @@ -214,6 +215,7 @@ ALTTPDungeonItemFlags ALTTPFileReader::readDungeonFlags() { ALTTPDungeonItemFlags flags; flags.Unused1 = base::readBit(); + flags.Unused2 = base::readBit(); flags.GanonsTower = base::readBit(); flags.TurtleRock = base::readBit(); flags.GargoylesDomain = base::readBit(); @@ -229,6 +231,7 @@ ALTTPDungeonItemFlags ALTTPFileReader::readDungeonFlags() flags.HyruleCastle = base::readBit(); flags.SewerPassage = base::readBit(); + aDebug() << std::hex << flags.flags1 << " " << flags.flags2 << std::dec << std::endl; return flags; } diff --git a/src/Athena/ALTTPQuest.cpp b/src/Athena/ALTTPQuest.cpp index e5d8eab..40c9eb9 100644 --- a/src/Athena/ALTTPQuest.cpp +++ b/src/Athena/ALTTPQuest.cpp @@ -542,12 +542,9 @@ std::vector ALTTPQuest::playerName() const std::string ALTTPQuest::playerNameToString() const { std::string ret; - std::vector::const_iterator iter = m_playerName.begin(); - for (; iter != m_playerName.end(); ++iter) + for (atInt16 c : m_playerName) { - atInt16 c = *iter; - if (c >= 0x00 && c <= 0x0F) { ret.push_back((char)('A' + c)); diff --git a/src/Athena/BinaryWriter.cpp b/src/Athena/BinaryWriter.cpp index e95f0d3..39a5dc3 100644 --- a/src/Athena/BinaryWriter.cpp +++ b/src/Athena/BinaryWriter.cpp @@ -302,8 +302,10 @@ void BinaryWriter::writeInt16(atInt16 val) if (m_position + sizeof(atInt16) > m_length) resize(m_position + sizeof(atInt16)); - if ((!utility::isSystemBigEndian() && isBigEndian()) || (utility::isSystemBigEndian() && isLittleEndian())) - val = utility::swap16(val); + if (isBigEndian()) + utility::BigInt16(val); + else + utility::LittleInt16(val); *(atInt16*)(m_data + m_position) = val; m_position += sizeof(atInt16); @@ -331,8 +333,10 @@ void BinaryWriter::writeInt32(atInt32 val) if (m_position + sizeof(atInt32) > m_length) resize(m_position + sizeof(atInt32)); - if ((!utility::isSystemBigEndian() && isBigEndian()) || (utility::isSystemBigEndian() && isLittleEndian())) - val = utility::swap32(val); + if (isBigEndian()) + utility::BigInt32(val); + else + utility::LittleInt32(val); *(atInt32*)(m_data + m_position) = val; m_position += sizeof(atInt32); @@ -358,8 +362,10 @@ void BinaryWriter::writeInt64(atInt64 val) resize(m_position + sizeof(atInt64)); - if ((!utility::isSystemBigEndian() && isBigEndian()) || (utility::isSystemBigEndian() && isLittleEndian())) - val = utility::swap64(val); + if (isBigEndian()) + utility::BigInt64(val); + else + utility::LittleInt64(val); *(atInt64*)(m_data + m_position) = val; m_position += sizeof(atInt64); @@ -376,8 +382,11 @@ void BinaryWriter::writeUint64(atUint64 val) if (m_position + sizeof(atUint64) > m_length) resize(m_position + sizeof(atUint64)); - if ((!utility::isSystemBigEndian() && isBigEndian()) || (utility::isSystemBigEndian() && isLittleEndian())) - val = utility::swapU64(val); + if (isBigEndian()) + utility::BigUint64(val); + else + utility::LittleUint64(val); + *(atUint64*)(m_data + m_position) = val; m_position += sizeof(atUint64); @@ -394,8 +403,11 @@ void BinaryWriter::writeFloat(float val) if (m_position + sizeof(float) > m_length) resize(m_position + sizeof(float)); - if ((!utility::isSystemBigEndian() && isBigEndian()) || (utility::isSystemBigEndian() && isLittleEndian())) - val = utility::swapFloat(val); + if (isBigEndian()) + utility::BigFloat(val); + else + utility::LittleFloat(val); + *(float*)(m_data + m_position) = val; m_position += sizeof(float); @@ -412,8 +424,10 @@ void BinaryWriter::writeDouble(double val) if (m_position + sizeof(double) > m_length) resize(m_position + sizeof(double)); - if ((!utility::isSystemBigEndian() && isBigEndian()) || (utility::isSystemBigEndian() && isLittleEndian())) - val = utility::swapDouble(val); + if (isBigEndian()) + utility::BigDouble(val); + else + utility::LittleDouble(val); *(double*)(m_data + m_position)= val; m_position += sizeof(double); diff --git a/src/Athena/MCFile.cpp b/src/Athena/MCFile.cpp index 35f873f..88ecf77 100644 --- a/src/Athena/MCFile.cpp +++ b/src/Athena/MCFile.cpp @@ -22,4 +22,36 @@ MCFile::MCFile() { } +// TODO: Rewrite this to be more optimized, the current solution takes quite a few cycles +atUint8* reverse(atUint8* data, atUint32 length) +{ + atUint32 a = 0; + atUint32 swap; + + for (;a<--length; a++) + { + swap = data[a]; + data[a] = data[length]; + data[length] = swap; + } + + return data; +} + +atUint8* MCFile::unscramble(atUint8* data, atUint32 length) +{ + if (!data) + return nullptr; + + for (atUint32 i = 0; i < length; i += 8) + { + atUint32 block1 = *(atUint32*)reverse((data + i), 4); + atUint32 block2 = *(atUint32*)reverse((data + i + 4), 4); + *(atUint32*)(data + i) = block2; + *(atUint32*)(data + i + 4) = block1; + } + + return data; +} + } // zelda diff --git a/src/Athena/MCFileReader.cpp b/src/Athena/MCFileReader.cpp index 59903b9..a592df0 100644 --- a/src/Athena/MCFileReader.cpp +++ b/src/Athena/MCFileReader.cpp @@ -15,13 +15,14 @@ // along with libAthena. If not, see #include "Athena/MCFileReader.hpp" - +#include "Athena/MCFile.hpp" namespace Athena { namespace io { +static const atUint32 SCRAMBLE_VALUE = 0x5A424741; MCFileReader::MCFileReader(atUint8* data, atUint64 length) : base(data, length) { @@ -32,6 +33,16 @@ MCFileReader::MCFileReader(const std::string& filename) { } +MCFile* MCFileReader::readFile() +{ + bool isScrambled = base::readUint32() != SCRAMBLE_VALUE; + base::m_position = 0; + + if (isScrambled) + MCFile::unscramble(base::m_data, base::m_length); +} + + } // io } // zelda #endif // ATHENA_NO_SAVES diff --git a/src/Athena/MCFileWriter.cpp b/src/Athena/MCFileWriter.cpp index 2e35f1a..d516283 100644 --- a/src/Athena/MCFileWriter.cpp +++ b/src/Athena/MCFileWriter.cpp @@ -64,38 +64,6 @@ atUint16 MCFileWriter::calculateChecksum(atUint8 *data, atUint32 length) return sum; } -// TODO: Rewrite this to be more optimized, the current solution takes quite a few cycles -atUint8* MCFileWriter::reverse(atUint8* data, atUint32 length) -{ - atUint32 a = 0; - atUint32 swap; - - for (;a<--length; a++) - { - swap = data[a]; - data[a] = data[length]; - data[length] = swap; - } - - return data; -} - - -// TODO: Rewrite this to be more optimized, unroll more?? -void MCFileWriter::unscramble() -{ - if (!m_data) - return; - - for (atUint32 i = 0; i < m_length; i += 4) - { - atUint32 block1 = *(atUint32*)reverse((m_data + i), 4); - atUint32 block2 = *(atUint32*)reverse((m_data + i + 4), 4); - *(atUint32*)(m_data + i) = block2; - *(atUint32*)(m_data + i + 4) = block1; - } -} - } // io } // zelda #endif // ATHENA_NO_SAVES diff --git a/src/Athena/MCSlot.cpp b/src/Athena/MCSlot.cpp new file mode 100644 index 0000000..2a429e3 --- /dev/null +++ b/src/Athena/MCSlot.cpp @@ -0,0 +1,11 @@ +#include "MCSlot.hpp" + +namespace Athena +{ + +MCSlot::MCSlot(atUint8* data, atUint32 length) + : ZQuestFile(ZQuestFile::MC, Endian::LittleEndian, data, length) +{ +} + +} // Athena diff --git a/src/Athena/MCSlot.hpp b/src/Athena/MCSlot.hpp new file mode 100644 index 0000000..bf6f089 --- /dev/null +++ b/src/Athena/MCSlot.hpp @@ -0,0 +1,17 @@ +#ifndef MCSLOT_HPP +#define MCSLOT_HPP + +#include "Athena/Global.hpp" +#include "Athena/ZQuestFile.hpp" + +namespace Athena +{ +class MCSlot : public ZQuestFile +{ +public: + MCSlot(atUint8* data, atUint32 length); +}; + +} // Athena + +#endif // MCSLOT_HPP diff --git a/src/Athena/SkywardSwordFileWriter.cpp b/src/Athena/SkywardSwordFileWriter.cpp index faee571..f6fccb7 100644 --- a/src/Athena/SkywardSwordFileWriter.cpp +++ b/src/Athena/SkywardSwordFileWriter.cpp @@ -55,14 +55,14 @@ void SkywardSwordFileWriter::write(SkywardSwordFile *file) { if (q->length() != 0x53C0) THROW_INVALID_DATA_EXCEPTION("q->data() not 0x53C0 bytes in length"); - if (q->skipLength() != 0x24) - THROW_INVALID_DATA_EXCEPTION("q->skipData() not 0x24 bytes in length"); + // Update the checksums + q->fixChecksums(); // Write the save data base::writeUBytes(q->data(), q->length()); atUint64 pos = base::position(); // Write the slots skip data base::seek(0xFB60 + (i * 0x24), SeekOrigin::Begin); - base::writeUBytes(q->skipData(), q->skipLength()); + base::writeUBytes(q->skipData(), 0x24); base::seek(pos, SeekOrigin::Begin); i++; } diff --git a/src/Athena/SkywardSwordQuest.cpp b/src/Athena/SkywardSwordQuest.cpp index 3d7f826..68dac12 100644 --- a/src/Athena/SkywardSwordQuest.cpp +++ b/src/Athena/SkywardSwordQuest.cpp @@ -15,17 +15,50 @@ // along with libAthena. If not, see #include "Athena/SkywardSwordQuest.hpp" - +#include "Athena/Checksums.hpp" +#include +#include "utf8.h" namespace Athena { + +namespace priv +{ +static const atUint32 NAME_OFFSET = 0x08D4; +static const atUint32 RUPEE_COUNT_OFFSET = 0x0A5E; +static const atUint32 AMMO_COUNT_OFFSET = 0x0A60; +static const atUint32 MAX_HP_OFFSET = 0x5302; +static const atUint32 SPAWN_HP_OFFSET = 0x5304; +static const atUint32 CURRENT_HP_OFFSET = 0x5306; +static const atUint32 ROOM_ID_OFFSET = 0x5309; +static const atUint32 CURRENT_LOCATION_OFFSET = 0x531C; +static const atUint32 CURRENT_AREA_OFFSET = 0x533C; +static const atUint32 CURRENT_LOCATION_COPY_OFFSET = 0x535C; +static const atUint32 CHECKSUM_OFFSET = 0x53BC; +static const atUint32 ISNEW_OFFSET = 0x53AD; + +static const atUint32 SKIP_CHECKSUM_OFFSET = 0x20; +} + +union AmmoValues +{ + struct + { + atUint32 arrows : 7; + atUint32 bombs : 7; + atUint32 : 9; + atUint32 seeds : 7; + atUint32 : 2; + }; + atUint32 value; +}; + SkywardSwordQuest::SkywardSwordQuest(atUint8 *data, atUint32 len) : ZQuestFile(ZQuestFile::SS, Endian::BigEndian, data, len), - m_skipData(nullptr), - m_skipLength(0) + m_skipData(nullptr) { } -void SkywardSwordQuest::setSkipData(const atUint8 *data, atUint32 len) +void SkywardSwordQuest::setSkipData(const atUint8 *data) { if (m_skipData) { @@ -34,7 +67,6 @@ void SkywardSwordQuest::setSkipData(const atUint8 *data, atUint32 len) } m_skipData = (atUint8*)data; - m_skipLength = len; } atUint8 *SkywardSwordQuest::skipData() const @@ -42,9 +74,192 @@ atUint8 *SkywardSwordQuest::skipData() const return m_skipData; } -atUint32 SkywardSwordQuest::skipLength() const +void SkywardSwordQuest::setPlayerName(const std::string& name) { - return m_skipLength; + if (name.length() > 8) + aDebug() << "WARNING: name cannot be greater than 8 characters, automatically truncating" << std::endl; + + std::vector val; + utf8::utf8to16(name.begin(), name.end(), std::back_inserter(val)); + + for (atUint32 i = 0; i < 8; i++) + { + atUint16& c = *(atUint16*)(m_data + priv::NAME_OFFSET + (i * 2)); + if (i >= val.size()) + { + c = 0; + continue; + } + + c = val[i]; + utility::BigUint16(c); + } +} + +std::string SkywardSwordQuest::playerName() const +{ + std::vector val; + for (atUint32 i = 0; i < 8; i++) + { + atUint16 c = *(atUint16*)(m_data + priv::NAME_OFFSET + (i * 2)); + if (c == 0) + break; + + utility::BigUint16(c); + val.push_back(c); + } + + std::string ret; + utf8::utf16to8(val.begin(), val.end(), std::back_inserter(ret)); + return std::string(ret.c_str()); +} + +void SkywardSwordQuest::setRupeeCount(atUint16 value) +{ + atUint16& tmp = *(atUint16*)(m_data + priv::RUPEE_COUNT_OFFSET); + tmp = value; + utility::BigUint16(tmp); +} + +atUint16 SkywardSwordQuest::rupeeCount() +{ + atUint16 ret = *(atUint16*)(m_data + priv::RUPEE_COUNT_OFFSET); + return utility::BigUint16(ret); +} + +void SkywardSwordQuest::setAmmoCount(SkywardSwordQuest::AmmoType type, atUint32 count) +{ + AmmoValues& values = *(AmmoValues*)(m_data + priv::AMMO_COUNT_OFFSET); + utility::BigUint32(values.value); + + switch(type) + { + case Arrows: + values.arrows = count; + break; + case Bombs: + values.bombs = count; + break; + case Seeds: + values.seeds = count; + break; + } + + utility::BigUint32(values.value); +} + +atUint32 SkywardSwordQuest::ammoCount(AmmoType type) +{ + AmmoValues values = *(AmmoValues*)(m_data + priv::AMMO_COUNT_OFFSET); + utility::BigUint32(values.value); + + switch(type) + { + case Arrows: return values.arrows; + case Bombs: return values.bombs; + case Seeds: return values.seeds; + default: return 0; + } +} + +void SkywardSwordQuest::setMaxHP(atUint16 val) +{ + *(atUint16*)(m_data + priv::MAX_HP_OFFSET) = utility::BigUint16(val); +} + +atUint16 SkywardSwordQuest::maxHP() +{ + atUint16 ret = *(atUint16*)(m_data + priv::MAX_HP_OFFSET); + return utility::BigUint16(ret); +} + +float SkywardSwordQuest::maxHearts() +{ + return (maxHP() / 4.f); +} + +void SkywardSwordQuest::setSpawnHP(atUint16 val) +{ + *(atUint16*)(m_data + priv::SPAWN_HP_OFFSET) = utility::BigUint16(val); +} + +atUint16 SkywardSwordQuest::spawnHP() +{ + atUint16 ret = *(atUint16*)(m_data + priv::SPAWN_HP_OFFSET); + return utility::BigUint16(ret); +} + +float SkywardSwordQuest::spawnHearts() +{ + return (spawnHP() / 4.f); +} + +void SkywardSwordQuest::setCurrentHP(atUint16 val) +{ + *(atUint16*)(m_data + priv::CURRENT_HP_OFFSET) = utility::BigUint16(val); +} + +atUint16 SkywardSwordQuest::currentHP() +{ + atUint16 ret = *(atUint16*)(m_data + priv::CURRENT_HP_OFFSET); + return utility::BigUint16(ret); +} + +float SkywardSwordQuest::currentHearts() +{ + return (currentHP() / 4.f); +} + +std::string SkywardSwordQuest::currentLocation() +{ + return std::string((char*)m_data + priv::CURRENT_LOCATION_OFFSET); +} + +std::string SkywardSwordQuest::currentArea() +{ + return std::string((char*)m_data + priv::CURRENT_AREA_OFFSET); +} + +std::string SkywardSwordQuest::currentLocationCopy() +{ + return std::string((char*)m_data + priv::CURRENT_LOCATION_COPY_OFFSET); +} + +atUint32 SkywardSwordQuest::slotChecksum() +{ + atUint32 ret = *(atUint32*)(m_data + priv::CHECKSUM_OFFSET); + utility::BigUint32(ret); + + return ret; +} + +atUint32 SkywardSwordQuest::skipChecksum() +{ + atUint32 ret = *(atUint32*)(m_skipData + priv::SKIP_CHECKSUM_OFFSET); + utility::BigUint32(ret); + + return ret; +} + +void SkywardSwordQuest::fixChecksums() +{ + atUint32 checksum = Checksums::crc32(m_data, priv::CHECKSUM_OFFSET); + utility::BigUint32(checksum); + *(atUint32*)(m_data + priv::CHECKSUM_OFFSET) = checksum; + + checksum = Checksums::crc32(m_skipData, priv::SKIP_CHECKSUM_OFFSET); + utility::BigUint32(checksum); + *(atUint32*)(m_skipData + priv::SKIP_CHECKSUM_OFFSET) = checksum; +} + +void SkywardSwordQuest::setNew(bool isNew) +{ + *(bool*)(m_data + priv::ISNEW_OFFSET) = isNew; +} + +bool SkywardSwordQuest::isNew() const +{ + return *(bool*)(m_data + priv::ISNEW_OFFSET); } } // zelda diff --git a/src/Athena/Utility.cpp b/src/Athena/Utility.cpp index 8c3013b..516a2be 100644 --- a/src/Athena/Utility.cpp +++ b/src/Athena/Utility.cpp @@ -374,5 +374,22 @@ atUint64 fileSize(FILE* f) return size; } +std::string& ltrim(std::string& s) +{ + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(std::isspace)))); + return s; +} + +std::string& rtrim(std::string& s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(std::isspace))).base(), s.end()); + return s; +} + +std::string& trim(std::string& s) +{ + return ltrim(rtrim(s)); +} + } // utility } // Athena diff --git a/src/Athena/WiiSave.cpp b/src/Athena/WiiSave.cpp index f0a647f..3daed4b 100644 --- a/src/Athena/WiiSave.cpp +++ b/src/Athena/WiiSave.cpp @@ -51,7 +51,7 @@ WiiSave::~WiiSave() } -void WiiSave::addFile(const std::string& filepath, WiiFile* file) +void WiiSave::addFile( WiiFile* file) { m_root->addChild(file); }