mirror of https://github.com/libAthena/athena.git
* Start to fill in save related classes
* Fix silly bug in WiiSave * Fix typos in Utilitiy.*
This commit is contained in:
parent
fea9a45a5c
commit
be135d1caa
|
@ -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
|
||||
|
|
|
@ -641,28 +641,28 @@ private:
|
|||
std::vector<ALTTPRoomFlags*> m_roomFlags;
|
||||
std::vector<ALTTPOverworldEvent*> 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<atUint8> m_dungeonKeys;
|
||||
std::vector<atUint8> 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<atUint8> m_oldManFlags;
|
||||
atUint8 m_bombFlag;
|
||||
std::vector<atUint8> m_unknown1;
|
||||
std::vector<atUint16> m_playerName;
|
||||
atUint16 m_valid;
|
||||
std::vector<atUint16> m_dungeonDeathTotals;
|
||||
atUint16 m_unknown2;
|
||||
atUint16 m_deathSaveCount;
|
||||
atInt16 m_postGameDeathCounter;
|
||||
atUint16 m_checksum;
|
||||
std::vector<atUint8> m_oldManFlags;
|
||||
atUint8 m_bombFlag;
|
||||
std::vector<atUint8> m_unknown1;
|
||||
std::vector<atUint16> m_playerName;
|
||||
atUint16 m_valid;
|
||||
std::vector<atUint16> m_dungeonDeathTotals;
|
||||
atUint16 m_unknown2;
|
||||
atUint16 m_deathSaveCount;
|
||||
atInt16 m_postGameDeathCounter;
|
||||
atUint16 m_checksum;
|
||||
};
|
||||
|
||||
} // zelda
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
# elif defined(__func__)
|
||||
# define __PRETTY_FUNCTION__ __func__
|
||||
# else
|
||||
# define __PRETTY_FUNCTION__ "<unkown>"
|
||||
# define __PRETTY_FUNCTION__ "<unknown>"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -53,6 +53,7 @@ public:
|
|||
|
||||
void setRegion(Region region);
|
||||
Region region() const;
|
||||
|
||||
private:
|
||||
Region m_region;
|
||||
// A vector is a bit overkill
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -152,12 +152,12 @@ public:
|
|||
std::string gameString() const;
|
||||
|
||||
static const std::vector<std::string> 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
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "Athena/ALTTPFile.hpp"
|
||||
#include "Athena/ALTTPQuest.hpp"
|
||||
#include <iostream>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
|
@ -542,12 +542,9 @@ std::vector<atUint16> ALTTPQuest::playerName() const
|
|||
std::string ALTTPQuest::playerNameToString() const
|
||||
{
|
||||
std::string ret;
|
||||
std::vector<atUint16>::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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -15,13 +15,14 @@
|
|||
// along with libAthena. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
#include "MCSlot.hpp"
|
||||
|
||||
namespace Athena
|
||||
{
|
||||
|
||||
MCSlot::MCSlot(atUint8* data, atUint32 length)
|
||||
: ZQuestFile(ZQuestFile::MC, Endian::LittleEndian, data, length)
|
||||
{
|
||||
}
|
||||
|
||||
} // Athena
|
|
@ -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
|
|
@ -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++;
|
||||
}
|
||||
|
|
|
@ -15,17 +15,50 @@
|
|||
// along with libAthena. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "Athena/SkywardSwordQuest.hpp"
|
||||
|
||||
#include "Athena/Checksums.hpp"
|
||||
#include <sstream>
|
||||
#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<atUint16> 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<atUint16> 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
|
||||
|
|
|
@ -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<int, int>(std::isspace))));
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& rtrim(std::string& s)
|
||||
{
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string& trim(std::string& s)
|
||||
{
|
||||
return ltrim(rtrim(s));
|
||||
}
|
||||
|
||||
} // utility
|
||||
} // Athena
|
||||
|
|
|
@ -51,7 +51,7 @@ WiiSave::~WiiSave()
|
|||
|
||||
}
|
||||
|
||||
void WiiSave::addFile(const std::string& filepath, WiiFile* file)
|
||||
void WiiSave::addFile( WiiFile* file)
|
||||
{
|
||||
m_root->addChild(file);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue