diff --git a/include/enums.h b/include/enums.h index d3f2e8ac..afafb338 100644 --- a/include/enums.h +++ b/include/enums.h @@ -46,7 +46,8 @@ typedef enum Vehicle { VEHICLE_BUBBLER, VEHICLE_WIZPIG, VEHICLE_ROCKET, - + + VEHICLE_NO_OVERRIDE = -1, // Only used in Object Maps for the setup point. NUMBER_OF_VEHICLE_TYPES, NUMBER_OF_PLAYER_VEHICLES = 3 } Vehicle; @@ -100,6 +101,20 @@ typedef enum ObjectModelType { OBJECT_MODEL_TYPE_MISC } ObjectModelType; +typedef enum WarpFlag { + WARP_FLAG_NORMAL = -1, // Not a boss warp + WARP_FLAG_BOSS_1, + WARP_FLAG_BOSS_2 +} WarpFlag; + +typedef enum BalloonType { + BALLOON_TYPE_BOOST, + BALLOON_TYPE_MISSILE, + BALLOON_TYPE_TRAP, + BALLOON_TYPE_SHIELD, + BALLOON_TYPE_MAGNET +} BalloonType; + typedef enum HorizontalAlignmentFlags { HORZ_ALIGN_LEFT = 0, HORZ_ALIGN_RIGHT = 1, diff --git a/include/level_object_entries.h b/include/level_object_entries.h index 43a25004..847de7e9 100644 --- a/include/level_object_entries.h +++ b/include/level_object_entries.h @@ -1,7 +1,15 @@ #ifndef _LEVEL_OBJECT_ENTRIES_H_ #define _LEVEL_OBJECT_ENTRIES_H_ - /* Size: 8 bytes. Shared across every entry. */ + +/** + * Only used in the DKR asset tool to help it figure out how to parse certain struct members. + * Shouldn't affect the actual game code. + * Also: It sucks that C didn't support variadic args in macros till c99. Double parenthesis is needed as a workaround. +*/ +#define Hint(args) + +/* Size: 8 bytes. Shared across every entry. */ typedef struct LevelObjectEntryCommon { u8 objectID; // 9-bit object ID to load (uses size's MSB). u8 size; // 7-bit total entry length (MSB is used in object_id). @@ -10,9 +18,9 @@ typedef struct LevelObjectEntryCommon { typedef struct LevelObjectEntry_Racer { /* 0x00 */ LevelObjectEntryCommon common; - /* 0x08 */ s16 angleZ; - /* 0x0A */ s16 angleX; - /* 0x0C */ s16 angleY; + /* 0x08 */ s16 angleZ; + /* 0x0A */ s16 angleX; + /* 0x0C */ s16 angleY; /* 0x0E */ s16 playerIndex; } LevelObjectEntry_Racer; @@ -42,7 +50,7 @@ typedef struct LevelObjectEntry_Smoke { typedef struct LevelObjectEntry_Exit { /* 0x00 */ LevelObjectEntryCommon common; - /* 0x08 */ u8 destinationMapId; + /* 0x08 */ u8 destinationMapId; Hint((AssetId:ASSET_LEVEL_HEADERS)) /* 0x09 */ u8 pad9; // Defines the location in overworld the player will spawn at. @@ -56,8 +64,8 @@ typedef struct LevelObjectEntry_Exit { /* 0x0A */ s8 overworldSpawnIndex; /* 0x0B */ u8 padB[5]; - /* 0x10 */ u8 radius; // Activation radius. - /* 0x11 */ u8 angleY; + /* 0x10 */ u8 radius; Hint((Scale, DivideBy:128)) // Activation radius. + /* 0x11 */ u8 angleY; Hint((Angle, DivideBy:64)) /* 0x12 */ u8 pad12[5]; // Defines the location in a hub world the player will spawn at when exiting `destinationMapId`. @@ -72,7 +80,7 @@ typedef struct LevelObjectEntry_Exit { // -1: Doesn't warp to a boss race // 0: Warps to a boss 1 race // 1: Warps to a boss 2 race - /* 0x18 */ s8 bossFlag; + /* 0x18 */ s8 bossFlag; Hint((Enum:WarpFlag)) /* 0x19 */ u8 pad19; } LevelObjectEntry_Exit; @@ -112,8 +120,8 @@ typedef struct LevelObjectEntry_SetupPoint { /* 0x00 */ LevelObjectEntryCommon common; /* 0x08 */ u8 unk8; /* 0x09 */ u8 unk9; - /* 0x0A */ u8 angleY; - /* 0x0B */ s8 vehicle; + /* 0x0A */ u8 angleY; Hint((Angle, DivideBy:64)) + /* 0x0B */ s8 vehicle; Hint((Enum:Vehicle)) } LevelObjectEntry_SetupPoint; typedef struct LevelObjectEntry_Dino_Whale { @@ -146,8 +154,8 @@ typedef struct LevelObjectEntry_Checkpoint { typedef struct LevelObjectEntry_Door { /* 0x00 */ LevelObjectEntryCommon common; - /* 0x08 */ u8 closedRotation; - /* 0x09 */ u8 openRotation; + /* 0x08 */ u8 closedRotation; //Hint((Angle, DivideBy:64)) + /* 0x09 */ u8 openRotation; //Hint((Angle, DivideBy:64)) /* 0x0A */ u8 modelIndex; /* 0x0B */ u8 distanceToOpen; /* 0x0C */ s8 unkC; @@ -182,17 +190,11 @@ typedef struct LevelObjectEntry_AiNode { /* 0x0F */ s8 padF; } LevelObjectEntry_AiNode; -#define BALLOON_TYPE_BOOST 0 -#define BALLOON_TYPE_MISSILE 1 -#define BALLOON_TYPE_TRAP 2 -#define BALLOON_TYPE_SHIELD 3 -#define BALLOON_TYPE_MAGNET 4 - typedef struct LevelObjectEntry_WeaponBalloon { /* 0x00 */ LevelObjectEntryCommon common; /* 0x08 */ u8 unk8; // Unused? - /* 0x09 */ u8 balloonType; - /* 0x0A */ u8 radius; // This is divided by 64 to get actual scale. + /* 0x09 */ u8 balloonType; Hint((Enum:BalloonType)) + /* 0x0A */ u8 radius; Hint((Scale, DivideBy:64)) // This is divided by 64 to get actual scale. /* 0x0B */ u8 padB; } LevelObjectEntry_WeaponBalloon; @@ -362,10 +364,10 @@ typedef struct LevelObjectEntry_CharacterFlag { */ typedef struct LevelObjectEntry_Animation { /* 0x00 */ LevelObjectEntryCommon common; - /* 0x08 */ u8 z_rotation; - /* 0x09 */ u8 x_rotation; - /* 0x0A */ u8 y_rotation; - /* 0x0B */ u8 scale; + /* 0x08 */ u8 z_rotation; //Hint((Angle, DivideBy:256)) + /* 0x09 */ u8 x_rotation; //Hint((Angle, DivideBy:256)) + /* 0x0A */ u8 y_rotation; //Hint((Angle, DivideBy:256)) + /* 0x0B */ u8 scale; Hint((Scale, DivideBy:64)) /* 0x0C */ s16 objectIdToSpawn; /* 0x0E */ s16 animationStartDelay; /* 0x10 */ s8 actorIndex; @@ -977,4 +979,6 @@ typedef struct LevelObjectEntry { }; } LevelObjectEntry; +#undef Hint + #endif diff --git a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/extract/extractor.o b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/extract/extractor.o index acb6b5f5..13619e68 100644 Binary files a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/extract/extractor.o and b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/extract/extractor.o differ diff --git a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cEnumsHelper.o b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cEnumsHelper.o index 951f0a93..8cfb1e99 100644 Binary files a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cEnumsHelper.o and b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cEnumsHelper.o differ diff --git a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructGltfHelper.o b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructGltfHelper.o index 471a934d..bd528c6d 100644 Binary files a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructGltfHelper.o and b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructGltfHelper.o differ diff --git a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructHelper.o b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructHelper.o index f08d6467..b10297ef 100644 Binary files a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructHelper.o and b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cStructHelper.o differ diff --git a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cTypes.o b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cTypes.o index fc862234..06c84681 100644 Binary files a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cTypes.o and b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/helpers/c/cTypes.o differ diff --git a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/test/test.o b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/test/test.o index 7bd9cc00..fbd14c39 100644 Binary files a/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/test/test.o and b/tools/dkr_assets_tool_src/_build/dkr_assets_tool_src/test/test.o differ diff --git a/tools/dkr_assets_tool_src/extract/extractor.cpp b/tools/dkr_assets_tool_src/extract/extractor.cpp index 1b859886..a58ca347 100644 --- a/tools/dkr_assets_tool_src/extract/extractor.cpp +++ b/tools/dkr_assets_tool_src/extract/extractor.cpp @@ -471,7 +471,9 @@ void DkrExtract::_generate_obj_behavior_to_entry_json_file() { for(int i = 0; i < 128; i++) { std::string symbol; - objBehaviors->get_symbol_of_value(i, symbol); + DebugHelper::assert_(objBehaviors->get_symbol_of_value(i, symbol), + "(DkrExtract::_generate_obj_behavior_to_entry_json_file) Could not get a symbol for the value ", i, " in the ObjectBehaviors enum."); + CStruct *entryStruct = _c_context.get_struct(defaultObjEntriesOrder[i]); DebugHelper::assert_(entryStruct != nullptr, diff --git a/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.cpp b/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.cpp index 336e470f..34401267 100644 --- a/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.cpp +++ b/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.cpp @@ -85,10 +85,22 @@ CEnum::CEnum(CContext *context, const std::string &rawCode) : _context(context) if(StringHelper::starts_with(rest, "=")) { std::string enumValue = match.get_group(3).get_text(); int parsedEnumValue = _parse_enum_value(enumValue); + if(_values.find(parsedEnumValue) == _values.end()) { + // New vector in values map. + _values[parsedEnumValue] = {}; + } _members[enumLabel] = parsedEnumValue; + // _values map is ordered by which the values were added. + _values[parsedEnumValue].push_back(enumLabel); nextValue = parsedEnumValue + 1; continue; } + if(_values.find(nextValue) == _values.end()) { + // New vector in values map. + _values[nextValue] = {}; + } + // _values map is ordered by which the values were added. + _values[nextValue].push_back(enumLabel); _members[enumLabel] = nextValue; nextValue++; } @@ -110,7 +122,6 @@ void CEnum::copy_members_to_map(std::unordered_map &inputMap) } } - bool CEnum::get_value_of_member(const std::string &memberKey, int &out) { if(_members.find(memberKey) == _members.end()) { return false; @@ -120,14 +131,12 @@ bool CEnum::get_value_of_member(const std::string &memberKey, int &out) { } bool CEnum::get_symbol_of_value(int value, std::string &outSymbol) { - for (auto& it : _members) { - if (it.second == value) { - outSymbol = it.first; // Return the first symbol that matches the value. - return true; - } + if(_values.find(value) == _values.end()) { + DebugHelper::warn("(CEnum::get_symbol_of_value) Could not find the value ", value, " for the enum \"", _name, "\""); + return false; } - // value was not found in the map. - return false; + outSymbol = _values[value][0]; // Return the first symbol of the value. + return true; } size_t CEnum::get_member_count() { diff --git a/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.h b/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.h index 2206d9ab..a702570f 100644 --- a/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.h +++ b/tools/dkr_assets_tool_src/helpers/c/cEnumsHelper.h @@ -34,6 +34,9 @@ private: CContext *_context; std::unordered_map _members; + // Multiple keys may have the same value, that is the reason for the vector here. + std::unordered_map> _values; + std::string _name; std::string _typedefName; diff --git a/tools/dkr_assets_tool_src/helpers/c/cStructGltfHelper.cpp b/tools/dkr_assets_tool_src/helpers/c/cStructGltfHelper.cpp index 52e92f79..8d2ba482 100644 --- a/tools/dkr_assets_tool_src/helpers/c/cStructGltfHelper.cpp +++ b/tools/dkr_assets_tool_src/helpers/c/cStructGltfHelper.cpp @@ -1,23 +1,143 @@ #include "cStructGltfHelper.h" +#include "helpers/c/cContext.h" +#include "helpers/c/cTypes.h" + +enum StructHintType { + NO_HINT, + ENUM, // An enum from include/enums.h + ASSET_ID, + ANGLE, // Converts angle byte into degrees. + SCALE // Divide by some number to get a float representation of scale. +}; + +std::unordered_map hintTypes = { + { "Enum", StructHintType::ENUM }, + { "Angle", StructHintType::ANGLE } +}; + +// Gets an enum symbol from a struct member and an integer value. +// Assumption: structMember has the "Enum" hint. +std::string get_hint_enum_value(CStructEntry *structMember, int value) { + std::string enumName = structMember->get_hint_value("Enum"); + CContext *context = structMember->get_context(); + CEnum *hintEnum = context->get_enum(enumName); + std::string enumValue; + + DebugHelper::assert_(hintEnum->get_symbol_of_value(value, enumValue), + "(get_hint_enum_value) Enum ", enumName, + " does not have a symbol for the value ", value); + + return enumValue; +} + +// Get the integer value from an enum value string. +// Assumption: structMember has the "Enum" hint. +int get_value_from_hint_enum(CStructEntry *structMember, std::string &enumValue) { + std::string enumName = structMember->get_hint_value("Enum"); + CContext *context = structMember->get_context(); + CEnum *hintEnum = context->get_enum(enumName); + int value; + + DebugHelper::assert_(hintEnum->get_value_of_member(enumValue, value), + "(get_value_from_hint_enum) Enum ", enumName, + " does not have a value for the symbol ", enumValue); + + return value; +} + +double get_hint_angle(CStructEntry *structMember, int value) { + std::string divideByStr = structMember->get_hint_value("DivideBy", "64"); + double divideBy = std::stod(divideByStr); + + double angle = (value / divideBy) * 360.0; // Convert value to degrees + + // *Probably* meant to be negative; convert the unsigned value to a signed value. + if((angle > 360.0) && (!CTypes::is_signed_type(structMember->type))) { + int64_t maxValuePlusOne = CTypes::get_max_int(structMember->type) + 1; + double maxAngle = (maxValuePlusOne / divideBy) * 360.0; + angle -= maxAngle; // convert angle to a signed value. + } + + return angle; +} + +int get_value_from_hint_angle(CStructEntry *structMember, double angle) { + std::string divideByStr = structMember->get_hint_value("DivideBy", "64"); + double divideBy = std::stod(divideByStr); + + if((angle < 0.0) && (!CTypes::is_signed_type(structMember->type))) { + // convert angle back to a unsigned type. + int64_t maxValuePlusOne = CTypes::get_max_int(structMember->type) + 1; + double maxAngle = (maxValuePlusOne / divideBy) * 360.0; + angle += maxAngle; // convert angle back to unsigned. + } + + int out = (int)((angle / 360.0) * divideBy); // Convert from degrees + return CTypes::clamp_int(structMember->type, out); +} + void CStructGltfHelper::put_struct_entry_into_gltf_node_extra(CStructEntry *structMember, WriteableGltfFile &gltfFile, int gltfNode, uint8_t *bytes) { CStructEntry::InternalType internalType = structMember->get_internal_type(); - + StructHintType hintType = StructHintType::NO_HINT; + if(structMember->has_hint()) { + hintType = hintTypes[structMember->get_hint_type()]; + } switch(internalType) { case CStructEntry::InternalType::INTEGER: { int value = structMember->get_integer_from_data(bytes); - gltfFile.set_node_extra(gltfNode, structMember->name, value); - } + switch(hintType) { + case StructHintType::NO_HINT: + gltfFile.set_node_extra(gltfNode, structMember->name, value); + break; + case StructHintType::ENUM: + { + std::string enumValue = get_hint_enum_value(structMember, value); + gltfFile.set_node_extra(gltfNode, structMember->name, enumValue); + break; + } + case StructHintType::ANGLE: + { + double angle = get_hint_angle(structMember, value); + gltfFile.set_node_extra(gltfNode, structMember->name, angle); + break; + } + default: + DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type()); + break; + } break; + } case CStructEntry::InternalType::ARRAY_INTEGER: { std::vector values; structMember->get_values_from_data_array(values, bytes); tinygltf::Value::Array arrayOfValues; - for(int64_t val : values) { - arrayOfValues.push_back(tinygltf::Value((int)val)); + for(int64_t value : values) { + // TODO: This looks pretty similar to the InternalType::INTEGER case. Can I make this a common function? + switch(hintType) { + case StructHintType::NO_HINT: + arrayOfValues.push_back(tinygltf::Value((int)value)); + break; + case StructHintType::ENUM: + { + std::string enumValue = get_hint_enum_value(structMember, value); + arrayOfValues.push_back(tinygltf::Value(enumValue)); + break; + } + case StructHintType::ANGLE: + { + double angle = get_hint_angle(structMember, value); + arrayOfValues.push_back(tinygltf::Value(angle)); + break; + } + default: + DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type()); + break; + } } + gltfFile.set_node_extra(gltfNode, structMember->name, arrayOfValues); } break; @@ -31,37 +151,66 @@ void CStructGltfHelper::put_struct_entry_into_gltf_node_extra(CStructEntry *stru void CStructGltfHelper::put_gltf_node_extra_into_struct_entry(CStructEntry *structMember, GltfFile &gltfFile, int gltfNode, uint8_t *bytes) { CStructEntry::InternalType internalType = structMember->get_internal_type(); + StructHintType hintType = StructHintType::NO_HINT; + if(structMember->has_hint()) { + hintType = hintTypes[structMember->get_hint_type()]; + } switch(internalType) { case CStructEntry::InternalType::INTEGER: { - int value = gltfFile.get_node_extra(gltfNode, structMember->name, 0); + int value = 0; + switch(hintType) { + case StructHintType::NO_HINT: + value = gltfFile.get_node_extra(gltfNode, structMember->name, 0); + break; + case StructHintType::ENUM: + { + std::string enumValue = gltfFile.get_node_extra(gltfNode, structMember->name, ""); + value = get_value_from_hint_enum(structMember, enumValue); + break; + } + case StructHintType::ANGLE: + { + double angle = gltfFile.get_node_extra(gltfNode, structMember->name, 0.0); + value = get_value_from_hint_angle(structMember, angle); + break; + } + default: + DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type()); + break; + } structMember->set_integer_to_data(bytes, value); - } break; + } case CStructEntry::InternalType::ARRAY_INTEGER: { tinygltf::Value::Array arrayOfValues = gltfFile.get_node_extra(gltfNode, structMember->name, {}); std::vector values; for(tinygltf::Value &val : arrayOfValues) { - values.push_back(val.GetNumberAsInt()); + switch(hintType) { + case StructHintType::NO_HINT: + values.push_back(val.GetNumberAsInt()); + break; + case StructHintType::ENUM: + values.push_back(get_value_from_hint_enum(structMember, val.Get())); + break; + case StructHintType::ANGLE: + values.push_back(get_value_from_hint_angle(structMember, val.Get())); + break; + default: + DebugHelper::error("Unimplemented hint type: ", structMember->get_hint_type()); + break; + } + } structMember->set_values_to_data_array(values, bytes); - /* - std::vector values; - structMember->get_values_from_data_array(values, bytes); - tinygltf::Value::Array arrayOfValues; - for(int64_t val : values) { - arrayOfValues.push_back(tinygltf::Value((int)val)); - } - gltfFile.set_node_extra(gltfNode, structMember->name, arrayOfValues); - */ - } break; + } case CStructEntry::InternalType::UNKNOWN: break; // Do nothing (for now) default: DebugHelper::error("(CStructGltfHelper::put_struct_entry_into_gltf_node_extra) Type not implemented: ", internalType); break; } -} \ No newline at end of file +} diff --git a/tools/dkr_assets_tool_src/helpers/c/cStructHelper.cpp b/tools/dkr_assets_tool_src/helpers/c/cStructHelper.cpp index 9cd9dc6e..0d8fe849 100644 --- a/tools/dkr_assets_tool_src/helpers/c/cStructHelper.cpp +++ b/tools/dkr_assets_tool_src/helpers/c/cStructHelper.cpp @@ -12,7 +12,7 @@ #include "cTypes.h" CStructEntry::CStructEntry(CContext *context, CStruct *parent, std::string &type, std::string &pointer, std::string &name, - std::string &arrayBrackets) : type(type), pointer(pointer), name(name), arrayBrackets(arrayBrackets), + std::string &arrayBrackets, std::string hint) : type(type), pointer(pointer), name(name), arrayBrackets(arrayBrackets), _context(context), _parent(parent) { // Make sure the strings have no whitespace. Makes processing easier. StringHelper::remove_all_whitespace(this->type); @@ -20,6 +20,12 @@ CStructEntry::CStructEntry(CContext *context, CStruct *parent, std::string &type StringHelper::remove_all_whitespace(this->name); StringHelper::remove_all_whitespace(this->arrayBrackets); _calc_array_multiplier(); + + if(!hint.empty()) { + _generate_hint_map(hint); + } + + _isSignedType = CTypes::is_signed_type(type); } bool CStructEntry::is_pointer_to_type() { @@ -30,6 +36,67 @@ bool CStructEntry::is_array() { return !arrayBrackets.empty(); } +bool CStructEntry::has_hint() { + return !_hintMap.empty(); +} + +std::string CStructEntry::get_hint_type() { + DebugHelper::assert(has_hint(), "(CStructEntry::get_hint_type) ", name, " does not have a hint!"); + DebugHelper::assert(_hintMap.find("_hintType") != _hintMap.end(), "(CStructEntry::get_hint_type) ", name + , " does not have a hint type! (Should not be possible?)"); + return _hintMap["_hintType"]; +} + +std::string CStructEntry::get_hint_value(const std::string hintKey) { + DebugHelper::assert(has_hint(), "(CStructEntry::get_hint_type) ", name, " does not have a hint!"); + DebugHelper::assert(_hintMap.find(hintKey) != _hintMap.end(), "(CStructEntry::get_hint_type) ", name + , " does not have the hint key: \"", hintKey, "\""); + return _hintMap[hintKey]; +} + +std::string CStructEntry::get_hint_value(const std::string hintKey, std::string defaultValue) { + DebugHelper::assert(has_hint(), "(CStructEntry::get_hint_type) ", name, " does not have a hint!"); + if(_hintMap.find(hintKey) == _hintMap.end()) { + return defaultValue; + } + return _hintMap[hintKey]; +} + +void CStructEntry::_generate_hint_map(std::string &hint) { + std::vector hintArgs; + StringHelper::split(hint, ',', hintArgs); + size_t numArgs = hintArgs.size(); + + std::string &firstArg = hintArgs[0]; + + // Arg 0 is always the hint type. Might have a colon to indicate the sub-type. + if(StringHelper::has(firstArg, ":")) { + size_t colonIndex = firstArg.find_first_of(':'); + std::string hintType = firstArg.substr(0, colonIndex); + _hintMap["_hintType"] = hintType; + _hintMap[hintType] = firstArg.substr(colonIndex + 1); + } else { + _hintMap["_hintType"] = firstArg; + } + + // Rest of the args + for(size_t i = 1; i < numArgs; i++) { + std::string &arg = hintArgs[i]; + + size_t colonIndex = arg.find_first_of(':'); + + if(colonIndex == std::string::npos) { + // TODO: Support other hint types besides just a std::string? + DebugHelper::error("(CStructEntry::_generate_hint_map) Invalid hint argument: \"", arg, "\""); + } + + std::string hintKey = arg.substr(0, colonIndex); + DebugHelper::assert(!hintKey.empty(), "(CStructEntry::_generate_hint_map) Hint arg ", i, + " for \"", name, "\" does not have a key!"); + _hintMap[hintKey] = arg.substr(colonIndex + 1); + } +} + std::string CStructEntry::to_string() { std::stringstream ss; ss << type << " " << pointer << name << arrayBrackets << ";"; @@ -151,6 +218,13 @@ int64_t CStructEntry::get_integer_from_data(uint8_t *data, bool isBigEndian) { value |= ((int64_t)data[index]) << (int64_t)(i * 8); } + if(_isSignedType) { + // Convert unsigned value to signed value. + int shiftBy = (sizeof(int64_t) - numBytes) * 8; + value <<= shiftBy; + value >>= shiftBy; + } + return value; } @@ -248,6 +322,10 @@ void CStructEntry::_calc_array_multiplier() { } } +CContext *CStructEntry::get_context() { + return _context; +} + CStruct::CStruct(CContext *context) : _context(context) { } @@ -273,6 +351,9 @@ struct CStructMemberParse { bool isStruct; // type is a struct/union int structStart; int structEnd; + bool hasHint; + int hintStart; + int hintEnd; void reset() { start = -1; @@ -280,6 +361,9 @@ struct CStructMemberParse { isStruct = false; structStart = -1; structEnd = -1; + hasHint = false; + hintStart = -1; + hintEnd = -1; } std::string to_string() { @@ -304,9 +388,19 @@ struct CStructMemberParse { size_t length = structEnd - structStart; return structCode.substr(structStart, length); } + + std::string get_hint_substring(const std::string &structCode) { + if(!hasHint) { + return ""; + } + size_t length = hintEnd - hintStart; + std::string out = structCode.substr(hintStart, length); + StringHelper::remove_all_whitespace(out); // Should be no spaces in a hint. + return out; + } }; -const std::string C_STRUCT_END_CHAR = R"(^((?:.|\n)*?([;{])))"; +const std::string C_STRUCT_END_CHAR = R"(^((?:.|\n)*?([;{]))(?:\s*Hint[(]\s*[(]?([^)\n]*)[)](?:\s*[)])?)?)"; int get_next_member(const std::string &structCode, int offsetInCode, CStructMemberParse &out) { @@ -340,6 +434,17 @@ int get_next_member(const std::string &structCode, int offsetInCode, CStructMemb // Not a struct type, so just return the start/end points. out.start = offsetInCode; out.end = endCharOffset + 1; + + // Check for hint. + out.hintStart = match->get_group(3).get_offset(); + + if(out.hintStart != -1) { + // Hint data was found! + out.hintStart += offsetInCode; + out.hintEnd = out.hintStart + match->get_group(3).get_text_length(); + out.hasHint = true; + } + delete match; return out.end; } @@ -358,17 +463,16 @@ int get_next_member(const std::string &structCode, int offsetInCode, CStructMemb offsetInCode = out.structEnd; - // Now that the struct was found, get the rest of the member. - //bool success = get_next_member_end_character(structCode, offsetInCode, endCharOffset, endChar, false); - //DebugHelper::assert(success, "(get_next_member) Couldn't find an ending colon for the struct! At pos ", offsetInCode); - delete match; + // Now that the struct was found, get the rest of the member. endCharOffset = structCode.find_first_of(';', offsetInCode); out.start = offsetInCode; out.end = endCharOffset + 1; + // Don't bother checking hints for the struct. (Maybe at a later date?) + return out.end; } @@ -407,6 +511,9 @@ CStruct::CStruct(CContext *context, const std::string &rawCode) : _context(conte std::string type; std::string rest; + // Should be empty if there is no hint. + std::string hint = parseInfo.get_hint_substring(structCode); + if(parseInfo.isStruct) { std::string innerStructCode = parseInfo.get_struct_substring(structCode); innerStruct = new CStruct(_context, innerStructCode); @@ -443,7 +550,7 @@ CStruct::CStruct(CContext *context, const std::string &rawCode) : _context(conte delete match; // done with match. } - _entries.push_back(new CStructEntry(_context, this, type, pointers, name, arrayBrackets)); + _entries.push_back(new CStructEntry(_context, this, type, pointers, name, arrayBrackets, hint)); if(parseInfo.isStruct) { _entries.back()->innerStruct = innerStruct; diff --git a/tools/dkr_assets_tool_src/helpers/c/cStructHelper.h b/tools/dkr_assets_tool_src/helpers/c/cStructHelper.h index 76b1fe25..cff47405 100644 --- a/tools/dkr_assets_tool_src/helpers/c/cStructHelper.h +++ b/tools/dkr_assets_tool_src/helpers/c/cStructHelper.h @@ -4,6 +4,7 @@ #include #include +#include #include class CContext; @@ -38,10 +39,17 @@ struct CStructEntry { CStruct *innerStruct = nullptr; // Only used in struct/union type. CStructEntry(CContext *context, CStruct *parent, std::string &type, std::string &pointer, std::string &name, - std::string &arrayBrackets); + std::string &arrayBrackets, std::string hint); bool is_pointer_to_type(); bool is_array(); + bool has_hint(); + std::string get_hint_type(); // What type of hint does this member have? + std::string get_hint_value(const std::string hintKey); + + // Returns the default value if the key isn't in the hint map. + std::string get_hint_value(const std::string hintKey, std::string defaultValue); + std::string to_string(); bool is_type_trivial(); @@ -74,11 +82,18 @@ struct CStructEntry { int get_offset(); void set_offset(int newOffset); // Note: Should only be called by the parent cstruct. + CContext *get_context(); private: + // The hint map is specifically used for Object Maps. + std::unordered_map _hintMap; + void _generate_hint_map(std::string &hint); + CContext *_context; CStruct *_parent; InternalType _internalType = InternalType::NOT_SET; int _offset = -1; // Is only calculated if get_offset is called. + bool _isSignedType; + size_t _arrayMultiplier; diff --git a/tools/dkr_assets_tool_src/helpers/c/cTypes.cpp b/tools/dkr_assets_tool_src/helpers/c/cTypes.cpp index 849db30b..ef73e02f 100644 --- a/tools/dkr_assets_tool_src/helpers/c/cTypes.cpp +++ b/tools/dkr_assets_tool_src/helpers/c/cTypes.cpp @@ -1,5 +1,6 @@ #include "cTypes.h" +#include #include #include "helpers/debugHelper.h" @@ -8,48 +9,66 @@ enum CTypeCategory { CTYPE_FLOAT }; +struct MinMaxInfo { + int64_t minInt; + uint64_t maxInt; +}; + struct CTypeInfo { size_t size; CTypeCategory category; + bool isSigned; + MinMaxInfo minMaxInfo; }; +// Min/Max ranges +const MinMaxInfo mmi_s8 = { INT8_MIN, INT8_MAX }; +const MinMaxInfo mmi_u8 = { 0, UINT8_MAX }; +const MinMaxInfo mmi_s16 = { SHRT_MIN, SHRT_MAX }; +const MinMaxInfo mmi_u16 = { 0, USHRT_MAX }; +const MinMaxInfo mmi_s32 = { LONG_MIN, LONG_MAX }; +const MinMaxInfo mmi_u32 = { 0, ULONG_MAX }; +const MinMaxInfo mmi_s64 = { LLONG_MIN, LLONG_MAX }; +const MinMaxInfo mmi_u64 = { 0, ULLONG_MAX }; + + // TODO: Is there a better way to do this? // Note: sizes according to N64 hardware. std::unordered_map typeInfos = { // 1 byte types - { "char", {1, CTYPE_INTEGER}}, - { "signed char", {1, CTYPE_INTEGER}}, - { "unsigned char", {1, CTYPE_INTEGER}}, - { "s8", {1, CTYPE_INTEGER}}, - { "u8", {1, CTYPE_INTEGER}}, + { "char", {1, CTYPE_INTEGER, true, mmi_s8}}, + { "signed char", {1, CTYPE_INTEGER, true, mmi_s8}}, + { "unsigned char", {1, CTYPE_INTEGER, false, mmi_u8}}, + { "s8", {1, CTYPE_INTEGER, true, mmi_s8}}, + { "u8", {1, CTYPE_INTEGER, false, mmi_u8}}, // 2 byte types - { "short", {2, CTYPE_INTEGER}}, - { "signed short", {2, CTYPE_INTEGER}}, - { "unsigned short", {2, CTYPE_INTEGER}}, - { "short int", {2, CTYPE_INTEGER}}, - { "signed short int", {2, CTYPE_INTEGER}}, - { "unsigned short int", {2, CTYPE_INTEGER}}, - { "s16", {2, CTYPE_INTEGER}}, - { "u16", {2, CTYPE_INTEGER}}, + { "short", {2, CTYPE_INTEGER, true, mmi_s16}}, + { "signed short", {2, CTYPE_INTEGER, true, mmi_s16}}, + { "unsigned short", {2, CTYPE_INTEGER, false, mmi_u16}}, + { "short int", {2, CTYPE_INTEGER, true, mmi_s16}}, + { "signed short int", {2, CTYPE_INTEGER, true, mmi_s16}}, + { "unsigned short int", {2, CTYPE_INTEGER, false, mmi_u16}}, + { "s16", {2, CTYPE_INTEGER, true, mmi_s16}}, + { "u16", {2, CTYPE_INTEGER, false, mmi_u16}}, // 4 byte types - { "int", {4, CTYPE_INTEGER}}, - { "signed int", {4, CTYPE_INTEGER}}, - { "unsigned int", {4, CTYPE_INTEGER}}, - { "long", {4, CTYPE_INTEGER}}, // long is 32-bits on N64 - { "signed long", {4, CTYPE_INTEGER}}, - { "unsigned long", {4, CTYPE_INTEGER}}, - { "s32", {4, CTYPE_INTEGER}}, - { "u32", {4, CTYPE_INTEGER}}, - { "float", {4, CTYPE_FLOAT}}, - { "f32", {4, CTYPE_FLOAT}}, + { "int", {4, CTYPE_INTEGER, true, mmi_s32}}, + { "signed int", {4, CTYPE_INTEGER, true, mmi_s32}}, + { "unsigned int", {4, CTYPE_INTEGER, false, mmi_u32}}, + { "long", {4, CTYPE_INTEGER, true, mmi_s32}}, // long is 32-bits on N64 + { "signed long", {4, CTYPE_INTEGER, true, mmi_s32}}, + { "unsigned long", {4, CTYPE_INTEGER, false, mmi_u32}}, + { "s32", {4, CTYPE_INTEGER, true, mmi_s32}}, + { "u32", {4, CTYPE_INTEGER, false, mmi_u32}}, + { "float", {4, CTYPE_FLOAT, false}}, + { "f32", {4, CTYPE_FLOAT, false}}, // 8 byte types - { "long long int", {8, CTYPE_INTEGER}}, - { "signed long long int", {8, CTYPE_INTEGER}}, - { "unsigned long long int", {8, CTYPE_INTEGER}}, - { "s64", {8, CTYPE_INTEGER}}, - { "u64", {8, CTYPE_INTEGER}}, - { "double", {8, CTYPE_FLOAT}}, - { "f64", {8, CTYPE_FLOAT}}, + { "long long int", {8, CTYPE_INTEGER, true, mmi_s64}}, + { "signed long long int", {8, CTYPE_INTEGER, true, mmi_s64}}, + { "unsigned long long int", {8, CTYPE_INTEGER, false, mmi_u64}}, + { "s64", {8, CTYPE_INTEGER, true, mmi_s64}}, + { "u64", {8, CTYPE_INTEGER, false, mmi_u64}}, + { "double", {8, CTYPE_FLOAT, false}}, + { "f64", {8, CTYPE_FLOAT, false}}, }; bool CTypes::is_standard_type(const std::string &typeName) { @@ -84,7 +103,41 @@ bool CTypes::is_float_type(const std::string &typeName) { return typeInfos[typeName].category == CTypeCategory::CTYPE_FLOAT; } +bool CTypes::is_signed_type(const std::string &typeName) { + if(!CTypes::is_standard_type(typeName)) { + return false; + } + return typeInfos[typeName].isSigned; +} + +int64_t CTypes::get_min_int(const std::string &typeName) { + DebugHelper::assert(CTypes::is_standard_type(typeName), + "(CTypes::get_min_int) Type ", typeName, " is not a standard type!"); + return typeInfos[typeName].minMaxInfo.minInt; +} + +int64_t CTypes::get_max_int(const std::string &typeName) { + DebugHelper::assert(CTypes::is_standard_type(typeName), + "(CTypes::get_max_int) Type ", typeName, " is not a standard type!"); + return typeInfos[typeName].minMaxInfo.maxInt; +} + size_t CTypes::size_of_pointer() { return 4; // Note: Assuming N64 platform. } +int64_t CTypes::clamp_int(const std::string &typeName, int64_t value) { + if(!CTypes::is_standard_type(typeName)) { + return value; + } + if(typeInfos[typeName].category != CTypeCategory::CTYPE_INTEGER) { + return value; + } + if(value < typeInfos[typeName].minMaxInfo.minInt) { + return typeInfos[typeName].minMaxInfo.minInt; + } + if(value > typeInfos[typeName].minMaxInfo.maxInt) { + return typeInfos[typeName].minMaxInfo.maxInt; + } + return value; +} diff --git a/tools/dkr_assets_tool_src/helpers/c/cTypes.h b/tools/dkr_assets_tool_src/helpers/c/cTypes.h index ce7160ad..e280268a 100644 --- a/tools/dkr_assets_tool_src/helpers/c/cTypes.h +++ b/tools/dkr_assets_tool_src/helpers/c/cTypes.h @@ -13,7 +13,15 @@ public: static bool is_integer_type(const std::string &typeName); static bool is_float_type(const std::string &typeName); + static bool is_signed_type(const std::string &typeName); + + static int64_t get_min_int(const std::string &typeName); + static int64_t get_max_int(const std::string &typeName); + static size_t size_of_pointer(); static bool does_type_have_an_entry(const std::string &typeName); + + static int64_t clamp_int(const std::string &typeName, int64_t value); + }; \ No newline at end of file diff --git a/tools/dkr_assets_tool_src/helpers/stringHelper.h b/tools/dkr_assets_tool_src/helpers/stringHelper.h index 32b42fcb..0c3dc090 100644 --- a/tools/dkr_assets_tool_src/helpers/stringHelper.h +++ b/tools/dkr_assets_tool_src/helpers/stringHelper.h @@ -6,7 +6,7 @@ // These are functions that I would've expected to be a part of std::string. (Except for a few) class StringHelper { public: - static bool has(const std::string &input, const char* substring); + static bool has(const std::string &input, const char *substring); static void make_lowercase(std::string &input); static void make_uppercase(std::string &input); diff --git a/tools/dkr_assets_tool_src/test/test.cpp b/tools/dkr_assets_tool_src/test/test.cpp index 9dece2de..004d60f2 100644 --- a/tools/dkr_assets_tool_src/test/test.cpp +++ b/tools/dkr_assets_tool_src/test/test.cpp @@ -273,17 +273,24 @@ void test_cstruct_helper_engine(RegexEngine engine, DkrAssetsSettings &settings) input = R"( typedef struct LevelObjectEntry_Racer { - LevelObjectEntryCommon common; - s16 angleZ; - s16 angleX; - s16 angleY; - s16 playerIndex; + /* 0x00 */ LevelObjectEntryCommon common; + /* 0x08 */ s16 angleZ; Hint((Angle)) + /* 0x0A */ s16 angleX; Hint(( Angle)) + /* 0x0C */ s16 angleY; Hint(( Angle )) + /* 0x0E */ s16 playerIndex; } LevelObjectEntry_Racer; )"; CStruct test1(&ctx, input); DebugHelper::assert(!test1.is_trivial(), "Failed test 1.0 (struct was trivial)"); - DebugHelper::assert(test1.get_byte_size() == 16, "Failed test 1.0 (struct size incorrect, was ", test1.get_byte_size(), " when it should be 16)"); + DebugHelper::assert(test1.get_byte_size() == 16, "Failed test 1.1 (struct size incorrect, was ", test1.get_byte_size(), " when it should be 16)"); + DebugHelper::assert(test1.get_entry(1)->get_hint_type() == "Angle", "Failed test 1.2 (Invalid hint type, was \"", test1.get_entry(1)->get_hint_type(), + "\" when it should've been \"Angle\")"); + DebugHelper::assert(test1.get_entry(2)->get_hint_type() == "Angle", "Failed test 1.3 (Invalid hint type, was \"", test1.get_entry(2)->get_hint_type(), + "\" when it should've been \"Angle\")"); + DebugHelper::assert(test1.get_entry(3)->get_hint_type() == "Angle", "Failed test 1.4 (Invalid hint type, was \"", test1.get_entry(3)->get_hint_type(), + "\" when it should've been \"Angle\")"); + // Clear structs that were created in the previous test. ctx.clear_structs(); @@ -331,7 +338,7 @@ void test_cstruct_helper_engine(RegexEngine engine, DkrAssetsSettings &settings) // -1: Doesn't warp to a boss race // 0: Warps to a boss 1 race // 1: Warps to a boss 2 race - s8 bossFlag; + s8 bossFlag; Hint( ( Enum : WarpFlag ) ) // Stuff added for testing purposes char *stringTest; @@ -376,6 +383,8 @@ void test_cstruct_helper_engine(RegexEngine engine, DkrAssetsSettings &settings) CStruct test2(&ctx, input); DebugHelper::assert(!test2.is_trivial(), "Failed test 2.0 (struct was trivial)"); + + DebugHelper::assert(test2.get_entry(9)->get_hint_value("Enum") == "WarpFlag", "Failed test 2.1 ()"); // TODO: More tests needed! // Clear structs that were created in the previous test.