* Start to fill in save related classes

* Fix silly bug in WiiSave
* Fix typos in Utilitiy.*
This commit is contained in:
Phillip Stephens 2014-11-28 19:32:37 -08:00
parent fea9a45a5c
commit be135d1caa
25 changed files with 476 additions and 122 deletions

View File

@ -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

View File

@ -105,6 +105,10 @@ struct ALTTPLightDarkWorldIndicator
struct ALTTPDungeonItemFlags
{
union
{
struct
{
bool Unused1:1;
bool Unused2:1;
@ -114,14 +118,25 @@ struct ALTTPDungeonItemFlags
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; // Doesn't exists in orignal game
bool HyruleCastle2:1; // unused 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
bool HyruleCastle:1; // unused exist in original game
bool SewerPassage:1; // unused exist in original game
};
atUint8 flags2;
};
};
struct ALTTPPendants

View File

@ -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);

View File

@ -30,7 +30,7 @@
# elif defined(__func__)
# define __PRETTY_FUNCTION__ __func__
# else
# define __PRETTY_FUNCTION__ "<unkown>"
# define __PRETTY_FUNCTION__ "<unknown>"
# endif
#endif

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -53,6 +53,7 @@ public:
void setRegion(Region region);
Region region() const;
private:
Region m_region;
// A vector is a bit overkill

View File

@ -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;
};

View File

@ -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 &ltrim(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

View File

@ -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

View File

@ -152,7 +152,7 @@ public:
std::string gameString() const;
static const std::vector<std::string> gameStringList();
private:
protected:
Game m_game;
std::string m_gameString;
Endian m_endian;

View File

@ -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;
}

View File

@ -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));

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

11
src/Athena/MCSlot.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "MCSlot.hpp"
namespace Athena
{
MCSlot::MCSlot(atUint8* data, atUint32 length)
: ZQuestFile(ZQuestFile::MC, Endian::LittleEndian, data, length)
{
}
} // Athena

17
src/Athena/MCSlot.hpp Normal file
View File

@ -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

View File

@ -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++;
}

View File

@ -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

View File

@ -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

View File

@ -51,7 +51,7 @@ WiiSave::~WiiSave()
}
void WiiSave::addFile(const std::string& filepath, WiiFile* file)
void WiiSave::addFile( WiiFile* file)
{
m_root->addChild(file);
}