* Added Sakura Sprite

* Renamed SSFile* and SSQuest to SkywardSword*
This commit is contained in:
Antidote 2014-01-25 20:56:04 -08:00
parent aa5c37856f
commit 3afe76c70e
22 changed files with 1398 additions and 78 deletions

View File

@ -1,24 +0,0 @@
#ifndef __SSFILEREADER_HPP__
#define __SSFILEREADER_HPP__
#include "BinaryReader.hpp"
namespace zelda
{
class SSFile;
namespace io
{
class SSFileReader : public BinaryReader
{
BINARYREADER_BASE
public:
SSFileReader(Uint8* data, Uint64 length);
SSFileReader(const std::string& filename);
SSFile* read();
};
} // io
} // zelda
#endif // __SSFILEREADER_HPP__

View File

@ -8,9 +8,9 @@
namespace zelda namespace zelda
{ {
class SSQuest; class SkywardSwordQuest;
class SSFile class SkywardSwordFile
{ {
public: public:
enum MagicNumbers enum MagicNumbers
@ -20,20 +20,20 @@ public:
EUMagic = 0x534F5550 EUMagic = 0x534F5550
}; };
SSFile(); SkywardSwordFile();
SSFile(std::vector<SSQuest*> quests); SkywardSwordFile(std::vector<SkywardSwordQuest*> quests);
~SSFile(); ~SkywardSwordFile();
void addQuest(SSQuest* q); void addQuest(SkywardSwordQuest* q);
SSQuest* quest(Uint32 id); SkywardSwordQuest* quest(Uint32 id);
std::vector<SSQuest*> questList() const; std::vector<SkywardSwordQuest*> questList() const;
void setRegion(Region region); void setRegion(Region region);
Region region() const; Region region() const;
private: private:
Region m_region; Region m_region;
// A vector is a bit overkill // A vector is a bit overkill
std::vector<SSQuest*> m_quests; std::vector<SkywardSwordQuest*> m_quests;
Uint32 m_numQuests; Uint32 m_numQuests;
}; };

View File

@ -0,0 +1,24 @@
#ifndef __SSFILEREADER_HPP__
#define __SSFILEREADER_HPP__
#include "BinaryReader.hpp"
namespace zelda
{
class SkywardSwordFile;
namespace io
{
class SkywardSwordFileReader : public BinaryReader
{
BINARYREADER_BASE
public:
SkywardSwordFileReader(Uint8* data, Uint64 length);
SkywardSwordFileReader(const std::string& filename);
SkywardSwordFile* read();
};
} // io
} // zelda
#endif // __SSFILEREADER_HPP__

View File

@ -5,20 +5,20 @@
namespace zelda namespace zelda
{ {
class SSFile; class SkywardSwordFile;
namespace io namespace io
{ {
class SSFileWriter : public BinaryWriter class SkywardSwordFileWriter : public BinaryWriter
{ {
// Why does this fuck up my formatting in Qt Creator? // Why does this fuck up my formatting in Qt Creator?
BINARYWRITER_BASE BINARYWRITER_BASE
public: public:
SSFileWriter(Uint8* data, Uint64 len); SkywardSwordFileWriter(Uint8* data, Uint64 len);
SSFileWriter(const std::string& filename); SkywardSwordFileWriter(const std::string& filename);
void write(SSFile* file); void write(SkywardSwordFile* file);
}; };
} }
} }

View File

@ -1,5 +1,5 @@
#ifndef SSQUEST_HPP #ifndef SKYWARDSWORDQUEST_HPP
#define SSQUEST_HPP #define SKYWARDSWORDQUEST_HPP
#include "ZQuestFile.hpp" #include "ZQuestFile.hpp"
@ -7,10 +7,10 @@ namespace zelda
{ {
// TODO: Handle game specific data // TODO: Handle game specific data
class SSQuest : public ZQuestFile class SkywardSwordQuest : public ZQuestFile
{ {
public: public:
SSQuest(Uint8* data, Uint32 len); SkywardSwordQuest(Uint8* data, Uint32 len);
// TODO: Is len really needed? // TODO: Is len really needed?
void setSkipData(const Uint8* data, Uint32 len = 0x24); void setSkipData(const Uint8* data, Uint32 len = 0x24);

64
include/Sprite.hpp Normal file
View File

@ -0,0 +1,64 @@
#ifndef SSPRITE_HPP
#define SSPRITE_HPP
#include <unordered_map>
#include <vector>
#include <string>
#include <Types.hpp>
namespace zelda
{
namespace Sakura
{
class SpriteFile;
class SpritePart;
class Sprite
{
public:
Sprite(SpriteFile* root);
Sprite(SpriteFile* root, const std::string& name);
virtual ~Sprite();
virtual void setPosition(const float x, const float y);
virtual void setPosition(const Vector2Df& pos);
Vector2Df position() const;
void setName(const std::string& name);
std::string name() const;
void addStateId(int id);
/*!
* \brief Returns the texture id of a given state
* \param index The index of the id.
* \return return the state id if it exists, -1 otherwise
*/
int stateId(int index) const;
void setStateIds(std::vector<int> ids);
std::vector<int> stateIds() const;
Uint32 stateCount() const;
void setCurrentState(const Uint32 id);
void addPart(SpritePart* part);
SpritePart* part(const std::string& name);
void setParts(std::vector<SpritePart*> parts);
Uint32 partCount() const;
std::vector<SpritePart*> parts() const;
SpriteFile* container() const;
private:
SpriteFile* m_root;
std::string m_name;
Vector2Df m_position;
std::vector<int> m_stateIds; //!< Stores the texture id's for each state.
std::vector<SpritePart*> m_parts;
Uint32 m_currentState;
};
} // Sakura
} // zelda
#endif // SSPRITE_HPP

181
include/SpriteFile.hpp Normal file
View File

@ -0,0 +1,181 @@
#ifndef SSPRITEFILE_HPP
#define SSPRITEFILE_HPP
#include <vector>
#include <unordered_map>
#include <string>
#include <Types.hpp>
namespace zelda
{
namespace Sakura
{
struct STexture
{
std::string Filepath;
bool Preload;
};
class Sprite;
class SpriteFile
{
public:
/*!
* \brief Major
*/
static const Uint32 Major;
/*!
* \brief Minor
*/
static const Uint32 Minor;
/*!
* \brief Revision
*/
static const Uint32 Revision;
/*!
* \brief Patch
*/
static const Uint32 Build;
/*!
* \brief Version
*/
static const Uint32 Version;
/*!
* \brief Magic
*/
static const Uint32 Magic;
/*!
* \brief SSprite
*/
SpriteFile();
/*!
* \brief SSpriteFile
* \param width
* \param height
* \param originX
* \param originY
*/
SpriteFile(Uint32 width, Uint32 height, float originX, float originY);
/*!
* \brief SSpriteFile
* \param size
* \param origin
*/
SpriteFile(const Vector2Di& size, const Vector2Df& origin);
/*!
* \brief setSize
* \param width
* \param height
*/
void setSize(Uint32 width, Uint32 height);
/*!
* \brief setSize
* \param size
*/
void setSize(const Vector2Di& size);
/*!
* \brief size
* \return
*/
Vector2Di size() const;
/*!
* \brief width
* \return
*/
Uint32 width() const;
/*!
* \brief height
* \return
*/
Uint32 height() const;
/*!
* \brief setOrigin
* \param x
* \param y
*/
void setOrigin(const float x, const float y);
/*!
* \brief setOrigin
* \param origin
*/
void setOrigin(const Vector2Df& origin);
/*!
* \brief origin
* \return
*/
Vector2Df origin() const;
/*!
* \brief originX
* \return
*/
float originX() const;
/*!
* \brief originY
* \return
*/
float originY() const;
/*!
* \brief addTexture
* \param texture
*/
void addTexture(STexture* texture);
/*!
* \brief removeTexture
* \param id
*/
void removeTexture(int id);
/*!
* \brief texture
* \param id
* \return
*/
STexture* texture(Uint32 id);
std::vector<STexture*> textures() const;
Uint32 textureCount() const;
/*!
* \brief setTextures
* \param textures
*/
void setTextures(std::vector<STexture*> textures);
void addSprite(Sprite* sprite);
void setSprites(std::unordered_map<std::string, Sprite*> sprites);
Sprite* sprite(const std::string& name);
std::unordered_map<std::string, Sprite*> sprites() const;
Uint32 spriteCount() const;
private:
std::vector<STexture*> m_textures;
Vector2Di m_size;
Vector2Df m_origin;
std::unordered_map<std::string, Sprite*> m_sprites;
};
} // Sakura
} // Zelda
#endif // SSPRITE_HPP

View File

@ -0,0 +1,29 @@
#ifndef SSPRITEFILEREADER_HPP
#define SSPRITEFILEREADER_HPP
#include <BinaryReader.hpp>
namespace zelda
{
namespace Sakura
{
class SpriteFile;
} // Sakura
namespace io
{
class SpriteFileReader : public zelda::io::BinaryReader
{
BINARYREADER_BASE;
public:
SpriteFileReader(Uint8* data, Uint64 length);
SpriteFileReader(const std::string& filepath);
Sakura::SpriteFile* readFile();
};
} // io
} // zelda
#endif // SSPRITEFILEREADER_HPP

View File

@ -0,0 +1,29 @@
#ifndef SSPRITEFILEWRITER_HPP
#define SSPRITEFILEWRITER_HPP
#include <BinaryWriter.hpp>
namespace zelda
{
namespace Sakura
{
class SpriteFile;
} // Sakura
namespace io
{
class SpriteFileWriter : public zelda::io::BinaryWriter
{
BINARYWRITER_BASE;
public:
SpriteFileWriter(Uint8* data, Uint64 length);
SpriteFileWriter(const std::string& filepath);
void writeFile(Sakura::SpriteFile* file);
};
} // io
} // zelda
#endif // SSPRITEFILEWRITER_HPP

152
include/SpriteFrame.hpp Normal file
View File

@ -0,0 +1,152 @@
#ifndef SSPRITEFRAME_HPP
#define SSPRITEFRAME_HPP
#include <Types.hpp>
#include <vector>
namespace zelda
{
namespace Sakura
{
class SpriteFrame
{
public:
/*!
* \brief SSpriteFrame
*/
SpriteFrame();
/*!
* \brief SSpriteFrame
* \param offX
* \param offY
* \param texX
* \param texY
* \param width
* \param height
* \param frameTime
* \param flippedH
* \param flippedV
*/
SpriteFrame(float offX, float offY, float texX, float texY, Uint32 width, Uint32 height, float frameTime, bool flippedH = false, bool flippedV = false);
/*!
* \brief SSpriteFrame
* \param frameOff
* \param texOff
* \param size
* \param frameTime
* \param flippedH
* \param flippedV
*/
SpriteFrame(const Vector2Df& frameOff, const Vector2Df& texOff, const Vector2Di& size, float frameTime, bool flippedH = false, bool flippedV = false);
/*!
* \brief setOffset
* \param x
* \param y
*/
void setOffset(float x, float y);
/*!
* \brief setOffset
* \param offset
*/
void setOffset(const Vector2Df& offset);
/*!
* \brief offset
* \return
*/
Vector2Df offset() const;
/*!
* \brief setTextureOffset
* \param x
* \param y
*/
void setTextureOffset(float x, float y);
/*!
* \brief setTextureOffset
* \param texOff
*/
void setTextureOffset(const Vector2Df& texOff);
/*!
* \brief textureOffset
* \return
*/
Vector2Df textureOffset() const;
/*!
* \brief setSize
* \param width
* \param height
*/
void setSize(Uint32 width, Uint32 height);
/*!
* \brief setSize
* \param size
*/
void setSize(const Vector2Di& size);
/*!
* \brief size
* \return
*/
Vector2Di size() const;
/*!
* \brief setFlippedHorizontally
* \param val
*/
void setFlippedHorizontally(const bool val);
/*!
* \brief flippedHorizontally
* \return
*/
bool flippedHorizontally() const;
/*!
* \brief setFlippedVertically
* \param val
*/
void setFlippedVertically(const bool val);
/*!
* \brief flippedVertically
* \return
*/
bool flippedVertically() const;
/*!
* \brief setFrameTime
* \param frameTime
*/
void setFrameTime(float frameTime);
/*!
* \brief frameTime
* \return
*/
float frameTime() const;
private:
Vector2Df m_offset;
Vector2Df m_textureOffset;
Vector2Di m_size;
float m_frameTime;
bool m_flippedH;
bool m_flippedV;
};
} // Sakura
} // zelda
#endif // SSpRITEFRAME_HPP

56
include/SpritePart.hpp Normal file
View File

@ -0,0 +1,56 @@
#ifndef SSPRITEPART_HPP
#define SSPRITEPART_HPP
#include <Types.hpp>
#include <vector>
#include <string>
#include <Types.hpp>
namespace zelda
{
namespace Sakura
{
class Sprite;
class SpriteFrame;
class SpritePart
{
public:
SpritePart(Sprite* root);
SpritePart(Sprite* root, const std::string& name, bool hasCollision = false);
virtual ~SpritePart();
void setName(const std::string& name);
std::string name() const;
void setCollision(bool col);
bool hasCollision() const;
void addFrame(SpriteFrame* frame);
void advanceFrame();
void retreatFrame();
SpriteFrame* frame(int id);
void setFrames(std::vector<SpriteFrame*> frames);
SpriteFrame* currentFrame();
int currentFrameID();
std::vector<SpriteFrame*> frames() const;
Uint32 frameCount() const;
void setRoot(Sprite* root);
private:
void updateTexture();
Sprite* m_root;
std::string m_name;
bool m_hasCollision;
SpriteFrame* m_currentFrame;
Uint32 m_frameIndex;
// The collection of frames for this part
std::vector<SpriteFrame*> m_frames;
};
}
}
#endif // SSpRITEPART_HPP

View File

@ -37,6 +37,33 @@ enum Region
NTSCJRegion, NTSCJRegion,
PALRegion PALRegion
}; };
namespace Sakura
{
template <typename T>
class Vector2D
{
public:
T x;
T y;
Vector2D()
: x(0),
y(0)
{
}
Vector2D(T x, T y)
: x(x),
y(y)
{
}
};
typedef Vector2D<int> Vector2Di;
typedef Vector2D<float> Vector2Df;
} // Sakura
} // zelda } // zelda
#endif #endif

View File

@ -1,26 +1,26 @@
#include "SSFile.hpp" #include "SkywardSwordFile.hpp"
#include "SSQuest.hpp" #include "SkywardSwordQuest.hpp"
#include "InvalidOperationException.hpp" #include "InvalidOperationException.hpp"
namespace zelda namespace zelda
{ {
SSFile::SSFile() SkywardSwordFile::SkywardSwordFile()
: m_numQuests(0) : m_numQuests(0)
{ {
} }
SSFile::SSFile(std::vector<SSQuest*> quests) SkywardSwordFile::SkywardSwordFile(std::vector<SkywardSwordQuest*> quests)
: m_numQuests(0) : m_numQuests(0)
{ {
m_quests = quests; m_quests = quests;
} }
SSFile::~SSFile() SkywardSwordFile::~SkywardSwordFile()
{ {
} }
void SSFile::addQuest(zelda::SSQuest *q) void SkywardSwordFile::addQuest(zelda::SkywardSwordQuest *q)
{ {
// Do not allow more than 3 quests // Do not allow more than 3 quests
if (m_quests.size() >= 3) if (m_quests.size() >= 3)
@ -29,7 +29,7 @@ void SSFile::addQuest(zelda::SSQuest *q)
m_quests.push_back(q); m_quests.push_back(q);
} }
SSQuest *SSFile::quest(Uint32 id) SkywardSwordQuest *SkywardSwordFile::quest(Uint32 id)
{ {
if (id > m_quests.size() - 1) if (id > m_quests.size() - 1)
throw zelda::error::InvalidOperationException("SSFile::quest -> id cannot be " throw zelda::error::InvalidOperationException("SSFile::quest -> id cannot be "
@ -38,17 +38,17 @@ SSQuest *SSFile::quest(Uint32 id)
return m_quests[id]; return m_quests[id];
} }
std::vector<SSQuest*> SSFile::questList() const std::vector<SkywardSwordQuest*> SkywardSwordFile::questList() const
{ {
return m_quests; return m_quests;
} }
void SSFile::setRegion(Region region) void SkywardSwordFile::setRegion(Region region)
{ {
m_region = region; m_region = region;
} }
Region SSFile::region() const Region SkywardSwordFile::region() const
{ {
return m_region; return m_region;
} }

View File

@ -1,6 +1,6 @@
#include "SSFileReader.hpp" #include "SkywardSwordFileReader.hpp"
#include "SSFile.hpp" #include "SkywardSwordFile.hpp"
#include "SSQuest.hpp" #include "SkywardSwordQuest.hpp"
#include "InvalidOperationException.hpp" #include "InvalidOperationException.hpp"
#include <iostream> #include <iostream>
@ -9,27 +9,27 @@ namespace zelda
namespace io namespace io
{ {
SSFileReader::SSFileReader(Uint8* data, Uint64 length) SkywardSwordFileReader::SkywardSwordFileReader(Uint8* data, Uint64 length)
: base(data, length) : base(data, length)
{ {
base::setEndianess(BigEndian); base::setEndianess(BigEndian);
} }
SSFileReader::SSFileReader(const std::string& filename) SkywardSwordFileReader::SkywardSwordFileReader(const std::string& filename)
: base(filename) : base(filename)
{ {
base::setEndianess(BigEndian); base::setEndianess(BigEndian);
} }
SSFile* SSFileReader::read() SkywardSwordFile* SkywardSwordFileReader::read()
{ {
SSFile* file = NULL; SkywardSwordFile* file = NULL;
if (base::length() != 0xFBE0) if (base::length() != 0xFBE0)
throw zelda::error::InvalidOperationException("SSFileReader::read -> File not the expected size of 0xFBE0"); throw zelda::error::InvalidOperationException("SSFileReader::read -> File not the expected size of 0xFBE0");
Uint32 magic = base::readUInt32(); Uint32 magic = base::readUInt32();
if (magic != SSFile::USMagic && magic != SSFile::JAMagic && magic != SSFile::EUMagic) if (magic != SkywardSwordFile::USMagic && magic != SkywardSwordFile::JAMagic && magic != SkywardSwordFile::EUMagic)
throw zelda::error::InvalidOperationException("SSFileReader::read -> Not a valid Skyward Sword save file"); throw zelda::error::InvalidOperationException("SSFileReader::read -> Not a valid Skyward Sword save file");
base::seek(0x01C, base::Beginning); base::seek(0x01C, base::Beginning);
@ -39,11 +39,11 @@ SSFile* SSFileReader::read()
throw zelda::error::InvalidOperationException("SSFileHeader::read -> Invalid header size, Corrupted data?"); throw zelda::error::InvalidOperationException("SSFileHeader::read -> Invalid header size, Corrupted data?");
// Time to read in each slot // Time to read in each slot
file = new SSFile; file = new SkywardSwordFile;
file->setRegion((magic == SSFile::USMagic ? NTSCURegion : (magic == SSFile::JAMagic ? NTSCJRegion : PALRegion))); file->setRegion((magic == SkywardSwordFile::USMagic ? NTSCURegion : (magic == SkywardSwordFile::JAMagic ? NTSCJRegion : PALRegion)));
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
SSQuest* q = new SSQuest((Uint8*)base::readBytes(0x53C0), 0x53C0); SkywardSwordQuest* q = new SkywardSwordQuest((Uint8*)base::readBytes(0x53C0), 0x53C0);
Uint64 pos = base::position(); Uint64 pos = base::position();
// seek to the skip data for this particular quest // seek to the skip data for this particular quest
base::seek(0xFB60 + (i * 0x24), base::Beginning); base::seek(0xFB60 + (i * 0x24), base::Beginning);

View File

@ -1,36 +1,36 @@
#include "SSFileWriter.hpp" #include "SkywardSwordFileWriter.hpp"
#include "SSFile.hpp" #include "SkywardSwordFile.hpp"
#include "SSQuest.hpp" #include "SkywardSwordQuest.hpp"
namespace zelda namespace zelda
{ {
namespace io namespace io
{ {
SSFileWriter::SSFileWriter(Uint8 *data, Uint64 len) SkywardSwordFileWriter::SkywardSwordFileWriter(Uint8 *data, Uint64 len)
: base(data, len) : base(data, len)
{ {
base::setEndianess(BigEndian); base::setEndianess(BigEndian);
} }
SSFileWriter::SSFileWriter(const std::string &filename) SkywardSwordFileWriter::SkywardSwordFileWriter(const std::string &filename)
: base(filename) : base(filename)
{ {
base::setEndianess(BigEndian); base::setEndianess(BigEndian);
} }
void SSFileWriter::write(SSFile *file) void SkywardSwordFileWriter::write(SkywardSwordFile *file)
{ {
Uint32 magic = (file->region() == NTSCURegion ? SSFile::USMagic : Uint32 magic = (file->region() == NTSCURegion ? SkywardSwordFile::USMagic :
(file->region() == NTSCJRegion ? SSFile::JAMagic : SSFile::EUMagic)); (file->region() == NTSCJRegion ? SkywardSwordFile::JAMagic : SkywardSwordFile::EUMagic));
base::writeUInt32(magic); base::writeUInt32(magic);
base::seek(0x1C, base::Beginning); base::seek(0x1C, base::Beginning);
base::writeUInt32(0x1D); base::writeUInt32(0x1D);
std::vector<SSQuest*> quests = file->questList(); std::vector<SkywardSwordQuest*> quests = file->questList();
int i = 0; int i = 0;
for (SSQuest* q : quests) for (SkywardSwordQuest* q : quests)
{ {
// Write the save data // Write the save data
base::writeUBytes(q->data(), q->length()); base::writeUBytes(q->data(), q->length());

View File

@ -1,15 +1,15 @@
#include "SSQuest.hpp" #include "SkywardSwordQuest.hpp"
namespace zelda namespace zelda
{ {
SSQuest::SSQuest(Uint8 *data, Uint32 len) SkywardSwordQuest::SkywardSwordQuest(Uint8 *data, Uint32 len)
: ZQuestFile(ZQuestFile::SS, BigEndian, data, len), : ZQuestFile(ZQuestFile::SS, BigEndian, data, len),
m_skipData(NULL), m_skipData(NULL),
m_skipLength(0) m_skipLength(0)
{ {
} }
void SSQuest::setSkipData(const Uint8 *data, Uint32 len) void SkywardSwordQuest::setSkipData(const Uint8 *data, Uint32 len)
{ {
if (m_skipData) if (m_skipData)
{ {
@ -21,12 +21,12 @@ void SSQuest::setSkipData(const Uint8 *data, Uint32 len)
m_skipLength = len; m_skipLength = len;
} }
Uint8 *SSQuest::skipData() const Uint8 *SkywardSwordQuest::skipData() const
{ {
return m_skipData; return m_skipData;
} }
Uint32 SSQuest::skipLength() const Uint32 SkywardSwordQuest::skipLength() const
{ {
return m_skipLength; return m_skipLength;
} }

136
src/Sprite.cpp Normal file
View File

@ -0,0 +1,136 @@
#include "Sprite.hpp"
#include "SpritePart.hpp"
#include "SpriteFile.hpp"
#include <algorithm>
namespace zelda
{
namespace Sakura
{
Sprite::Sprite(SpriteFile* root)
: m_root(root),
m_currentState(0)
{
}
Sprite::Sprite(SpriteFile* root, const std::string& name)
: m_root(root),
m_name(name),
m_currentState(0)
{
}
Sprite::~Sprite()
{
}
void Sprite::setPosition(const float x, const float y)
{
setPosition(Vector2Df(x, y));
}
void Sprite::setPosition(const Vector2Df& pos)
{
m_position = pos;
}
Vector2Df Sprite::position() const
{
return m_position;
}
void Sprite::setName(const std::string& name)
{
m_name = name;
}
std::string Sprite::name() const
{
return m_name;
}
void Sprite::addStateId(int id)
{
if (m_stateIds.size() >= 65536)
return;
if (std::find(m_stateIds.begin(), m_stateIds.end(), id) == m_stateIds.end())
m_stateIds.push_back(id);
}
int Sprite::stateId(int index) const
{
if (index >= (int)m_stateIds.size())
return -1;
return m_stateIds[index];
}
void Sprite::setStateIds(std::vector<int> ids)
{
if (ids.size() == 0)
return;
m_stateIds = ids;
}
std::vector<int> Sprite::stateIds() const
{
return m_stateIds;
}
Uint32 Sprite::stateCount() const
{
return m_stateIds.size();
}
void Sprite::setCurrentState(const Uint32 id)
{
m_currentState = id;
}
void Sprite::addPart(SpritePart* part)
{
for (SpritePart* tmp : m_parts)
{
if (tmp == part)
return;
}
m_parts.push_back(part);
}
void Sprite::setParts(std::vector<SpritePart*> parts)
{
if (parts.size() == 0)
return;
if (m_parts.size() > 0)
{
for (SpritePart* part : m_parts)
{
delete part;
part = NULL;
}
m_parts.clear();
}
m_parts = parts;
}
Uint32 Sprite::partCount() const
{
return m_parts.size();
}
std::vector<SpritePart*> Sprite::parts() const
{
return m_parts;
}
SpriteFile* Sprite::container() const
{
return m_root;
}
}
}

180
src/SpriteFile.cpp Normal file
View File

@ -0,0 +1,180 @@
#include "SpriteFile.hpp"
#include "Sprite.hpp"
#include <iostream>
namespace zelda
{
namespace Sakura
{
const Uint32 SpriteFile::Major = 1;
const Uint32 SpriteFile::Minor = 0;
const Uint32 SpriteFile::Revision = 1;
const Uint32 SpriteFile::Build = 0;
const Uint32 SpriteFile::Version = Major | (Minor << 8) | (Revision << 16) | (Build << 24);
const Uint32 SpriteFile::Magic = 'S' | ('P' << 8) | ('R' << 16) | ('S' << 24);
SpriteFile::SpriteFile()
: m_size(Vector2Di(1, 1))
{
}
SpriteFile::SpriteFile(Uint32 width, Uint32 height, float originX, float originY)
: m_size(Vector2Di(width, height)),
m_origin(Vector2Df(originX, originY))
{
}
SpriteFile::SpriteFile(const Vector2Di& size, const Vector2Df& origin)
: m_size(size),
m_origin(origin)
{
}
void SpriteFile::setSize(Uint32 width, Uint32 height)
{
setSize(Vector2Di(width, height));
}
void SpriteFile::setSize(const Vector2Di& size)
{
m_size = size;
}
Vector2Di SpriteFile::size() const
{
return m_size;
}
Uint32 SpriteFile::width() const
{
return m_size.x;
}
Uint32 SpriteFile::height() const
{
return m_size.y;
}
void SpriteFile::setOrigin(const float x, const float y)
{
setOrigin(Vector2Df(x, y));
}
void SpriteFile::setOrigin(const Vector2Df& origin)
{
m_origin = origin;
}
Vector2Df SpriteFile::origin() const
{
return m_origin;
}
float SpriteFile::originX() const
{
return m_origin.x;
}
float SpriteFile::originY() const
{
return m_origin.y;
}
void SpriteFile::addTexture(STexture* texture)
{
m_textures.push_back(texture);
}
void SpriteFile::removeTexture(int id)
{
if (id > (int)m_textures.size() || id < 0)
return;
STexture* tex = m_textures[id];
m_textures.erase(m_textures.begin() + id);
delete tex;
}
STexture* SpriteFile::texture(Uint32 id)
{
if (id >= m_textures.size())
return NULL;
return m_textures[id];
}
std::vector<STexture*> SpriteFile::textures() const
{
return m_textures;
}
Uint32 SpriteFile::textureCount() const
{
return m_textures.size();
}
void SpriteFile::addSprite(Sprite* sprite)
{
if (m_sprites.find(sprite->name()) != m_sprites.end())
return;
m_sprites[sprite->name()] = sprite;
}
void SpriteFile::setSprites(std::unordered_map<std::string, Sprite*> sprites)
{
if (sprites.size() == 0)
return;
if (m_sprites.size() > 0)
{
for (std::pair<std::string, Sprite*> sprite : m_sprites)
{
delete sprite.second;
sprite.second = NULL;
}
m_sprites.clear();
}
m_sprites = sprites;
}
Sprite* SpriteFile::sprite(const std::string& name)
{
if (m_sprites.find(name) == m_sprites.end())
return NULL;
return m_sprites[name];
}
std::unordered_map<std::string, Sprite*> SpriteFile::sprites() const
{
return m_sprites;
}
Uint32 SpriteFile::spriteCount() const
{
return m_sprites.size();
}
void SpriteFile::setTextures(std::vector<STexture*> textures)
{
if (textures.size() == 0)
return;
if (m_textures.size() > 0)
{
for(STexture* tex : m_textures)
{
delete tex;
tex = NULL;
}
m_textures.clear();
}
m_textures = textures;
}
} // Sakura
} // zelda

148
src/SpriteFileReader.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "SpriteFileReader.hpp"
#include "SpriteFile.hpp"
#include "Sprite.hpp"
#include "SpritePart.hpp"
#include "SpriteFrame.hpp"
#include "InvalidOperationException.hpp"
#include "IOException.hpp"
namespace zelda
{
namespace io
{
SpriteFileReader::SpriteFileReader(Uint8* data, Uint64 length)
: base(data, length)
{
}
SpriteFileReader::SpriteFileReader(const std::string& filepath)
: base(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<Sakura::STexture*> textures;
for (Uint16 i = 0; i < textureCount; i++)
{
Sakura::STexture* texture = new Sakura::STexture;
texture->Filepath = base::readString();
texture->Preload = base::readBool();
textures.push_back(texture);
}
ret->setTextures(textures);
// 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 <std::string, Sakura::Sprite*> 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<int> 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<Sakura::SpritePart*> 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<Sakura::SpriteFrame*> 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);
return ret;
}
} // io
} // zelda

81
src/SpriteFileWriter.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "SpriteFileWriter.hpp"
#include "SpriteFile.hpp"
#include "Sprite.hpp"
#include "SpritePart.hpp"
#include "SpriteFrame.hpp"
#include "InvalidOperationException.hpp"
namespace zelda
{
namespace io
{
SpriteFileWriter::SpriteFileWriter(Uint8 *data, Uint64 length)
: base(data, length)
{
}
SpriteFileWriter::SpriteFileWriter(const std::string& filepath)
: base(filepath)
{
}
void SpriteFileWriter::writeFile(Sakura::SpriteFile* file)
{
if (!file)
throw zelda::error::InvalidOperationException("SSpriteFileWriter::writeFile -> file cannot be NULL");
base::writeUInt32(Sakura::SpriteFile::Magic);
base::writeUInt32(Sakura::SpriteFile::Version);
base::writeUInt16(file->textureCount());
base::writeUInt32(file->width());
base::writeUInt32(file->height());
base::writeFloat(file->originX());
base::writeFloat(file->originY());
base::writeUInt16(file->spriteCount());
base::writeUInt32(0xFFFFFFFF);
for (Sakura::STexture* texture : file->textures())
{
base::writeString(texture->Filepath);
base::writeBool(texture->Preload);
}
for (std::pair<std::string, Sakura::Sprite*> spritePair : file->sprites())
{
Sakura::Sprite* sprite = spritePair.second;
base::writeString(sprite->name());
base::writeUInt16(sprite->partCount());
base::writeUInt16(sprite->stateCount());
for (int id : sprite->stateIds())
base::writeUInt16(id);
for (Sakura::SpritePart* part : sprite->parts())
{
base::writeString(part->name());
base::writeBool(part->hasCollision());
base::writeUInt32(part->frameCount());
for (Sakura::SpriteFrame* frame : part->frames())
{
base::writeFloat(frame->offset().x);
base::writeFloat(frame->offset().y);
base::writeFloat(frame->textureOffset().x);
base::writeFloat(frame->textureOffset().y);
base::writeUInt32(frame->size().x);
base::writeUInt32(frame->size().y);
base::writeFloat(frame->frameTime());
base::writeBool(frame->flippedHorizontally());
base::writeBool(frame->flippedVertically());
}
}
}
save();
}
} // io
} // zelda

109
src/SpriteFrame.cpp Normal file
View File

@ -0,0 +1,109 @@
#include "SpriteFrame.hpp"
#include "SpritePart.hpp"
namespace zelda
{
namespace Sakura
{
SpriteFrame::SpriteFrame()
{
}
SpriteFrame::SpriteFrame(float offX, float offY, float texX, float texY, Uint32 width, Uint32 height, float frameTime, bool flippedH, bool flippedV)
: m_offset(Vector2Df(offX, offY)),
m_textureOffset(Vector2Df(texX, texY)),
m_size(Vector2Di(width, height)),
m_frameTime(frameTime),
m_flippedH(flippedH),
m_flippedV(flippedV)
{
}
SpriteFrame::SpriteFrame(const Vector2Df& frameOff, const Vector2Df& texOff, const Vector2Di& size, float frameTime, bool flippedH, bool flippedV)
: m_offset(frameOff),
m_textureOffset(texOff),
m_size(size),
m_frameTime(frameTime),
m_flippedH(flippedH),
m_flippedV(flippedV)
{
}
void SpriteFrame::setOffset(float x, float y)
{
setOffset(Vector2Df(x, y));
}
void SpriteFrame::setOffset(const Vector2Df& offset)
{
m_offset = offset;
}
Vector2Df SpriteFrame::offset() const
{
return m_offset;
}
void SpriteFrame::setTextureOffset(float x, float y)
{
setTextureOffset(Vector2Df(x, y));
}
void SpriteFrame::setTextureOffset(const Vector2Df& texOff)
{
m_textureOffset = texOff;
}
Vector2Df SpriteFrame::textureOffset() const
{
return m_textureOffset;
}
void SpriteFrame::setSize(Uint32 width, Uint32 height)
{
setSize(Vector2Di(width, height));
}
void SpriteFrame::setSize(const Vector2Di& size)
{
m_size = size;
}
Vector2Di SpriteFrame::size() const
{
return m_size;
}
void SpriteFrame::setFlippedHorizontally(const bool val)
{
m_flippedH = val;
}
bool SpriteFrame::flippedHorizontally() const
{
return m_flippedH;
}
void SpriteFrame::setFlippedVertically(const bool val)
{
m_flippedV = val;
}
bool SpriteFrame::flippedVertically() const
{
return m_flippedV;
}
void SpriteFrame::setFrameTime(float frameTime)
{
m_frameTime = frameTime;
}
float SpriteFrame::frameTime() const
{
return m_frameTime;
}
} // Sakura
} // zelda

128
src/SpritePart.cpp Normal file
View File

@ -0,0 +1,128 @@
#include "SpritePart.hpp"
#include "SpriteFrame.hpp"
#include "Sprite.hpp"
namespace zelda
{
namespace Sakura
{
SpritePart::SpritePart(Sprite* root)
: m_root(root),
m_hasCollision(false),
m_currentFrame(NULL),
m_frameIndex(0)
{
}
SpritePart::SpritePart(Sprite* root, const std::string& name, bool hasCollision)
: m_root(root),
m_name(name),
m_hasCollision(hasCollision),
m_currentFrame(NULL),
m_frameIndex(0)
{
}
SpritePart::~SpritePart()
{
}
void SpritePart::setName(const std::string& name)
{
m_name = name;
}
std::string SpritePart::name() const
{
return m_name;
}
void SpritePart::setCollision(bool col)
{
m_hasCollision = col;
}
bool SpritePart::hasCollision() const
{
return m_hasCollision;
}
void SpritePart::addFrame(SpriteFrame* frame)
{
m_frames.push_back(frame);
}
void SpritePart::advanceFrame()
{
if (m_frameIndex < m_frames.size() - 1)
{
m_frameIndex++;
m_currentFrame = m_frames[m_frameIndex];
}
}
void SpritePart::retreatFrame()
{
if (m_frameIndex > 0)
{
m_frameIndex--;
m_currentFrame = m_frames[m_frameIndex];
}
}
SpriteFrame* SpritePart::frame(int id)
{
if (id < 0 || id >= (int)m_frames.size())
return NULL;
return m_frames[id];
}
void SpritePart::setFrames(std::vector<SpriteFrame*> frames)
{
if (frames.size() == 0)
return;
if (m_frames.size() > 0)
{
for (SpriteFrame* frame : m_frames)
{
delete frame;
frame = NULL;
}
m_frames.clear();
}
if (!m_currentFrame)
{
m_currentFrame = frames[0];
updateTexture();
}
m_frames = frames;
}
SpriteFrame* SpritePart::currentFrame()
{
return m_currentFrame;
}
int SpritePart::currentFrameID()
{
return m_frameIndex;
}
std::vector<SpriteFrame*> SpritePart::frames() const
{
return m_frames;
}
Uint32 SpritePart::frameCount() const
{
return m_frames.size();
}
}
}