diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 30ceadf..ef1d5bc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,6 +56,7 @@ SET(mst06_SRCS SET(mst06_H byteorder.h byteswap.h + common.h mst_structs.h Mst.hpp TextFuncs.hpp diff --git a/src/Mst.cpp b/src/Mst.cpp index 909fb52..d2d036f 100644 --- a/src/Mst.cpp +++ b/src/Mst.cpp @@ -43,6 +43,9 @@ using std::vector; #include using namespace tinyxml2; +// Invalid offset value. +#define INVALID_OFFSET ~0U + Mst::Mst() : m_version('1') , m_isBigEndian(true) @@ -53,7 +56,7 @@ Mst::Mst() * @param ppDiffOffTbl [in/out] Pointer to current pointer into the differential offset table. * This is adjusted based on the differential offset data. * @param pDiffOffTblEnd [in] Pointer to the end of the differential offset table. - * @return Offset value, or ~0U if end of table. + * @return Offset value, or INVALID_OFFSET if end of table. */ uint32_t Mst::getNextDiffOff(const uint8_t **ppDiffOffTbl, const uint8_t *const pDiffOffTblEnd) { @@ -61,7 +64,7 @@ uint32_t Mst::getNextDiffOff(const uint8_t **ppDiffOffTbl, const uint8_t *const switch (**ppDiffOffTbl >> 6) { case 0: // 0 bits long. End of offset table. - offset_diff = ~0U; + offset_diff = INVALID_OFFSET; break; case 1: // 6 bits long. @@ -77,7 +80,7 @@ uint32_t Mst::getNextDiffOff(const uint8_t **ppDiffOffTbl, const uint8_t *const if (*ppDiffOffTbl + 2 >= pDiffOffTblEnd) { // Out of bounds! // TODO: Store more comprehensive error information. - offset_diff = ~0U; + offset_diff = INVALID_OFFSET; break; } offset_diff = ((*ppDiffOffTbl[0] & 0x3F) << 10) | @@ -92,7 +95,7 @@ uint32_t Mst::getNextDiffOff(const uint8_t **ppDiffOffTbl, const uint8_t *const if (*ppDiffOffTbl + 4 >= pDiffOffTblEnd) { // Out of bounds! // TODO: Store more comprehensive error information. - offset_diff = ~0U; + offset_diff = INVALID_OFFSET; break; } offset_diff = ((*ppDiffOffTbl[0] & 0x3F) << 26) | @@ -273,14 +276,14 @@ int Mst::loadMST(FILE *fp) for (const WTXT_MsgPointer *p = pOffTbl; p < pOffTblEnd; p++, idx++) { WTXT_MsgPointer ptr = *p; if (!hostMatchesFileEndianness) { - ptr.msg_id_name_offset = __swab32(ptr.msg_id_name_offset); - ptr.msg_offset = __swab32(ptr.msg_offset); + ptr.name_offset = __swab32(ptr.name_offset); + ptr.text_offset = __swab32(ptr.text_offset); ptr.placeholder_offset = __swab32(ptr.placeholder_offset); } - const char *const pMsgName = reinterpret_cast(&pOffTblU8[ptr.msg_id_name_offset]); + const char *const pMsgName = reinterpret_cast(&pOffTblU8[ptr.name_offset]); // TODO: Verify alignment. - const char16_t *pMsgText = reinterpret_cast(&pOffTblU8[ptr.msg_offset]); + const char16_t *pMsgText = reinterpret_cast(&pOffTblU8[ptr.text_offset]); const char *const pPlaceholderName = (ptr.placeholder_offset != 0 ? reinterpret_cast(&pOffTblU8[ptr.placeholder_offset]) @@ -619,9 +622,6 @@ int Mst::saveMST(FILE *fp) const { if (m_vStrTbl.empty()) { return -ENODATA; // TODO: Better error code? - } else if (m_vDiffOffTbl.empty()) { - // FIXME: How to generate differential offsets properly? - return -EIO; // TODO: Better error code? } // MST header. @@ -644,36 +644,30 @@ int Mst::saveMST(FILE *fp) const // NOTE: vOffsetTbl will have offsets relative to the // beginning of each vMsgNames and vMsgText in the first run. // The base addresses will be added in the second run. - vector vOffsetTbl; // Primary offset table. - vector vOffsetTblType; // Type of entry for each offset: 0=zero, 1=text, 2=name + vector vOffsetTbl; // Primary offset table. + // NOTE: If any field contains INVALID_OFFSET, + // it should be written as an actual zero. vector vMsgText; // Message text. vector vMsgNames; // Message names. - const uint8_t *pDiffOffTbl = m_vDiffOffTbl.data(); // Differential offset table. - const uint8_t *const pDiffOffTblEnd = pDiffOffTbl + m_vDiffOffTbl.size(); + + // Differential offset table. + // This usually consists of 'AB' for strings with names and text, + // or 'AAA' for strings with names, text, and placeholders. + vector vDiffOffTbl; // String deduplication for vMsgNames. // TODO: Do we need to deduplicate *all* strings, or just the string table name. unordered_map map_nameDedupe; // TODO: Better size reservations. - vOffsetTbl.reserve(m_vStrTbl.size() * 3); - vOffsetTblType.reserve(m_vStrTbl.size() * 3); + vOffsetTbl.reserve(m_vStrTbl.size()); vMsgText.reserve(m_vStrTbl.size() * 32); vMsgNames.reserve(m_vStrTbl.size() * 32); - - // For strings with both name and text, three offsets will be stored: - // - Name offset - // - Text offset - // - Zero - - // For strings with only name, only the name offset will be stored. - // TODO: Compare to MST files and see if maybe a zero is stored too? - uint32_t offset_diff; + vDiffOffTbl.reserve(((((m_vStrTbl.size() * 2) + m_mapPlaceholder.size())) + 3) & ~(size_t)(3U)); // String table name. // NOTE: While this is part of the names table, the offset is stored - // in the WTXT header, *not* the offset table, and it's not included - // in the differential offset table. + // in the WTXT header, *not* the offset table. { if (!m_name.empty()) { const size_t name_size = m_name.size(); @@ -694,26 +688,20 @@ int Mst::saveMST(FILE *fp) const // Differential offset table initialization: // - 'A': Skip "WTXT" // - 'B': Skip string table name offset and count. - offset_diff = getNextDiffOff(&pDiffOffTbl, pDiffOffTblEnd); - if (offset_diff != 4) { - // Invalid differential offset for the string table name... - return -EIO; // TODO: Better error code? - } - offset_diff = getNextDiffOff(&pDiffOffTbl, pDiffOffTblEnd); - if (offset_diff != 8) { - // Invalid differential offset for the string table name... - return -EIO; // TODO: Better error code? - } + vDiffOffTbl.push_back('A'); + vDiffOffTbl.push_back('B'); } // Host endianness. static const bool hostIsBigEndian = (SYS_BYTEORDER == SYS_BIG_ENDIAN); const bool hostMatchesFileEndianness = (hostIsBigEndian == m_isBigEndian); - size_t i = 0; - for (auto iter = m_vStrTbl.cbegin(); iter != m_vStrTbl.cend(); ++iter, ++i) { - size_t name_pos = vMsgNames.size(); - const size_t text_pos = vMsgText.size(); + size_t idx = 0; + for (auto iter = m_vStrTbl.cbegin(); iter != m_vStrTbl.cend(); ++iter, ++idx) { + WTXT_MsgPointer ptr; + ptr.name_offset = INVALID_OFFSET; + ptr.text_offset = INVALID_OFFSET; + ptr.placeholder_offset = INVALID_OFFSET; // Copy the message name. if (!iter->first.empty()) { @@ -723,9 +711,10 @@ int Mst::saveMST(FILE *fp) const auto map_iter = map_nameDedupe.find(iter->first); if (map_iter != map_nameDedupe.end()) { // Found the string. - name_pos = map_iter->second; + ptr.name_offset = static_cast(map_iter->second); } else { // String not found, so cannot dedupe. + ptr.name_offset = static_cast(vMsgNames.size()); // Convert to Shift-JIS first. // TODO: Show warnings for strings with characters that @@ -734,46 +723,31 @@ int Mst::saveMST(FILE *fp) const // Copy the message name into the vector. const size_t name_size = sjis_str.size(); // +1 for NULL terminator. - vMsgNames.resize(name_pos + name_size + 1); - memcpy(&vMsgNames[name_pos], sjis_str.c_str(), name_size+1); + vMsgNames.resize(ptr.name_offset + name_size + 1); + memcpy(&vMsgNames[ptr.name_offset], sjis_str.c_str(), name_size+1); // Add the string to the deduplication map. - map_nameDedupe.insert(std::make_pair(iter->first, name_pos)); + map_nameDedupe.insert(std::make_pair(iter->first, ptr.name_offset)); } } else { // Empty message name... // TODO: Report a warning. char buf[64]; - int len = snprintf(buf, sizeof(buf), "XXX_MSG_%zu", i); + int len = snprintf(buf, sizeof(buf), "XXX_MSG_%zu", idx); // +1 for NULL terminator. - vMsgNames.resize(name_pos + len + 1); - memcpy(&vMsgNames[name_pos], buf, len+1); - } - assert(name_pos <= 16U*1024*1024); - vOffsetTbl.push_back(static_cast(name_pos)); - vOffsetTblType.push_back(2); // Name - - // If the next differential offset is >4, skip some bytes in the offset table. - // NOTE: Offset is always a multiple of 4. - offset_diff = getNextDiffOff(&pDiffOffTbl, pDiffOffTblEnd); - /* TODO: Handle this? - if (offset_diff < 4) { - // Shouldn't be less than 4! - return -EIO; // TODO: Better error code? - } */ - if (offset_diff == ~0U) { - // End of offset table. - break; - } - for (; offset_diff > 4; offset_diff -= 4) { - vOffsetTbl.push_back(0); - vOffsetTblType.push_back(0); // Zero + vMsgNames.resize(ptr.name_offset + len + 1); + memcpy(&vMsgNames[ptr.name_offset], buf, len+1); } + assert(ptr.name_offset <= 16U*1024*1024); // Copy the message text. // TODO: Add support for writing little-endian files? if (!iter->second.empty()) { // Copy the message text into the vector. + // NOTE: c16pos is in units of char16_t, whereas + // ptr.text_offset is in bytes. + const uint32_t c16pos = static_cast(vMsgText.size()); + ptr.text_offset = c16pos * sizeof(char16_t); u16string msg_text; if (hostMatchesFileEndianness) { // Host endianness matches file endianness. @@ -787,41 +761,77 @@ int Mst::saveMST(FILE *fp) const } const size_t msg_size = msg_text.size(); - vMsgText.resize(text_pos + msg_size + 1); + vMsgText.resize(c16pos + msg_size + 1); // +1 for NULL terminator. - memcpy(&vMsgText[text_pos], msg_text.c_str(), (msg_size+1) * sizeof(char16_t)); + memcpy(&vMsgText[c16pos], msg_text.c_str(), (msg_size+1) * sizeof(char16_t)); - // NOTE: Text offset must be mutliplied by sizeof(char16_t), + // NOTE: Text offset must be multiplied by sizeof(char16_t), // since vMsgText is char16_t, but vOffsetTbl uses bytes. - assert(text_pos <= 16U*1024*1024); - vOffsetTbl.push_back(static_cast(text_pos * sizeof(char16_t))); - vOffsetTblType.push_back(1); // Text + assert(ptr.text_offset <= 16U*1024*1024); + } - // If the next differential offset is >4, skip some bytes in the offset table. - // NOTE: Offset is always a multiple of 4. - offset_diff = getNextDiffOff(&pDiffOffTbl, pDiffOffTblEnd); - /* TODO: Handle this? - if (offset_diff < 4) { - // Shouldn't be less than 4! - return -EIO; // TODO: Better error code? - } */ - if (offset_diff == ~0U) { - // End of offset table. - break; - } - for (; offset_diff > 4; offset_diff -= 4) { - vOffsetTbl.push_back(0); - vOffsetTblType.push_back(0); // Zero + // Do we have a placeholder name? + auto plc_iter = m_mapPlaceholder.find(idx); + if (plc_iter != m_mapPlaceholder.end()) { + // Is the name already present? + // This usually occurs if a string has the same name as the string table. + // TODO: Do we need to deduplicate *all* strings, or just the string table name. + auto map_iter = map_nameDedupe.find(plc_iter->second); + if (map_iter != map_nameDedupe.end()) { + // Found the string. + ptr.placeholder_offset = static_cast(map_iter->second); + } else { + // String not found, so cannot dedupe. + ptr.placeholder_offset = static_cast(vMsgNames.size()); + + // Convert to Shift-JIS first. + // TODO: Show warnings for strings with characters that + // can't be converted to Shift-JIS? + const string sjis_str = utf8_to_cpN(932, plc_iter->second.data(), (int)plc_iter->second.size()); + // Copy the message name into the vector. + const size_t name_size = sjis_str.size(); + // +1 for NULL terminator. + vMsgNames.resize(ptr.placeholder_offset + name_size + 1); + memcpy(&vMsgNames[ptr.placeholder_offset], sjis_str.c_str(), name_size+1); + + // Add the string to the deduplication map. + map_nameDedupe.insert(std::make_pair(plc_iter->second, ptr.placeholder_offset)); } } + + // Add differential offset values. + assert(ptr.name_offset != INVALID_OFFSET); + assert(ptr.text_offset != INVALID_OFFSET); + if (ptr.name_offset != INVALID_OFFSET && ptr.text_offset != INVALID_OFFSET) { + if (ptr.placeholder_offset != INVALID_OFFSET) { + // Placeholder name is present. + vDiffOffTbl.push_back('A'); + vDiffOffTbl.push_back('A'); + vDiffOffTbl.push_back('A'); + } else { + // Placeholder name is NOT present. + vDiffOffTbl.push_back('A'); + vDiffOffTbl.push_back('B'); + } + } else { + // ERROR: Name and text must be present... + // TODO: More comprehensive error reporting. + return -EIO; + } + + // Add the offsets to the offset table. + vOffsetTbl.push_back(ptr); } - // Offset table always ends with an extra zero. - vOffsetTbl.push_back(0); - vOffsetTblType.push_back(0); // Zero + // Remove the last differential offset table entry, + // since it's EOF. + assert(!vDiffOffTbl.empty()); + if (!vDiffOffTbl.empty()) { + vDiffOffTbl.resize(vDiffOffTbl.size()-1); + } // Determine the message table base addresses. - const uint32_t text_tbl_base = static_cast(sizeof(wtxt_header) + (vOffsetTbl.size() * sizeof(uint32_t))); + const uint32_t text_tbl_base = static_cast(sizeof(wtxt_header) + (vOffsetTbl.size() * sizeof(WTXT_MsgPointer))); const uint32_t name_tbl_base = static_cast(text_tbl_base + (vMsgText.size() * sizeof(char16_t))); // Differential offset table must be DWORD-aligned for both @@ -832,38 +842,43 @@ int Mst::saveMST(FILE *fp) const vMsgNames.resize(vMsgNames.size() + (4 - (doff_tbl_offset & 3))); doff_tbl_offset = static_cast(name_tbl_base + vMsgNames.size()); } - const uint32_t doff_tbl_length = static_cast(m_vDiffOffTbl.size()); + uint32_t doff_tbl_length = static_cast(vDiffOffTbl.size()); + if (doff_tbl_length & 3) { + // Need to align the size to a multiple of 4. + vDiffOffTbl.resize((vDiffOffTbl.size() + 3) & ~(size_t)(3U)); + doff_tbl_length = static_cast(vDiffOffTbl.size()); + } // Update WTXT_Header. wtxt_header.msg_tbl_name_offset = cpu_to_be32(name_tbl_base); - // TODO: Make sure the offset table's size is a multiple of 3 uint32_t's. (12 bytes) - const uint32_t msg_tbl_count = static_cast(vOffsetTbl.size() / 3); + const uint32_t msg_tbl_count = static_cast(vOffsetTbl.size()); wtxt_header.msg_tbl_count = cpu_to_be32(msg_tbl_count); // Update the offset table base addresses. - i = 0; - for (auto iter = vOffsetTbl.begin(); iter != vOffsetTbl.end(); ++iter, ++i) { - switch (vOffsetTblType[i]) { - case 0: - // Zero entry. - break; - case 1: - // Text entry. - *iter += text_tbl_base; - break; - case 2: - // Name entry. - *iter += name_tbl_base; - break; - default: - // Invalid entry. - assert(!"Unexpected offset table type."); - break; + for (auto iter = vOffsetTbl.begin(); iter != vOffsetTbl.end(); ++iter) { + if (iter->name_offset == INVALID_OFFSET) { + iter->name_offset = 0; + } else { + iter->name_offset += name_tbl_base; + } + + if (iter->text_offset == INVALID_OFFSET) { + iter->text_offset = 0; + } else { + iter->text_offset += text_tbl_base; + } + + if (iter->placeholder_offset == INVALID_OFFSET) { + iter->placeholder_offset = 0; + } else { + iter->placeholder_offset += name_tbl_base; } if (!hostMatchesFileEndianness) { - // Byteswap the offset. - *iter = __swab32(*iter); + // Byteswap the offsets. + iter->name_offset = __swab32(iter->name_offset); + iter->text_offset = __swab32(iter->text_offset); + iter->placeholder_offset = __swab32(iter->placeholder_offset); } } @@ -892,7 +907,7 @@ int Mst::saveMST(FILE *fp) const return (errno ? -errno : -EIO); } errno = 0; - size = fwrite(vOffsetTbl.data(), sizeof(uint32_t), vOffsetTbl.size(), fp); + size = fwrite(vOffsetTbl.data(), sizeof(WTXT_MsgPointer), vOffsetTbl.size(), fp); if (size != vOffsetTbl.size()) { return (errno ? -errno : -EIO); } @@ -907,8 +922,8 @@ int Mst::saveMST(FILE *fp) const return (errno ? -errno : -EIO); } errno = 0; - size = fwrite(m_vDiffOffTbl.data(), 1, m_vDiffOffTbl.size(), fp); - if (size != m_vDiffOffTbl.size()) { + size = fwrite(vDiffOffTbl.data(), 1, vDiffOffTbl.size(), fp); + if (size != vDiffOffTbl.size()) { return (errno ? -errno : -EIO); } @@ -999,12 +1014,12 @@ int Mst::saveXML(FILE *fp) const xml_mst06->InsertEndChild(xml_msg); xml_msg->SetAttribute("index", static_cast(idx)); xml_msg->SetAttribute("name", iter->first.c_str()); - // TODO: Make sure that offset_diff isn't ~0U. + // TODO: Make sure that offset_diff isn't INVALID_OFFSET. offset_diff = getNextDiffOff(&pDiffOffTbl, pDiffOffTblEnd); if (!iter->second.empty()) { xml_msg->SetText(escape(utf16_to_utf8(iter->second)).c_str()); - // TODO: Make sure that offset_diff isn't ~0U. + // TODO: Make sure that offset_diff isn't INVALID_OFFSET. offset_diff = getNextDiffOff(&pDiffOffTbl, pDiffOffTblEnd); } @@ -1013,7 +1028,7 @@ int Mst::saveXML(FILE *fp) const if (plc_iter != m_mapPlaceholder.end()) { // Save the placeholder text as an attribute. xml_msg->SetAttribute("placeholder", escape(plc_iter->second).c_str()); - // TODO: Make sure that offset_diff isn't ~0U. + // TODO: Make sure that offset_diff isn't INVALID_OFFSET. offset_diff = getNextDiffOff(&pDiffOffTbl, pDiffOffTblEnd); } @@ -1349,7 +1364,7 @@ int Mst::unescapeDiffOffTbl(std::vector &vDiffOffTbl, const char *s_dif buf[0] = s_diffOffTbl[2]; buf[1] = s_diffOffTbl[3]; buf[2] = 0; - vDiffOffTbl.push_back(strtoul(buf, nullptr, 16)); + vDiffOffTbl.push_back(static_cast(strtoul(buf, nullptr, 16))); s_diffOffTbl += 3; } break; diff --git a/src/common.h b/src/common.h new file mode 100644 index 0000000..57ca207 --- /dev/null +++ b/src/common.h @@ -0,0 +1,127 @@ +/*************************************************************************** + * MST Decoder/Encoder for Sonic '06 * + * common.h: Common types and macros. * + * * + * Copyright (c) 2016-2018 by David Korth. * + * SPDX-License-Identifier: GPL-2.0-or-later * + ***************************************************************************/ + +#ifndef __MST06_LIBRPBASE_COMMON_H__ +#define __MST06_LIBRPBASE_COMMON_H__ + +#ifdef __cplusplus +# include +#else +# include +#endif + +/** + * Number of elements in an array. + * + * Includes a static check for pointers to make sure + * a dynamically-allocated array wasn't specified. + * Reference: http://stackoverflow.com/questions/8018843/macro-definition-array-size + */ +#define ARRAY_SIZE(x) \ + ((int)(((sizeof(x) / sizeof(x[0]))) / \ + (size_t)(!(sizeof(x) % sizeof(x[0]))))) + +// PACKED struct attribute. +// Use in conjunction with #pragma pack(1). +#ifdef __GNUC__ +# define PACKED __attribute__((packed)) +#else +# define PACKED +#endif + +/** + * static_asserts size of a structure + * Also defines a constant of form StructName_SIZE + */ +// TODO: Check MSVC support for static_assert() in C mode. +#if defined(__cplusplus) +# define ASSERT_STRUCT(st,sz) enum { st##_SIZE = (sz), }; \ + static_assert(sizeof(st)==(sz),#st " is not " #sz " bytes.") +#elif defined(__GNUC__) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L +# define ASSERT_STRUCT(st,sz) enum { st##_SIZE = (sz), }; \ + _Static_assert(sizeof(st)==(sz),#st " is not " #sz " bytes.") +#else +# define ASSERT_STRUCT(st, sz) +#endif + +// Deprecated function attribute. +#ifndef DEPRECATED +# if defined(__GNUC__) +# define DEPRECATED __attribute__ ((deprecated)) +# elif defined(_MSC_VER) +# define DEPRECATED __declspec(deprecated) +# else +# define DEPRECATED +# endif +#endif + +// Force inline attribute. +#if !defined(FORCEINLINE) +# if (!defined(_DEBUG) || defined(NDEBUG)) +# if defined(__GNUC__) +# define FORCEINLINE inline __attribute__((always_inline)) +# elif defined(_MSC_VER) +# define FORCEINLINE __forceinline +# else +# define FORCEINLINE inline +# endif +# else +# ifdef _MSC_VER +# define FORCEINLINE __inline +# else +# define FORCEINLINE inline +# endif +# endif +#endif /* !defined(FORCEINLINE) */ + +// gcc branch prediction hints. +// Should be used in combination with profile-guided optimization. +#ifdef __GNUC__ +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +#else +# define likely(x) x +# define unlikely(x) x +#endif + +// C99 restrict macro. +// NOTE: gcc only defines restrict in C, not C++, +// so use __restrict on both gcc and MSVC. +#define RESTRICT __restrict + +/** + * Alignment macro. + * @param a Alignment value. + * @param x Byte count to align. + */ +// FIXME: No __typeof__ in MSVC's C mode... +#if defined(_MSC_VER) && !defined(__cplusplus) +# define ALIGN(a, x) (((x)+((a)-1)) & ~((uint64_t)((a)-1))) +#else +# define ALIGN(a, x) (((x)+((a)-1)) & ~((__typeof__(x))((a)-1))) +#endif + +/** + * Alignment assertion macro. + */ +#define ASSERT_ALIGNMENT(a, ptr) assert(reinterpret_cast(ptr) % (a) == 0); + +/** + * Aligned variable macro. + * @param a Alignment value. + * @param decl Variable declaration. + */ +#if defined(__GNUC__) +# define ALIGNED_VAR(a, decl) decl __attribute__((aligned(a))) +#elif defined(_MSC_VER) +# define ALIGNED_VAR(a, decl) __declspec(align(a)) decl +#else +# error No aligned variable macro for this compiler. +#endif + +#endif /* __MST06_LIBRPBASE_COMMON_H__ */ diff --git a/src/mst_structs.h b/src/mst_structs.h index 2ae3e43..6d8e2b4 100644 --- a/src/mst_structs.h +++ b/src/mst_structs.h @@ -9,15 +9,8 @@ #ifndef __MST06_MST_STRUCTS_H__ #define __MST06_MST_STRUCTS_H__ -#ifndef PACKED -# ifdef __GNUC__ -# define PACKED __attribute__((packed)) -# else -# define PACKED -# endif -#endif /* !PACKED */ - #include +#include "common.h" #pragma pack(1) @@ -42,6 +35,9 @@ typedef struct PACKED _MST_Header { uint32_t bina_magic; // [0x018] 'BINA' uint32_t unk_zero4; // [0x01C] } MST_Header; +ASSERT_STRUCT(MST_Header, 32); + +#pragma pack() /** * WTXT header. @@ -51,22 +47,22 @@ typedef struct PACKED _MST_Header { * in the MST header. */ #define WTXT_MAGIC 'WTXT' -typedef struct PACKED _WTXT_Header { +typedef struct _WTXT_Header { uint32_t magic; // [0x000] 'WTXT' uint32_t msg_tbl_name_offset; // [0x004] Offset of message table name. uint32_t msg_tbl_count; // [0x008] Number of strings in the message table. } WTXT_Header; +ASSERT_STRUCT(WTXT_Header, 3*sizeof(uint32_t)); /** * Following WTXT_Header is an array of message pointers. * Messages are encded as UTF-16BE. */ -typedef struct PACKED _WTXT_MsgPointer { - uint32_t msg_id_name_offset; // [0x000] Offset of message name. (Shift-JIS) - uint32_t msg_offset; // [0x004] Offset of message. (UTF-16) +typedef struct _WTXT_MsgPointer { + uint32_t name_offset; // [0x000] Offset of message name. (Shift-JIS) + uint32_t text_offset; // [0x004] Offset of message text. (UTF-16) uint32_t placeholder_offset; // [0x008] If non-zero, offset of placeholder icon name. (Shift-JIS) } WTXT_MsgPointer; - -#pragma pack() +ASSERT_STRUCT(WTXT_MsgPointer, 3*sizeof(uint32_t)); #endif /* __MST06_MST_STRUCTS_H__ */