Applied some fixes to the binary reader/writer classes

This commit is contained in:
Aruki 2017-07-02 02:12:01 -06:00
parent c6e6ccaa89
commit 5a398423e1
6 changed files with 127 additions and 69 deletions

View File

@ -6,11 +6,14 @@
class CHashFNV1A class CHashFNV1A
{ {
u64 mHash; public:
enum EHashLength { enum EHashLength {
e32Bit, e64Bit e32Bit, e64Bit
} mHashLength; };
private:
u64 mHash;
EHashLength mHashLength;
static const u64 skFNVOffsetBasis32 = 0x811C9DC5; static const u64 skFNVOffsetBasis32 = 0x811C9DC5;
static const u64 skFNVOffsetBasis64 = 0xCBF29CE484222325; static const u64 skFNVOffsetBasis64 = 0xCBF29CE484222325;
@ -18,9 +21,12 @@ class CHashFNV1A
static const u64 skFNVPrime64 = 0x100000001B3; static const u64 skFNVPrime64 = 0x100000001B3;
public: public:
CHashFNV1A() CHashFNV1A(EHashLength Length)
{ {
Init32(); if (Length == e32Bit)
Init32();
else
Init64();
} }
void Init32() void Init32()

View File

@ -11,7 +11,8 @@ class CBinaryReader : public IArchive
{ {
u32 Offset; u32 Offset;
u16 Size; u16 Size;
bool HasChildren; u16 NumChildren;
u16 ChildIndex;
bool Abstract; bool Abstract;
}; };
std::vector<SParameter> mParamStack; std::vector<SParameter> mParamStack;
@ -30,7 +31,7 @@ public:
CSerialVersion Version(*mpStream); CSerialVersion Version(*mpStream);
SetVersion(Version); SetVersion(Version);
mParamStack.reserve(20); InitParamStack();
} }
CBinaryReader(IInputStream *pStream, const CSerialVersion& rkVersion) CBinaryReader(IInputStream *pStream, const CSerialVersion& rkVersion)
@ -41,7 +42,7 @@ public:
mpStream = pStream; mpStream = pStream;
SetVersion(rkVersion); SetVersion(rkVersion);
mParamStack.reserve(20); InitParamStack();
} }
~CBinaryReader() ~CBinaryReader()
@ -49,27 +50,43 @@ public:
if (mOwnsStream) delete mpStream; if (mOwnsStream) delete mpStream;
} }
private:
void InitParamStack()
{
mpStream->Skip(4); // Skip root ID (which is always -1)
u16 Size = mpStream->ReadShort();
u32 Offset = mpStream->Tell();
u16 NumChildren = mpStream->ReadShort();
mParamStack.push_back( SParameter { Offset, Size, NumChildren, 0, false } );
mParamStack.reserve(20);
}
public:
// Interface // Interface
virtual bool ParamBegin(const char *pkName) virtual bool ParamBegin(const char *pkName)
{ {
// If this is the parent parameter's first child, then skip the child count // If this is the parent parameter's first child, then read the child count
if (!mParamStack.empty() && !mParamStack.back().HasChildren) if (mParamStack.back().NumChildren == 0xFFFF)
{ {
mpStream->Seek(0x2, SEEK_CUR); mParamStack.back().NumChildren = mpStream->ReadShort();
mParamStack.back().HasChildren = true;
} }
// Check the next parameter ID first and check whether it's a match for the current parameter // Save current offset
u32 ParamID = TString(pkName).Hash32();
u32 Offset = mpStream->Tell(); u32 Offset = mpStream->Tell();
u32 NextID = mpStream->ReadLong(); u32 ParamID = TString(pkName).Hash32();
u16 NextSize = mpStream->ReadShort();
// Does the next parameter ID match the current one? // Check the next parameter ID first and check whether it's a match for the current parameter
if (NextID == ParamID) if (mParamStack.back().ChildIndex < mParamStack.back().NumChildren)
{ {
mParamStack.push_back( SParameter { Offset, NextSize, false, false } ); u32 NextID = mpStream->ReadLong();
return true; u16 NextSize = mpStream->ReadShort();
// Does the next parameter ID match the current one?
if (NextID == ParamID)
{
mParamStack.push_back( SParameter { Offset, NextSize, 0xFFFF, 0, false } );
return true;
}
} }
// It's not a match - return to the parent parameter's first child and check all children to find a match // It's not a match - return to the parent parameter's first child and check all children to find a match
@ -77,26 +94,27 @@ public:
{ {
bool ParentAbstract = mParamStack.back().Abstract; bool ParentAbstract = mParamStack.back().Abstract;
u32 ParentOffset = mParamStack.back().Offset; u32 ParentOffset = mParamStack.back().Offset;
mpStream->Seek(ParentOffset + (ParentAbstract ? 0xA : 0x6), SEEK_SET); u16 NumChildren = mParamStack.back().NumChildren;
u16 NumChildren = mpStream->ReadShort(); mpStream->GoTo(ParentOffset + (ParentAbstract ? 0xC : 0x8));
for (u32 iChild = 0; iChild < NumChildren; iChild++) for (u32 ChildIdx = 0; ChildIdx < NumChildren; ChildIdx++)
{ {
u32 ChildID = mpStream->ReadLong(); u32 ChildID = mpStream->ReadLong();
u16 ChildSize = mpStream->ReadShort(); u16 ChildSize = mpStream->ReadShort();
if (ChildID != ParamID) if (ChildID != ParamID)
mpStream->Seek(ChildSize, SEEK_CUR); mpStream->Skip(ChildSize);
else else
{ {
mParamStack.push_back( SParameter { (u32) mpStream->Tell() - 6, NextSize, false, false } ); mParamStack.back().ChildIndex = (u16) ChildIdx;
mParamStack.push_back( SParameter { mpStream->Tell() - 6, ChildSize, 0xFFFF, 0, false } );
return true; return true;
} }
} }
} }
// None of the children were a match - this parameter isn't in the file // None of the children were a match - this parameter isn't in the file
mpStream->Seek(Offset, SEEK_SET); mpStream->GoTo(Offset);
return false; return false;
} }
@ -105,8 +123,12 @@ public:
// Make sure we're at the end of the parameter // Make sure we're at the end of the parameter
SParameter& rParam = mParamStack.back(); SParameter& rParam = mParamStack.back();
u32 EndOffset = rParam.Offset + rParam.Size + 6; u32 EndOffset = rParam.Offset + rParam.Size + 6;
mpStream->Seek(EndOffset, SEEK_SET); mpStream->GoTo(EndOffset);
mParamStack.pop_back(); mParamStack.pop_back();
// Increment parent child index
if (!mParamStack.empty())
mParamStack.back().ChildIndex++;
} }
virtual void SerializeContainerSize(u32& rSize, const TString& /*rkElemName*/) virtual void SerializeContainerSize(u32& rSize, const TString& /*rkElemName*/)

View File

@ -27,6 +27,7 @@ public:
SetVersion(skCurrentArchiveVersion, FileVersion, Game); SetVersion(skCurrentArchiveVersion, FileVersion, Game);
GetVersionInfo().Write(*mpStream); GetVersionInfo().Write(*mpStream);
InitParamStack();
} }
CBinaryWriter(IOutputStream *pStream, u16 FileVersion, EGame Game = eUnknownGame) CBinaryWriter(IOutputStream *pStream, u16 FileVersion, EGame Game = eUnknownGame)
@ -36,6 +37,7 @@ public:
ASSERT(pStream->IsValid()); ASSERT(pStream->IsValid());
mpStream = pStream; mpStream = pStream;
SetVersion(skCurrentArchiveVersion, FileVersion, Game); SetVersion(skCurrentArchiveVersion, FileVersion, Game);
InitParamStack();
} }
CBinaryWriter(IOutputStream *pStream, const CSerialVersion& rkVersion) CBinaryWriter(IOutputStream *pStream, const CSerialVersion& rkVersion)
@ -45,49 +47,71 @@ public:
ASSERT(pStream->IsValid()); ASSERT(pStream->IsValid());
mpStream = pStream; mpStream = pStream;
SetVersion(rkVersion); SetVersion(rkVersion);
InitParamStack();
} }
~CBinaryWriter() ~CBinaryWriter()
{ {
if (mOwnsStream) delete mpStream; // Ensure all params have been finished
ASSERT(mParamStack.size() == 1);
// Finish root param
ParamEnd();
// Delete stream
if (mOwnsStream)
delete mpStream;
} }
private:
void InitParamStack()
{
mParamStack.reserve(20);
mpStream->WriteLong(0xFFFFFFFF);
mpStream->WriteShort(0); // Size filler
mParamStack.push_back( SParameter { mpStream->Tell(), 0, false } );
}
public:
// Interface // Interface
virtual bool ParamBegin(const char *pkName) virtual bool ParamBegin(const char *pkName)
{ {
if (!mParamStack.empty()) // Update parent param
{ mParamStack.back().NumSubParams++;
mParamStack.back().NumSubParams++;
if (mParamStack.back().NumSubParams == 1) if (mParamStack.back().NumSubParams == 1)
mpStream->WriteShort(-1); // Sub-param count filler mpStream->WriteShort(-1); // Sub-param count filler
}
mParamStack.push_back( SParameter { (u32) mpStream->Tell(), 0, false } );
// Write param metadata
u32 ParamID = TString(pkName).Hash32(); u32 ParamID = TString(pkName).Hash32();
mpStream->WriteLong(ParamID); mpStream->WriteLong(ParamID);
mpStream->WriteShort((u16) 0xFFFF); // Param size filler mpStream->WriteShort((u16) 0xFFFF); // Param size filler
// Add new param to the stack
mParamStack.push_back( SParameter { mpStream->Tell(), 0, false } );
return true; return true;
} }
virtual void ParamEnd() virtual void ParamEnd()
{ {
// Write param size
SParameter& rParam = mParamStack.back(); SParameter& rParam = mParamStack.back();
u32 StartOffset = rParam.Offset; u32 StartOffset = rParam.Offset;
u32 EndOffset = mpStream->Tell(); u32 EndOffset = mpStream->Tell();
u16 ParamSize = (u16) (EndOffset - StartOffset) - 6; u16 ParamSize = (u16) (EndOffset - StartOffset);
mpStream->Seek(StartOffset + 4, SEEK_SET); mpStream->GoTo(StartOffset - 2);
mpStream->WriteShort(ParamSize); mpStream->WriteShort(ParamSize);
if (rParam.NumSubParams > 0) // Write param child count
if (rParam.NumSubParams > 0 || mParamStack.size() == 1)
{ {
if (rParam.Abstract) mpStream->Seek(4, SEEK_CUR); if (rParam.Abstract) mpStream->Skip(4);
mpStream->WriteShort(rParam.NumSubParams); mpStream->WriteShort(rParam.NumSubParams);
} }
mpStream->Seek(EndOffset, SEEK_SET); mpStream->GoTo(EndOffset);
mParamStack.pop_back(); mParamStack.pop_back();
} }

View File

@ -1,9 +1,24 @@
#include "TString.h" #include "TString.h"
#include "CHashFNV1A.h"
#include <FileIO/IOUtil.h> #include <FileIO/IOUtil.h>
#include <codecvt> #include <codecvt>
#include <locale> #include <locale>
// ************ TString ************ // ************ TString ************
u32 TString::Hash32() const
{
CHashFNV1A Hash(CHashFNV1A::e32Bit);
Hash.HashString(*this);
return Hash.GetHash32();
}
u64 TString::Hash64() const
{
CHashFNV1A Hash(CHashFNV1A::e64Bit);
Hash.HashString(*this);
return Hash.GetHash64();
}
TWideString TString::ToUTF16() const TWideString TString::ToUTF16() const
{ {
TWideString Out; TWideString Out;
@ -81,6 +96,20 @@ TWideString TString::ToUTF16() const
} }
// ************ TWideString ************ // ************ TWideString ************
u32 TWideString::Hash32() const
{
CHashFNV1A Hash(CHashFNV1A::e32Bit);
Hash.HashData(Data(), Size() * sizeof(wchar_t));
return Hash.GetHash32();
}
u64 TWideString::Hash64() const
{
CHashFNV1A Hash(CHashFNV1A::e64Bit);
Hash.HashData(Data(), Size() * sizeof(wchar_t));
return Hash.GetHash64();
}
TString TWideString::ToUTF8() const TString TWideString::ToUTF8() const
{ {
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> Convert; std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> Convert;

View File

@ -387,32 +387,6 @@ public:
return SubString(0, Size() - Amount); return SubString(0, Size() - Amount);
} }
u32 Hash32() const
{
u32 Hash = 0;
for (u32 iChar = 0; iChar < Size(); iChar++)
{
Hash += At(iChar);
Hash *= 101;
}
return Hash;
}
u64 Hash64() const
{
u64 Hash = 0;
for (u32 iChar = 0; iChar < Size(); iChar++)
{
Hash += At(iChar);
Hash *= 101;
}
return Hash;
}
inline u32 ToInt32(int Base = 16) const inline u32 ToInt32(int Base = 16) const
{ {
try { try {
@ -1112,6 +1086,8 @@ public:
TString(const std::string& rkText) : TBasicString<char>(rkText) {} TString(const std::string& rkText) : TBasicString<char>(rkText) {}
TString(const TBasicString<char>& rkStr) : TBasicString<char>(rkStr) {} TString(const TBasicString<char>& rkStr) : TBasicString<char>(rkStr) {}
u32 Hash32() const;
u64 Hash64() const;
class TWideString ToUTF16() const; class TWideString ToUTF16() const;
}; };
@ -1127,6 +1103,8 @@ public:
TWideString(const std::wstring& rkText) : TBasicString<wchar_t>(rkText) {} TWideString(const std::wstring& rkText) : TBasicString<wchar_t>(rkText) {}
TWideString(const TBasicString<wchar_t>& rkStr) : TBasicString<wchar_t>(rkStr) {} TWideString(const TBasicString<wchar_t>& rkStr) : TBasicString<wchar_t>(rkStr) {}
u32 Hash32() const;
u64 Hash64() const;
class TString ToUTF8() const; class TString ToUTF8() const;
}; };

View File

@ -243,8 +243,7 @@ u64 CMaterial::HashParameters()
{ {
if (mRecalcHash) if (mRecalcHash)
{ {
CHashFNV1A Hash; CHashFNV1A Hash(CHashFNV1A::e64Bit);
Hash.Init64();
Hash.HashLong(mVersion); Hash.HashLong(mVersion);
Hash.HashLong(mOptions); Hash.HashLong(mOptions);