Skip to content

Commit

Permalink
Confirmed support of all levels to read & write (validation passed)
Browse files Browse the repository at this point in the history
  • Loading branch information
DronCode committed Jun 11, 2024
1 parent a774f9a commit 50927c3
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 25 deletions.
3 changes: 2 additions & 1 deletion BMEdit/GameLib/Include/GameLib/LOC/LOCTreeNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ namespace gamelib::loc
EMPTY_BLOCK = 0x8, ///< Empty chunk, no data at all
LOCALIZED_STRING = 0x9, ///< Key value (string to aligned string)
CHILDREN = 0x10, ///< Container (amount & list of offsets)
SUBTITLES_FIN = 0x28, ///< Subtitles finish string. NOTE: Maybe it's finish subtitle, will rename it later
SUBTITLES_HINT_WITH_COMMENT = 0x0B, ///< Some subtitle bullshit with comment (key, value string, comment aligned CString)
SUBTITLES_FIN = 0x28, ///< Subtitles finish string
SUBTITLES = 0x29, ///< Subtitles value (long text with extra parameters) | 0x20 mask means that extra data exists
SUBTITLES_HINT = 0x2B ///< Another subtitles data with extra string hint
};
Expand Down
90 changes: 66 additions & 24 deletions BMEdit/GameLib/Source/GameLib/LOC/LOCTreeNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ namespace gamelib::loc
type == LOCTreeNodeType::LOCALIZED_STRING ||
type == LOCTreeNodeType::SUBTITLES ||
type == LOCTreeNodeType::SUBTITLES_HINT ||
type == LOCTreeNodeType::SUBTITLES_FIN;
type == LOCTreeNodeType::SUBTITLES_FIN ||
type == LOCTreeNodeType::SUBTITLES_HINT_WITH_COMMENT;
}

bool LOCTreeNode::canHaveChildren() const
Expand All @@ -30,7 +31,8 @@ namespace gamelib::loc
const auto type = binaryReader->read<int8_t, ZBio::Endianness::LE>();
if (type != LOCTreeNodeType::CHILDREN && type != LOCTreeNodeType::LOCALIZED_STRING &&
type != LOCTreeNodeType::SUBTITLES && type != LOCTreeNodeType::EMPTY_BLOCK &&
type != LOCTreeNodeType::SUBTITLES_FIN && type != LOCTreeNodeType::SUBTITLES_HINT)
type != LOCTreeNodeType::SUBTITLES_FIN && type != LOCTreeNodeType::SUBTITLES_HINT &&
type != LOCTreeNodeType::SUBTITLES_HINT_WITH_COMMENT)
{
auto errorMessage = fmt::format("Invalid LOC format: unexpected entity code 0x{:02X} at offset {} (0x{:X})", type, binaryReader->tell(), binaryReader->tell());
throw std::runtime_error(errorMessage);
Expand Down Expand Up @@ -80,38 +82,64 @@ namespace gamelib::loc
ZBioHelpers::seekBy(binaryReader, 4); // Need seek by 4 because value strings are "aligned".
// Formula: len + 1 + 4 (+1 - zero terminator, 4 - "alignment")
}
else if (node->type == LOCTreeNodeType::SUBTITLES || node->type == LOCTreeNodeType::SUBTITLES_HINT || node->type == LOCTreeNodeType::SUBTITLES_FIN)
else if (node->type == LOCTreeNodeType::SUBTITLES)
{
// Subtitles text
node->value = binaryReader->readCString();

if (node->type == LOCTreeNodeType::SUBTITLES_HINT)
// Read subtitle data. In most cases there are 2xu32, but when first u32 zeroed next u32 not presented
// I love IOI because they don't give me an opportunity to relax...
uint32_t first = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
uint32_t second = 0;
if (first != 0)
{
// Read extra hint string
node->subtitle.extraHint = binaryReader->readCString();
// Ok, read second
second = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
}

if (node->type == LOCTreeNodeType::SUBTITLES || node->type == LOCTreeNodeType::SUBTITLES_HINT)
{
// Read subtitle data. In most cases there are 2xu32, but when first u32 zeroed next u32 not presented
// I love IOI because they don't give me an opportunity to relax...
uint32_t first = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
uint32_t second = 0;
if (first != 0)
{
// Ok, read second
second = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
}
*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[0]) = first;
*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[4]) = second;
}
else if (node->type == LOCTreeNodeType::SUBTITLES_HINT)
{
// Subtitles text
node->value = binaryReader->readCString();

*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[0]) = first;
*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[4]) = second;
}
// Read extra hint string
node->subtitle.extraHint = binaryReader->readCString();

if (node->type == LOCTreeNodeType::SUBTITLES_FIN)
// Read subtitle data. In most cases there are 2xu32, but when first u32 zeroed next u32 not presented
// I love IOI because they don't give me an opportunity to relax...
uint32_t first = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
uint32_t second = 0;
if (first != 0)
{
// Always only 1 u32
binaryReader->read<uint8_t, ZBio::Endianness::LE>(&node->subtitle.unkData[0], 4);
// Ok, read second
second = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
}

*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[0]) = first;
*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[4]) = second;
}
else if (node->type == LOCTreeNodeType::SUBTITLES_FIN)
{
// Always only 1 u32
uint32_t first = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[0]) = first;
}
else if (node->type == LOCTreeNodeType::SUBTITLES_HINT_WITH_COMMENT)
{
// Read value
node->value = binaryReader->readCString();

// Read hint (?)
node->subtitle.extraHint = binaryReader->readCString();

// DronCode: I'm not sure that next few bytes always zeroed.
// As we remember in LOCTreeNodeType::SUBTITLES & LOCTreeNodeType::SUBTITLES_HINT we have extra 8 bytes (2xu32 but sometimes only 1xu32 when it's zeroed).
// This could be our case. Idk, let's use code from SUBTITLES_FIN (idk why)
uint32_t first = binaryReader->read<uint32_t, ZBio::Endianness::LE>();
*reinterpret_cast<uint32_t*>(&node->subtitle.unkData[0]) = first;
}
else if (node->type == LOCTreeNodeType::EMPTY_BLOCK)
{
Expand Down Expand Up @@ -193,10 +221,24 @@ namespace gamelib::loc
if (first)
binaryWriter->write<uint32_t, ZBio::Endianness::LE>(second);
}
else if (node->type == LOCTreeNodeType::SUBTITLES_HINT_WITH_COMMENT)
{
// Write value
binaryWriter->writeCString(node->value);

// Write hint
binaryWriter->writeCString(node->subtitle.extraHint);

// Write u32 (see deserializer code for details)
uint32_t first = *reinterpret_cast<uint32_t*>(&node->subtitle.unkData[0]);
binaryWriter->write<uint32_t, ZBio::Endianness::LE>(first);
}
else if (node->type == LOCTreeNodeType::SUBTITLES_FIN)
{
// Store tutorial data here
binaryWriter->write<uint8_t, ZBio::Endianness::LE>(&node->subtitle.unkData[0], 4);
uint32_t first = *reinterpret_cast<uint32_t*>(&node->subtitle.unkData[0]);

binaryWriter->write<uint32_t, ZBio::Endianness::LE>(first);
}
else if (node->type == LOCTreeNodeType::EMPTY_BLOCK)
{
Expand Down

0 comments on commit 50927c3

Please sign in to comment.