More changes to how we handle text encoding, fixing up the code so it compiles now

This commit is contained in:
Aruki
2018-12-23 21:44:31 -07:00
parent e92a9fc6b0
commit 0ae7b8686e
16 changed files with 163 additions and 139 deletions

View File

@@ -63,7 +63,7 @@ void CWorld::SetAreaLayerInfo(CGameArea *pArea)
TString CWorld::InGameName() const
{
if (mpWorldName)
return mpWorldName->String("ENGL", 0);
return mpWorldName->GetString(ELanguage::English, 0);
else
return Entry()->Name();
}
@@ -73,7 +73,7 @@ TString CWorld::AreaInGameName(uint32 AreaIndex) const
const SArea& rkArea = mAreas[AreaIndex];
if (rkArea.pAreaName)
return rkArea.pAreaName->String("ENGL", 0);
return rkArea.pAreaName->GetString(ELanguage::English, 0);
else
return "!!" + rkArea.InternalName;
}

View File

@@ -214,7 +214,7 @@ void CScanLoader::LoadParamsMP3(IInputStream& rSCAN, uint16 NumProperties)
switch (PropertyID)
{
case 0x2F5B6423:
mpScan->mpStringTable = gpResourceStore->LoadResource(rSCAN.ReadLongLong(), EResourceType::Scan);
mpScan->mpStringTable = gpResourceStore->LoadResource(rSCAN.ReadLongLong(), EResourceType::StringTable);
break;
case 0xC308A322:

View File

@@ -1,193 +1,214 @@
#include "CStringLoader.h"
#include <Common/Log.h>
#include <Common/Math/MathUtil.h>
void CStringLoader::LoadPrimeDemoSTRG(IInputStream& rSTRG)
void CStringLoader::LoadPrimeDemoSTRG(IInputStream& STRG)
{
// This function starts at 0x4 in the file - right after the size
// This STRG version only supports one language per file
mpStringTable->mLangTables.resize(1);
CStringTable::SLangTable* Lang = &mpStringTable->mLangTables[1];
Lang->Language = "ENGL";
uint32 TableStart = rSTRG.Tell();
mpStringTable->mLanguages.resize(1);
CStringTable::SLanguageData& Language = mpStringTable->mLanguages[0];
Language.Language = ELanguage::English;
uint TableStart = STRG.Tell();
// Header
uint32 NumStrings = rSTRG.ReadLong();
Lang->Strings.resize(NumStrings);
mpStringTable->mNumStrings = NumStrings;
uint NumStrings = STRG.ReadLong();
Language.Strings.resize(NumStrings);
// String offsets (yeah, that wasn't much of a header)
std::vector<uint32> StringOffsets(NumStrings);
for (uint32 iOff = 0; iOff < StringOffsets.size(); iOff++)
StringOffsets[iOff] = rSTRG.ReadLong();
std::vector<uint> StringOffsets(NumStrings);
for (uint32 OffsetIdx = 0; OffsetIdx < NumStrings; OffsetIdx++)
StringOffsets[OffsetIdx] = STRG.ReadLong();
// Strings
for (uint32 iStr = 0; iStr < NumStrings; iStr++)
for (uint StringIdx = 0; StringIdx < NumStrings; StringIdx++)
{
rSTRG.Seek(TableStart + StringOffsets[iStr], SEEK_SET);
Lang->Strings[iStr] = rSTRG.ReadWString().ToUTF8();
STRG.GoTo( TableStart + StringOffsets[StringIdx] );
Language.Strings[StringIdx] = STRG.Read16String().ToUTF8();
}
}
void CStringLoader::LoadPrimeSTRG(IInputStream& rSTRG)
void CStringLoader::LoadPrimeSTRG(IInputStream& STRG)
{
// This function starts at 0x8 in the file, after magic/version
// Header
uint32 NumLanguages = rSTRG.ReadLong();
uint32 NumStrings = rSTRG.ReadLong();
mpStringTable->mNumStrings = NumStrings;
uint NumLanguages = STRG.ReadLong();
uint NumStrings = STRG.ReadLong();
// Language definitions
mpStringTable->mLangTables.resize(NumLanguages);
std::vector<uint32> LangOffsets(NumLanguages);
mpStringTable->mLanguages.resize(NumLanguages);
std::vector<uint> LanguageOffsets(NumLanguages);
for (uint32 iLang = 0; iLang < NumLanguages; iLang++)
for (uint LanguageIdx = 0; LanguageIdx < NumLanguages; LanguageIdx++)
{
mpStringTable->mLangTables[iLang].Language = CFourCC(rSTRG);
LangOffsets[iLang] = rSTRG.ReadLong();
if (mVersion == EGame::Echoes) rSTRG.Seek(0x4, SEEK_CUR); // Skipping strings size
mpStringTable->mLanguages[LanguageIdx].Language = (ELanguage) STRG.ReadFourCC();
LanguageOffsets[LanguageIdx] = STRG.ReadLong();
// Skip strings size in MP2
if (mVersion >= EGame::EchoesDemo)
{
STRG.Skip(4);
}
}
// String names
if (mVersion == EGame::Echoes)
LoadNameTable(rSTRG);
if (mVersion >= EGame::EchoesDemo)
{
LoadNameTable(STRG);
}
// Strings
uint32 StringsStart = rSTRG.Tell();
for (uint32 iLang = 0; iLang < NumLanguages; iLang++)
uint StringsStart = STRG.Tell();
for (uint32 LanguageIdx = 0; LanguageIdx < NumLanguages; LanguageIdx++)
{
rSTRG.Seek(StringsStart + LangOffsets[iLang], SEEK_SET);
if (mVersion == EGame::Prime) rSTRG.Seek(0x4, SEEK_CUR); // Skipping strings size
STRG.GoTo( StringsStart + LanguageOffsets[LanguageIdx] );
uint32 LangStart = rSTRG.Tell();
CStringTable::SLangTable* pLang = &mpStringTable->mLangTables[iLang];
pLang->Strings.resize(NumStrings);
// Skip strings size in MP1
if (mVersion == EGame::Prime)
{
STRG.Skip(4);
}
CStringTable::SLanguageData& Language = mpStringTable->mLanguages[LanguageIdx];
Language.Strings.resize(NumStrings);
// Offsets
std::vector<uint32> StringOffsets(NumStrings);
for (uint32 iOff = 0; iOff < NumStrings; iOff++)
StringOffsets[iOff] = rSTRG.ReadLong();
uint LanguageStart = STRG.Tell();
std::vector<uint> StringOffsets(NumStrings);
for (uint StringIdx = 0; StringIdx < NumStrings; StringIdx++)
{
StringOffsets[StringIdx] = LanguageStart + STRG.ReadLong();
}
// The actual strings
for (uint32 iStr = 0; iStr < NumStrings; iStr++)
for (uint StringIdx = 0; StringIdx < NumStrings; StringIdx++)
{
rSTRG.Seek(LangStart + StringOffsets[iStr], SEEK_SET);
pLang->Strings[iStr] = rSTRG.ReadWString().ToUTF8();
STRG.GoTo( StringOffsets[StringIdx] );
Language.Strings[StringIdx] = STRG.Read16String().ToUTF8();
}
}
}
void CStringLoader::LoadCorruptionSTRG(IInputStream& rSTRG)
void CStringLoader::LoadCorruptionSTRG(IInputStream& STRG)
{
// This function starts at 0x8 in the file, after magic/version
// Header
uint32 NumLanguages = rSTRG.ReadLong();
uint32 NumStrings = rSTRG.ReadLong();
mpStringTable->mNumStrings = NumStrings;
uint NumLanguages = STRG.ReadLong();
uint NumStrings = STRG.ReadLong();
// String names
LoadNameTable(rSTRG);
LoadNameTable(STRG);
// Language definitions
mpStringTable->mLangTables.resize(NumLanguages);
std::vector<std::vector<uint32>> LangOffsets(NumLanguages);
mpStringTable->mLanguages.resize(NumLanguages);
std::vector< std::vector<uint> > LanguageOffsets(NumLanguages);
for (uint32 iLang = 0; iLang < NumLanguages; iLang++)
mpStringTable->mLangTables[iLang].Language = CFourCC(rSTRG);
for (uint32 iLang = 0; iLang < NumLanguages; iLang++)
for (uint LanguageIdx = 0; LanguageIdx < NumLanguages; LanguageIdx++)
{
LangOffsets[iLang].resize(NumStrings);
mpStringTable->mLanguages[LanguageIdx].Language = (ELanguage) STRG.ReadFourCC();
}
rSTRG.Seek(0x4, SEEK_CUR); // Skipping total string size
for (uint LanguageIdx = 0; LanguageIdx < NumLanguages; LanguageIdx++)
{
LanguageOffsets[LanguageIdx].resize(NumStrings);
STRG.Skip(4); // Skipping total string size
for (uint32 iStr = 0; iStr < NumStrings; iStr++)
LangOffsets[iLang][iStr] = rSTRG.ReadLong();
for (uint StringIdx = 0; StringIdx < NumStrings; StringIdx++)
{
LanguageOffsets[LanguageIdx][StringIdx] = STRG.ReadLong();
}
}
// Strings
uint32 StringsStart = rSTRG.Tell();
uint StringsStart = STRG.Tell();
for (uint32 iLang = 0; iLang < NumLanguages; iLang++)
for (uint LanguageIdx = 0; LanguageIdx < NumLanguages; LanguageIdx++)
{
CStringTable::SLangTable *pLang = &mpStringTable->mLangTables[iLang];
pLang->Strings.resize(NumStrings);
CStringTable::SLanguageData& Language = mpStringTable->mLanguages[LanguageIdx];
Language.Strings.resize(NumStrings);
for (uint32 iStr = 0; iStr < NumStrings; iStr++)
for (uint StringIdx = 0; StringIdx < NumStrings; StringIdx++)
{
rSTRG.Seek(StringsStart + LangOffsets[iLang][iStr], SEEK_SET);
rSTRG.Seek(0x4, SEEK_CUR); // Skipping string size
pLang->Strings[iStr] = rSTRG.ReadString();
STRG.GoTo( StringsStart + LanguageOffsets[LanguageIdx][StringIdx] );
STRG.Skip(4); // Skipping string size
Language.Strings[StringIdx] = STRG.ReadString();
}
}
}
void CStringLoader::LoadNameTable(IInputStream& rSTRG)
void CStringLoader::LoadNameTable(IInputStream& STRG)
{
// Name table header
uint32 NameCount = rSTRG.ReadLong();
uint32 NameTableSize = rSTRG.ReadLong();
uint32 NameTableStart = rSTRG.Tell();
uint32 NameTableEnd = NameTableStart + NameTableSize;
uint NameCount = STRG.ReadLong();
uint NameTableSize = STRG.ReadLong();
uint NameTableStart = STRG.Tell();
uint NameTableEnd = NameTableStart + NameTableSize;
// Name definitions
struct SNameDef {
uint32 NameOffset, StringIndex;
uint NameOffset, StringIndex;
};
std::vector<SNameDef> NameDefs(NameCount);
for (uint32 iName = 0; iName < NameCount; iName++)
// Keep track of max string index so we can size the names array appropriately.
// Note that it is possible that not every string in the table has a name.
int MaxIndex = -1;
for (uint NameIdx = 0; NameIdx < NameCount; NameIdx++)
{
NameDefs[iName].NameOffset = rSTRG.ReadLong() + NameTableStart;
NameDefs[iName].StringIndex = rSTRG.ReadLong();
NameDefs[NameIdx].NameOffset = STRG.ReadLong() + NameTableStart;
NameDefs[NameIdx].StringIndex = STRG.ReadLong();
MaxIndex = Math::Max(MaxIndex, (int) NameDefs[NameIdx].StringIndex);
}
// Name strings
mpStringTable->mStringNames.resize(mpStringTable->mNumStrings);
for (uint32 iName = 0; iName < NameCount; iName++)
mpStringTable->mStringNames.resize(MaxIndex + 1);
for (uint NameIdx = 0; NameIdx < NameCount; NameIdx++)
{
SNameDef *pDef = &NameDefs[iName];
rSTRG.Seek(pDef->NameOffset, SEEK_SET);
mpStringTable->mStringNames[pDef->StringIndex] = rSTRG.ReadString();
SNameDef& NameDef = NameDefs[NameIdx];
STRG.GoTo(NameDef.NameOffset);
mpStringTable->mStringNames[NameDef.StringIndex] = STRG.ReadString();
}
rSTRG.Seek(NameTableEnd, SEEK_SET);
STRG.GoTo(NameTableEnd);
}
// ************ STATIC ************
CStringTable* CStringLoader::LoadSTRG(IInputStream& rSTRG, CResourceEntry *pEntry)
CStringTable* CStringLoader::LoadSTRG(IInputStream& STRG, CResourceEntry* pEntry)
{
// Verify that this is a valid STRG
if (!rSTRG.IsValid()) return nullptr;
if (!STRG.IsValid()) return nullptr;
uint32 Magic = rSTRG.ReadLong();
// Verify that this is a valid STRG
uint Magic = STRG.ReadLong();
EGame Version = EGame::Invalid;
if (Magic != 0x87654321)
{
// Check for MP1 Demo STRG format - no magic/version; the first value is actually the filesize
// so the best I can do is verify the first value actually points to the end of the file
if (Magic <= (uint32) rSTRG.Size())
// so the best I can do is verify the first value actually points to the end of the file.
// The file can have up to 31 padding bytes at the end so we account for that
if (Magic <= (uint) STRG.Size() && Magic > STRG.Size() - 32)
{
rSTRG.Seek(Magic, SEEK_SET);
if ((rSTRG.EoF()) || (rSTRG.ReadShort() == 0xFFFF))
Version = EGame::PrimeDemo;
Version = EGame::PrimeDemo;
}
// If not, then we seem to have an invalid file...
if (Version != EGame::PrimeDemo)
{
errorf("%s: Invalid STRG magic: 0x%08X", *rSTRG.GetSourceString(), Magic);
errorf("%s: Invalid STRG magic: 0x%08X", *STRG.GetSourceString(), Magic);
return nullptr;
}
}
else
{
uint32 FileVersion = rSTRG.ReadLong();
uint FileVersion = STRG.ReadLong();
Version = GetFormatVersion(FileVersion);
if (Version == EGame::Invalid)
{
errorf("%s: Unsupported STRG version: 0x%X", *rSTRG.GetSourceString(), FileVersion);
errorf("%s: Unrecognized STRG version: 0x%X", *STRG.GetSourceString(), FileVersion);
return nullptr;
}
}
@@ -197,9 +218,9 @@ CStringTable* CStringLoader::LoadSTRG(IInputStream& rSTRG, CResourceEntry *pEntr
Loader.mpStringTable = new CStringTable(pEntry);
Loader.mVersion = Version;
if (Version == EGame::PrimeDemo) Loader.LoadPrimeDemoSTRG(rSTRG);
else if (Version < EGame::Corruption) Loader.LoadPrimeSTRG(rSTRG);
else Loader.LoadCorruptionSTRG(rSTRG);
if (Version == EGame::PrimeDemo) Loader.LoadPrimeDemoSTRG(STRG);
else if (Version < EGame::Corruption) Loader.LoadPrimeSTRG(STRG);
else Loader.LoadCorruptionSTRG(STRG);
return Loader.mpStringTable;
}

View File

@@ -12,14 +12,14 @@ class CStringLoader
EGame mVersion;
CStringLoader() {}
void LoadPrimeDemoSTRG(IInputStream& rSTRG);
void LoadPrimeSTRG(IInputStream& rSTRG);
void LoadCorruptionSTRG(IInputStream& rSTRG);
void LoadNameTable(IInputStream& rSTRG);
void LoadPrimeDemoSTRG(IInputStream& STRG);
void LoadPrimeSTRG(IInputStream& STRG);
void LoadCorruptionSTRG(IInputStream& STRG);
void LoadNameTable(IInputStream& STRG);
public:
static CStringTable* LoadSTRG(IInputStream &rSTRG, CResourceEntry *pEntry);
static EGame GetFormatVersion(uint32 Version);
static CStringTable* LoadSTRG(IInputStream& STRG, CResourceEntry* pEntry);
static EGame GetFormatVersion(uint Version);
};
#endif // CSTRINGLOADER_H

View File

@@ -36,11 +36,11 @@ const ELanguage gkSupportedLanguagesDKCR[] =
};
// Utility function - retrieve the index of a given language
static int FindLanguageIndex(CStringTable* pInTable, ELanguage InLanguage)
static int FindLanguageIndex(const CStringTable* pkInTable, ELanguage InLanguage)
{
for (uint LanguageIdx = 0; LanguageIdx < pInTable->NumLanguages(); LanguageIdx++)
for (uint LanguageIdx = 0; LanguageIdx < pkInTable->NumLanguages(); LanguageIdx++)
{
if (pInTable->LanguageByIndex(LanguageIdx) == InLanguage)
if (pkInTable->LanguageByIndex(LanguageIdx) == InLanguage)
{
return LanguageIdx;
}
@@ -155,7 +155,7 @@ CDependencyTree* CStringTable::BuildDependencyTree() const
// Determine which params are textures based on image type
TStringList Params = ParamString.Split(",");
TString ImageType = Params.front();
int TexturesStart = -1;
uint TexturesStart = 0;
if (ImageType == "A")
TexturesStart = 2;
@@ -265,7 +265,7 @@ bool CStringTable::IsLanguageSupported(ELanguage Language, EGame Game, ERegion R
pkSupportedLanguages = &gkSupportedLanguagesDKCR[0];
NumLanguages = ARRAY_SIZE( gkSupportedLanguagesDKCR );
}
ASSERT(pkSupportedLangages);
ASSERT(pkSupportedLanguages);
ASSERT(NumLanguages > 0);
// Check if the requested language is in the array.

View File

@@ -8,7 +8,9 @@
#include <Common/TString.h>
#include <vector>
/** A table of localized strings from STRG assets */
/** A table of localized strings from STRG assets.
* Strings are always internally stored as UTF-8.
*/
class CStringTable : public CResource
{
DECLARE_RESOURCE_TYPE(StringTable)

View File

@@ -15,7 +15,7 @@ enum class ELanguage
Italian = FOURCC('ITAL'),
Dutch = FOURCC('DUTC'), // Unused
Japanese = FOURCC('JAPN'),
// The rest of these languages were added in Metroid Prime 3
// The rest of these languages were added in Donkey Kong Country Returns
SimplifiedChinese = FOURCC('SCHN'), // Unused
TraditionalChinese = FOURCC('TCHN'), // Unused
UKEnglish = FOURCC('UKEN'),