Created
November 10, 2024 04:38
-
-
Save FelixWolf/00e46fa39ad3f92a4b1ef8ae884233c9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "fox5.h" | |
#if __has_include("fox5cipher.h") | |
#include "fox5cipher.h" | |
#define HAS_CIPHER | |
#endif | |
#include <fstream> | |
#include <string> | |
#include "LzmaDec.h" | |
#include <iostream> | |
#include <vector> | |
#include <cstring> | |
#include <stdexcept> | |
#include <cstdlib> | |
#define LZMA_PROPS_SIZE 5 | |
#define LZMA_ALONE_HEADER_SIZE 13 // LZMA Alone has 5 bytes properties + 8 bytes uncompressed size | |
// Implement memory allocation functions | |
void* SzAlloc(const ISzAlloc* /*p*/, size_t size) | |
{ | |
return std::malloc(size); | |
} | |
void SzFree(const ISzAlloc* /*p*/, void* address) | |
{ | |
std::free(address); | |
} | |
static ISzAlloc g_Alloc = { SzAlloc, SzFree }; | |
std::vector<uint8_t> decompressLZMA(const std::vector<uint8_t>& compressedData, SizeT uncompressedSize) | |
{ | |
std::vector<uint8_t> decompressedData; | |
// LZMA "Alone" format includes an 8-byte uncompressed size in the header (after the properties) | |
if(uncompressedSize == 0) | |
{ | |
memcpy(&uncompressedSize, compressedData.data() + LZMA_PROPS_SIZE, sizeof(uncompressedSize)); | |
} | |
decompressedData.resize(uncompressedSize); | |
SizeT destLen = uncompressedSize; | |
SizeT srcLen = compressedData.size() - LZMA_ALONE_HEADER_SIZE; // Skip the 13-byte header | |
ELzmaStatus status; | |
int res = LzmaDecode( | |
decompressedData.data(), &destLen, | |
compressedData.data() + LZMA_ALONE_HEADER_SIZE, &srcLen, | |
compressedData.data(), LZMA_PROPS_SIZE, | |
LZMA_FINISH_ANY, &status, &g_Alloc); | |
if(res != SZ_OK) | |
throw std::runtime_error("LZMA decompression failed"); | |
return decompressedData; | |
} | |
// Utilities | |
uint32_t readUint32(uint8_t** dataPtr, uint8_t* dataEnd) { | |
if (*dataPtr + 4 > dataEnd) | |
throw std::runtime_error("Not enough data to read uint32_t."); | |
uint32_t value = (static_cast<uint32_t>((*dataPtr)[0]) << 24) | | |
(static_cast<uint32_t>((*dataPtr)[1]) << 16) | | |
(static_cast<uint32_t>((*dataPtr)[2]) << 8) | | |
(static_cast<uint32_t>((*dataPtr)[3])); | |
*dataPtr += 4; // Move the pointer forward by 4 bytes | |
return value; | |
} | |
int32_t readInt32(uint8_t** dataPtr, uint8_t* dataEnd) { | |
if (*dataPtr + 4 > dataEnd) | |
throw std::runtime_error("Not enough data to read uint32_t."); | |
int32_t value = (static_cast<int32_t>((*dataPtr)[0]) << 24) | | |
(static_cast<int32_t>((*dataPtr)[1]) << 16) | | |
(static_cast<int32_t>((*dataPtr)[2]) << 8) | | |
(static_cast<int32_t>((*dataPtr)[3])); | |
*dataPtr += 4; // Move the pointer forward by 4 bytes | |
return value; | |
} | |
uint16_t readUint16(uint8_t** dataPtr, uint8_t* dataEnd) { | |
if (*dataPtr + 2 > dataEnd) | |
throw std::runtime_error("Not enough data to read uint16_t."); | |
uint16_t value = (static_cast<uint16_t>((*dataPtr)[0]) << 8) | | |
(static_cast<uint16_t>((*dataPtr)[1])); | |
*dataPtr += 2; // Move the pointer forward by 2 bytes | |
return value; | |
} | |
int16_t readInt16(uint8_t** dataPtr, uint8_t* dataEnd) { | |
if (*dataPtr + 2 > dataEnd) | |
throw std::runtime_error("Not enough data to read uint16_t."); | |
int16_t value = (static_cast<int16_t>((*dataPtr)[0]) << 8) | | |
(static_cast<int16_t>((*dataPtr)[1])); | |
*dataPtr += 2; // Move the pointer forward by 2 bytes | |
return value; | |
} | |
uint8_t readUint8(uint8_t** dataPtr, uint8_t* dataEnd) { | |
if (*dataPtr + 1 > dataEnd) | |
throw std::runtime_error("Not enough data to read uint8_t."); | |
uint8_t value = static_cast<uint8_t>((*dataPtr)[0]); | |
*dataPtr += 1; // Move the pointer forward by 2 bytes | |
return value; | |
} | |
int8_t readInt8(uint8_t** dataPtr, uint8_t* dataEnd) { | |
if (*dataPtr + 1 > dataEnd) | |
throw std::runtime_error("Not enough data to read uint8_t."); | |
int8_t value = static_cast<int8_t>((*dataPtr)[0]); | |
*dataPtr += 1; // Move the pointer forward by 2 bytes | |
return value; | |
} | |
uint32_t readUint32(std::ifstream& file) | |
{ | |
uint8_t buffer[4]; | |
file.read(reinterpret_cast<char*>(buffer), 4); | |
if (!file) | |
throw std::runtime_error("Failed to read uint32_t from file."); | |
uint32_t value = (static_cast<uint32_t>(buffer[0]) << 24) | | |
(static_cast<uint32_t>(buffer[1]) << 16) | | |
(static_cast<uint32_t>(buffer[2]) << 8) | | |
(static_cast<uint32_t>(buffer[3])); | |
return value; | |
} | |
uint16_t readUint16(std::ifstream& file) | |
{ | |
uint8_t buffer[2]; | |
file.read(reinterpret_cast<char*>(buffer), 2); | |
if (!file) | |
throw std::runtime_error("Failed to read uint32_t from file."); | |
uint16_t value = (static_cast<uint32_t>(buffer[0]) << 8) | | |
(static_cast<uint32_t>(buffer[1])); | |
return value; | |
} | |
// Implementation | |
void FOX5Sprite::parseData(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
while (*dataPtr < dataEnd) | |
{ | |
uint8_t cmd = readUint8(dataPtr, dataEnd); | |
switch(static_cast<FOX5Sprite::BlockCommand>(cmd)) | |
{ | |
case FOX5Sprite::BlockCommand::NOP: | |
break; | |
case FOX5Sprite::BlockCommand::PURPOSE: | |
{ | |
mPurpose = readUint16(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Sprite::BlockCommand::IMAGE_ID: | |
{ | |
mImageID = readUint16(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Sprite::BlockCommand::OFFSET: | |
{ | |
mOffset[0] = readInt16(dataPtr, dataEnd); | |
mOffset[1] = readInt16(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Sprite::BlockCommand::LIST_START: | |
{ | |
throw std::runtime_error("FOX5Sprite can't contain lists"); | |
break; | |
} | |
case FOX5Sprite::BlockCommand::LIST_END: | |
return; | |
default: | |
throw std::runtime_error("Unknown command " + std::to_string(cmd)); | |
} | |
} | |
} | |
void FOX5Frame::parseData(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
while (*dataPtr < dataEnd) | |
{ | |
uint8_t cmd = readUint8(dataPtr, dataEnd); | |
switch(static_cast<FOX5Frame::BlockCommand>(cmd)) | |
{ | |
case FOX5Frame::BlockCommand::NOP: | |
break; | |
case FOX5Frame::BlockCommand::FRAME_OFFSET: | |
{ | |
mFrameOffset[0] = readInt16(dataPtr, dataEnd); | |
mFrameOffset[1] = readInt16(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Frame::BlockCommand::FURRE_OFFSET: | |
{ | |
mFurreOffset[0] = readInt16(dataPtr, dataEnd); | |
mFurreOffset[1] = readInt16(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Frame::BlockCommand::LIST_START: | |
{ | |
uint8_t level = readUint8(dataPtr, dataEnd); | |
if(level != 4) | |
{ | |
throw std::runtime_error("Expected sprite level 4 in FOX5Frame"); | |
} | |
uint32_t count = readUint32(dataPtr, dataEnd); | |
mSprites.resize(count); | |
for(uint32_t i = 0; i < count; i++) | |
{ | |
mSprites[i] = std::make_shared<FOX5Sprite>( | |
dataPtr, dataEnd | |
);; | |
} | |
break; | |
} | |
case FOX5Frame::BlockCommand::LIST_END: | |
return; | |
default: | |
throw std::runtime_error("Unknown command " + std::to_string(cmd)); | |
} | |
} | |
} | |
void FOX5Shape::parseData(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
while (*dataPtr < dataEnd) | |
{ | |
uint8_t cmd = readUint8(dataPtr, dataEnd); | |
switch(static_cast<FOX5Shape::BlockCommand>(cmd)) | |
{ | |
case FOX5Shape::BlockCommand::NOP: | |
break; | |
case FOX5Shape::BlockCommand::PURPOSE: | |
{ | |
mPurpose = static_cast<FOX5Shape::Purpose>(readUint8(dataPtr, dataEnd)); | |
break; | |
} | |
case FOX5Shape::BlockCommand::STATE: | |
{ | |
mState = readUint8(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Shape::BlockCommand::DIRECTION: | |
{ | |
mDirection = static_cast<FOX5Shape::Direction>(readUint8(dataPtr, dataEnd)); | |
break; | |
} | |
case FOX5Shape::BlockCommand::RATIO: | |
{ | |
mRatio[0] = readUint8(dataPtr, dataEnd); | |
mRatio[1] = readUint8(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Shape::BlockCommand::KITTERSPEAK: | |
{ | |
uint16_t count = readUint16(dataPtr, dataEnd); | |
mKitterspeak.resize(count); | |
for(uint16_t i; i < count; i++) | |
{ | |
mKitterspeak[i] = std::make_unique<Kitterspeak>( | |
readUint16(dataPtr, dataEnd), | |
readInt16(dataPtr, dataEnd), | |
readInt16(dataPtr, dataEnd) | |
); | |
} | |
break; | |
} | |
case FOX5Shape::BlockCommand::LIST_START: | |
{ | |
uint8_t level = readUint8(dataPtr, dataEnd); | |
if(level != 3) | |
{ | |
throw std::runtime_error("Expected frame level 3 in FOX5Shape"); | |
} | |
uint32_t count = readUint32(dataPtr, dataEnd); | |
mFrames.resize(count); | |
for(uint32_t i = 0; i < count; i++) | |
{ | |
mFrames[i] = std::make_shared<FOX5Frame>( | |
dataPtr, dataEnd | |
);; | |
} | |
break; | |
} | |
case FOX5Shape::BlockCommand::LIST_END: | |
return; | |
default: | |
throw std::runtime_error("Unknown command " + std::to_string(cmd)); | |
} | |
} | |
} | |
void FOX5Object::parseData(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
while (*dataPtr < dataEnd) | |
{ | |
uint8_t cmd = readUint8(dataPtr, dataEnd); | |
switch(static_cast<FOX5Object::BlockCommand>(cmd)) | |
{ | |
case FOX5Object::BlockCommand::NOP: | |
break; | |
case FOX5Object::BlockCommand::AUTHOR_REVISION: | |
{ | |
mAuthorRevision = readUint32(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Object::BlockCommand::AUTHORS: | |
{ | |
// TODO: Actually add strings here | |
uint16_t count = readUint16(dataPtr, dataEnd); | |
//mAuthors.resize(count); | |
for(uint16_t i; i < count; i++) | |
{ | |
uint16_t size = readUint16(dataPtr, dataEnd); | |
dataPtr += size; | |
} | |
break; | |
} | |
// case FOX5Object::BlockCommand::AUTHORS_HASH: // Unused ? | |
case FOX5Object::BlockCommand::LICENSE: | |
{ | |
mLicense = static_cast<FOX5Object::License>(readUint8(dataPtr, dataEnd)); | |
break; | |
} | |
case FOX5Object::BlockCommand::KEYWORDS: | |
{ | |
// TODO: Actually add strings here | |
uint16_t count = readUint16(dataPtr, dataEnd); | |
//mKeywords.resize(count); | |
for(uint16_t i; i < count; i++) | |
{ | |
uint16_t size = readUint16(dataPtr, dataEnd); | |
dataPtr += size; | |
} | |
break; | |
} | |
case FOX5Object::BlockCommand::NAME: | |
{ | |
// TODO: Actually add strings here | |
uint16_t size = readUint16(dataPtr, dataEnd); | |
dataPtr += size; | |
break; | |
} | |
case FOX5Object::BlockCommand::DESCRIPTION: | |
{ | |
// TODO: Actually add strings here | |
uint16_t size = readUint16(dataPtr, dataEnd); | |
dataPtr += size; | |
break; | |
} | |
case FOX5Object::BlockCommand::FLAGS: | |
{ | |
mFlags = readUint8(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Object::BlockCommand::TELEPORT_DREAMURL: | |
{ | |
// TODO: Actually add strings here | |
uint16_t size = readUint16(dataPtr, dataEnd); | |
dataPtr += size; | |
break; | |
} | |
case FOX5Object::BlockCommand::MORE_FLAGS: | |
{ | |
mMoreFlags = readUint32(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Object::BlockCommand::OBJECT_IDENTIFIER: | |
{ | |
mObjectID = readInt32(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Object::BlockCommand::EDIT_TYPE: | |
{ | |
mEditType = readUint8(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Object::BlockCommand::FX_TYPE: | |
{ | |
mFilterTarget = readUint8(dataPtr, dataEnd); | |
mFilterMode = readUint8(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5Object::BlockCommand::LIST_START: | |
{ | |
uint8_t level = readUint8(dataPtr, dataEnd); | |
if(level != 2) | |
throw std::runtime_error("Expected shape level 2 in FOX5Object"); | |
uint32_t count = readUint32(dataPtr, dataEnd); | |
mShapes.resize(count); | |
for(uint32_t i = 0; i < count; i++) | |
{ | |
mShapes[i] = std::make_shared<FOX5Shape>( | |
dataPtr, dataEnd | |
);; | |
} | |
break; | |
} | |
case FOX5Object::BlockCommand::LIST_END: | |
return; | |
default: | |
throw std::runtime_error("Unknown command FOX5Object " + std::to_string(cmd)); | |
} | |
} | |
} | |
void FOX5File::parseData(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
while (*dataPtr < dataEnd) | |
{ | |
uint8_t cmd = readUint8(dataPtr, dataEnd); | |
switch(static_cast<FOX5File::BlockCommand>(cmd)) | |
{ | |
case FOX5File::BlockCommand::NOP: | |
break; | |
case FOX5File::BlockCommand::IMAGE_LIST: | |
{ | |
uint32_t count = readUint32(dataPtr, dataEnd); | |
mImageList.resize(count); | |
uint32_t offset = mFile.tellg(); | |
for(uint32_t i = 0; i < count; i++) | |
{ | |
uint32_t compressedSize = readUint32(dataPtr, dataEnd); | |
uint16_t width = readUint16(dataPtr, dataEnd); | |
uint16_t height = readUint16(dataPtr, dataEnd); | |
uint8_t format = readUint8(dataPtr, dataEnd); | |
mImageList[i] = std::make_shared<FOX5Image>( | |
offset, | |
compressedSize, | |
width, height, | |
static_cast<FOX5Image::ImageFormat>(format) | |
); | |
offset += compressedSize; | |
} | |
break; | |
} | |
case FOX5File::BlockCommand::GENERATOR: | |
{ | |
mGenerator = readUint8(dataPtr, dataEnd); | |
break; | |
} | |
case FOX5File::BlockCommand::LIST_START: | |
{ | |
uint8_t level = readUint8(dataPtr, dataEnd); | |
if(level != 1) | |
throw std::runtime_error("Expected object level 1 in FOX5File"); | |
uint32_t count = readUint32(dataPtr, dataEnd); | |
mObjects.resize(count); | |
for(uint32_t i = 0; i < count; i++) | |
{ | |
mObjects[i] = std::make_shared<FOX5Object>( | |
dataPtr, dataEnd | |
); | |
} | |
break; | |
} | |
case FOX5File::BlockCommand::LIST_END: | |
return; | |
default: | |
throw std::runtime_error("Unknown command in FOX5File " + std::to_string(cmd)); | |
} | |
} | |
} | |
FOX5Image FOX5File::getImage(uint32_t id) | |
{ | |
if(id >= mImageList.size()) | |
throw std::runtime_error("Image index out of bounds"); | |
FOX5Image im = FOX5Image(*mImageList[id]); | |
printf("%i %i %i\n", im.mWidth, im.mHeight, im.getMemSize()); | |
im.mData.resize(im.getMemSize()); | |
mFile.seekg(im.mOffset, std::ios::beg); | |
mFile.read(reinterpret_cast<char*>(im.mData.data()), im.mCompressedSize); | |
if(mEncryptionType == EncryptionType::ENCRYPTED) | |
{ | |
#ifdef HAS_CIPHER | |
Fox5Cipher(im.mData, im.mCompressedSize, im.mData.size(), mSeed); | |
#else | |
throw std::runtime_error("Can't decrypt without fox5 cipher library"); | |
#endif | |
} | |
if(mCompressionType == FOX5File::CompressionType::LZMA) | |
{ | |
im.mData = decompressLZMA(im.mData, im.mData.size()); | |
} | |
else if(mCompressionType != FOX5File::CompressionType::NOT) | |
{ | |
throw std::runtime_error("Unknown compression type"); | |
} | |
return im; | |
} | |
FOX5File::FOX5File(const std::string& filename) : | |
mFile(filename, std::ios::binary) | |
{ | |
if (!mFile) throw std::runtime_error("Failed to open file."); | |
mFile.seekg(0, std::ios::end); | |
if(mFile.tellg() < 20) throw std::runtime_error("Too small to be a FOX5 file."); | |
mFile.seekg(-20, std::ios::end); | |
uint8_t uint8read; | |
mFile.read(reinterpret_cast<char*>(&uint8read), sizeof(uint8read)); | |
if (!mFile) throw std::runtime_error("Failed to read compression type."); | |
mCompressionType = static_cast<CompressionType>(uint8read); | |
mFile.read(reinterpret_cast<char*>(&uint8read), sizeof(uint8read)); | |
if (!mFile) throw std::runtime_error("Failed to read encryption type."); | |
mEncryptionType = static_cast<EncryptionType>(uint8read); | |
mFile.seekg(2, std::ios::cur); //Skip two reserved | |
uint32_t dbCompressedSize = readUint32(mFile); | |
uint32_t dbUncompressedSize = readUint32(mFile); | |
char magic[9] = {0}; | |
mFile.read(magic, 8); | |
if (!mFile) throw std::runtime_error("Failed to read magic from FOX5."); | |
if(std::string(magic, sizeof(magic) - 1) != "FOX5.1.1") | |
throw std::runtime_error("Not a FOX5 file."); | |
if(mEncryptionType == EncryptionType::ENCRYPTED) | |
{ | |
mFile.seekg(-36, std::ios::end); | |
mFile.read(reinterpret_cast<char*>(mSeed), sizeof(mSeed)); | |
if (!mFile) throw std::runtime_error("Failed to read seed from FOX5."); | |
} | |
mFile.seekg(0, std::ios::beg); | |
std::vector<uint8_t> commandBlock(dbUncompressedSize); | |
mFile.read(reinterpret_cast<char*>(commandBlock.data()), dbCompressedSize); | |
if(mEncryptionType == EncryptionType::ENCRYPTED) | |
{ | |
#ifdef HAS_CIPHER | |
Fox5Cipher(commandBlock, dbCompressedSize, dbUncompressedSize, mSeed); | |
#else | |
throw std::runtime_error("Can't decrypt without fox5 cipher library"); | |
#endif | |
} | |
if(mCompressionType == FOX5File::CompressionType::LZMA) | |
{ | |
commandBlock = decompressLZMA(commandBlock, dbUncompressedSize); | |
} | |
else if(mCompressionType != FOX5File::CompressionType::NOT) | |
{ | |
throw std::runtime_error("Unknown compression type"); | |
} | |
uint8_t* pointer = commandBlock.data(); | |
uint8_t* dataEnd = pointer + commandBlock.size(); | |
pointer += 4; | |
uint8_t cmd = readUint8(&pointer, dataEnd); | |
if(static_cast<FOX5File::BlockCommand>(cmd) != FOX5File::BlockCommand::LIST_START) | |
throw std::runtime_error("Expected list start as first entry"); | |
uint8_t level = readUint8(&pointer, dataEnd); | |
if(level != 0) | |
throw std::runtime_error("Expected file level 0 at start"); | |
uint32_t count = readUint32(&pointer, dataEnd); | |
if(count != 1) | |
throw std::runtime_error("File level list should always have 1 entry"); | |
parseData(&pointer, dataEnd); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef FOX5_H | |
#define FOX5_H | |
#include <cstdint> | |
#include <fstream> | |
#include <vector> | |
#include <string> | |
#include <map> | |
#include <unordered_map> | |
#include <memory> | |
class FOX5Image | |
{ | |
public: | |
uint32_t mOffset; | |
uint32_t mCompressedSize; | |
uint16_t mWidth; | |
uint16_t mHeight; | |
std::vector<uint8_t> mData; | |
enum class ImageFormat : uint8_t | |
{ | |
E_8BIT = 0, | |
E_32BIT = 1 | |
}; | |
ImageFormat mImageFormat; | |
uint32_t getMemSize() | |
{ | |
uint32_t s = mWidth*mHeight; | |
if(mImageFormat == ImageFormat::E_32BIT) | |
s *= 4; | |
return s; | |
} | |
FOX5Image(uint32_t offset, uint32_t compressedSize, uint16_t width, uint16_t height, ImageFormat format) | |
: mOffset(offset), mCompressedSize(compressedSize), mWidth(width), mHeight(height), mImageFormat(format) | |
{} | |
}; | |
class FOX5List | |
{ | |
public: | |
enum class BlockCommand : uint8_t { | |
NOP = 0x00, | |
LIST_START = 0x4C, // 'L' | |
LIST_END = 0x3C // '<' | |
}; | |
FOX5List() = default; | |
FOX5List(const std::vector<uint8_t> data){}; | |
}; | |
class FOX5Sprite : FOX5List | |
{ | |
public: | |
enum class BlockCommand : uint8_t | |
{ | |
NOP = 0x00, | |
LIST_START = 0x4C, // 'L' | |
LIST_END = 0x3C, // '<' | |
PURPOSE = 0x43, // 'C' | |
IMAGE_ID = 0x63, // 'c' | |
OFFSET = 0x4F // 'O' | |
}; | |
union | |
{ | |
uint16_t mPurpose; | |
struct | |
{ | |
uint8_t : 5; | |
bool mRemap : 1; | |
bool mShadow : 1; | |
bool mMarkup : 1; | |
}; | |
}; | |
uint16_t mImageID; //Number in FOX5File ImageList | |
int16_t mOffset[2] = {0}; | |
void parseData(uint8_t** dataPtr, uint8_t* dataEnd); | |
FOX5Sprite(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
parseData(dataPtr, dataEnd); | |
}; | |
}; | |
class FOX5Frame : FOX5List | |
{ | |
public: | |
enum class BlockCommand : uint8_t | |
{ | |
NOP = 0x00, | |
LIST_START = 0x4C, // 'L' | |
LIST_END = 0x3C, // '<' | |
FRAME_OFFSET = 0x6F, // 'o' | |
FURRE_OFFSET = 0x66 // 'f' | |
}; | |
int16_t mFrameOffset[2] = {0}; | |
int16_t mFurreOffset[2] = {0}; | |
std::vector<std::shared_ptr<FOX5Sprite>> mSprites; | |
void parseData(uint8_t** dataPtr, uint8_t* dataEnd); | |
FOX5Frame(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
parseData(dataPtr, dataEnd); | |
}; | |
}; | |
class FOX5Shape : FOX5List | |
{ | |
public: | |
enum class BlockCommand : uint8_t | |
{ | |
NOP = 0x00, | |
LIST_START = 0x4C, // 'L' | |
LIST_END = 0x3C, // '<' | |
PURPOSE = 0x70, // 'p' | |
STATE = 0x73, // 's' | |
DIRECTION = 0x44, // 'D' | |
RATIO = 0x52, // 'R' | |
KITTERSPEAK = 0x4B, // 'K' | |
}; | |
enum class Purpose : uint8_t | |
{ | |
UNSPECIFIED = 0, | |
MENU_ICON = 1, | |
UI_BUTTON = 2, | |
BUTLER = 3, | |
PORTRAIT = 4, | |
DS_BUTTON = 5, | |
AVATAR = 11, | |
FLOOR = 21, | |
ITEM = 22, | |
WALL = 23, | |
REGION = 24, | |
EFFECT = 25, | |
PAD_ITEM = 28, | |
PORTAL_ITEM = 29, | |
SPECITAG = 35, | |
LIGHTING = 41, | |
AMBIENCE = 42 | |
}; | |
Purpose mPurpose; | |
union | |
{ | |
uint8_t mState; | |
struct | |
{ | |
bool mFemale : 1; | |
bool mMale : 1; | |
bool mUnspecified : 1; | |
}; | |
struct | |
{ | |
bool mClicked : 1; | |
bool mMouseOver : 1; | |
bool mActivated : 1; | |
}; | |
}; | |
enum class Direction : uint8_t | |
{ | |
UNSPECIFIED = 0, | |
SW = 1, | |
S = 2, | |
SE = 3, | |
W = 4, | |
NO_DIRECTION = 5, | |
E = 6, | |
NW = 7, | |
LEFT = 7, | |
N = 8, | |
NE = 9, | |
RIGHT = 9, | |
UP = 10, | |
DOWN = 11 | |
}; | |
Direction mDirection; | |
uint8_t mRatio[2] = {0,0}; | |
struct Kitterspeak { | |
uint16_t mCommand; | |
uint16_t mArg1; | |
uint16_t mArg2; | |
Kitterspeak(uint16_t command, uint16_t arg1, uint16_t arg2) | |
: mCommand(command), mArg1(arg1), mArg2(arg2) {} | |
}; | |
std::vector<std::unique_ptr<Kitterspeak>> mKitterspeak; | |
std::vector<std::shared_ptr<FOX5Frame>> mFrames; | |
void parseData(uint8_t** dataPtr, uint8_t* dataEnd); | |
FOX5Shape(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
parseData(dataPtr, dataEnd); | |
}; | |
}; | |
class FOX5Object : FOX5List | |
{ | |
public: | |
enum class BlockCommand : uint8_t | |
{ | |
NOP = 0x00, | |
LIST_START = 0x4C, // 'L' | |
LIST_END = 0x3C, // '<' | |
AUTHOR_REVISION = 0x72, // 'r' | |
AUTHORS = 0x61, // 'a' | |
AUTHORS_HASH = 0x61, // 'h' | |
LICENSE = 0x6C, // 'l' | |
KEYWORDS = 0x6B, // 'k' | |
NAME = 0x6E, // 'n' | |
DESCRIPTION = 0x64, // 'd' | |
FLAGS = 0x21, // '!' | |
TELEPORT_DREAMURL = 0x50, // 'P' | |
MORE_FLAGS = 0x3F, // '?' | |
OBJECT_IDENTIFIER = 0x69, // 'i' | |
EDIT_TYPE = 0x74, // 't' | |
FX_TYPE = 0x46 // 'F' | |
}; | |
uint32_t mAuthorRevision; | |
std::vector<std::string> mAuthors; | |
enum class License : uint8_t | |
{ | |
STANDARD = 0, | |
FREEDOM = 1, | |
LIMITED = 2, | |
EXCLUSIVE = 3, | |
PRIVATE = 4, | |
CONDITIONAL = 5 | |
}; | |
License mLicense; | |
std::vector<std::string> mKeywords; | |
std::string mObjectName; | |
std::string mObjectDescription; | |
union | |
{ | |
uint8_t mFlags; | |
struct | |
{ | |
bool mWalkable : 1; | |
bool mGettable : 1; | |
bool mSittable : 1; | |
bool mFlyable : 1; | |
bool mSwimmable : 1; | |
bool mClickable : 1; | |
bool mMouseOver : 1; | |
bool mKickable : 1; | |
}; | |
}; | |
std::string mTeleportDreamURL; | |
union | |
{ | |
uint32_t mMoreFlags; | |
struct | |
{ | |
bool mDreamPadAll : 1; | |
bool mDreamPadSS : 1; | |
bool mDreamPadGS : 1; | |
bool mDreamPadLG : 1; | |
bool mDreamPadHG : 1; | |
bool mDreamPadDEP : 1; | |
}; | |
struct | |
{ | |
bool mHopping : 1; | |
bool mFlying : 1; | |
bool mSwimming : 1; | |
bool mChild : 1; | |
}; | |
}; | |
int32_t mObjectID; | |
uint8_t mEditType; | |
uint8_t mFilterTarget; | |
uint8_t mFilterMode; | |
std::vector<std::shared_ptr<FOX5Shape>> mShapes; | |
void parseData(uint8_t** dataPtr, uint8_t* dataEnd); | |
FOX5Object(uint8_t** dataPtr, uint8_t* dataEnd) | |
{ | |
parseData(dataPtr, dataEnd); | |
}; | |
}; | |
class FOX5File : FOX5List | |
{ | |
protected: | |
std::ifstream mFile; | |
public: // Footer | |
uint8_t mSeed[16] = {0}; | |
enum class CompressionType : uint8_t | |
{ | |
NOT = 0, | |
ZLIB = 1, | |
LZMA = 2 | |
}; | |
CompressionType mCompressionType; | |
enum class EncryptionType : uint8_t | |
{ | |
NOT = 0, | |
ENCRYPTED = 1 | |
}; | |
EncryptionType mEncryptionType; | |
public: // FOX5List | |
enum class BlockCommand : uint8_t | |
{ | |
NOP = 0x00, | |
LIST_START = 0x4C, // 'L' | |
LIST_END = 0x3C, // '<' | |
GENERATOR = 0x67, // 'g' | |
IMAGE_LIST = 0x53 // 'S' | |
}; | |
uint8_t mGenerator; | |
std::vector<std::shared_ptr<FOX5Image>> mImageList; | |
std::vector<std::shared_ptr<FOX5Object>> mObjects; | |
public: | |
FOX5Image getImage(uint32_t id); | |
public: | |
FOX5File(const std::string& filename); | |
void parseData(uint8_t** dataPtr, uint8_t* dataEnd); | |
}; | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment