mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-24 22:35:53 -04:00
Merge branch 'rando-mod' of https://github.com/TwilitRealm/dusk into rando-mod
This commit is contained in:
+44
-4
@@ -411,7 +411,47 @@ if (DUSK_ENABLE_CODE_MODS)
|
||||
setup_symbol_manifest(dusklight)
|
||||
endif ()
|
||||
|
||||
# Hook reliability: guaranteed patchable entries on the game ABI surface, and no identical-code folding.
|
||||
# Hook reliability: prevent auto-inlining, guarantee patchable function entries,
|
||||
# and disable identical-code folding for game functions.
|
||||
set(DUSK_GAME_ABI_INLINE_OPTIONS)
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND DUSK_GAME_ABI_INLINE_OPTIONS
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-finline-hint-functions>)
|
||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
list(APPEND DUSK_GAME_ABI_INLINE_OPTIONS
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-flive-patching=inline-clone>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-inline-functions>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-inline-small-functions>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-inline-functions-called-once>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-early-inlining>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-ipa-cp>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-ipa-cp-clone>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-ipa-sra>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-partial-inlining>)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (DUSK_GAME_ABI_INLINE_OPTIONS)
|
||||
set(_game_abi_files ${GAME_BASE_FILES} ${GAME_DEBUG_FILES})
|
||||
list(FILTER _game_abi_files EXCLUDE REGEX "^src/dusk/")
|
||||
set_property(SOURCE ${_game_abi_files} APPEND PROPERTY
|
||||
COMPILE_OPTIONS ${DUSK_GAME_ABI_INLINE_OPTIONS})
|
||||
|
||||
foreach(jsystem_lib IN LISTS JSYSTEM_LIBRARIES)
|
||||
target_compile_options(${jsystem_lib} PRIVATE ${DUSK_GAME_ABI_INLINE_OPTIONS})
|
||||
endforeach()
|
||||
foreach(_sdk_lib aurora_card aurora_core aurora_dvd aurora_gd aurora_gx aurora_mtx
|
||||
aurora_os aurora_pad aurora_si aurora_vi)
|
||||
if (TARGET ${_sdk_lib})
|
||||
get_target_property(_sdk_lib_imported ${_sdk_lib} IMPORTED)
|
||||
if (NOT _sdk_lib_imported)
|
||||
target_compile_options(${_sdk_lib} PRIVATE ${DUSK_GAME_ABI_INLINE_OPTIONS})
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
if (MSVC)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:/hotpatch>)
|
||||
@@ -421,11 +461,11 @@ if (MSVC)
|
||||
else ()
|
||||
target_link_options(dusklight PRIVATE /FUNCTIONPADMIN /OPT:NOICF)
|
||||
endif ()
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "^(AppleClang|Clang)$")
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "^(AppleClang|Clang|GNU)$")
|
||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$" OR CMAKE_OSX_ARCHITECTURES MATCHES "arm64")
|
||||
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=2,1>)
|
||||
else ()
|
||||
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=10,5>)
|
||||
set(DUSK_PATCHABLE_ENTRY_FLAG $<$<COMPILE_LANGUAGE:C,CXX>:-fpatchable-function-entry=10,4>)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
|
||||
@@ -8449,8 +8449,10 @@ struct daAlink_cutHorseParamTbl {
|
||||
/* 0xA */ u8 m_cutType;
|
||||
}; // Size: 0xC
|
||||
|
||||
#if !TARGET_PC
|
||||
inline BOOL dComIfGs_isTransformLV(int i_no);
|
||||
inline BOOL dComIfGs_isEventBit(const u16);
|
||||
#endif
|
||||
|
||||
static fopAc_ac_c* daAlink_searchPortal(fopAc_ac_c* i_actor, void* i_data);
|
||||
static fopAc_ac_c* daAlink_searchCanoe(fopAc_ac_c* i_actor, void* i_data);
|
||||
|
||||
@@ -311,9 +311,11 @@ private:
|
||||
class daMidna_c;
|
||||
class daSpinner_c;
|
||||
class daPy_py_c;
|
||||
#if !TARGET_PC
|
||||
inline daPy_py_c* dComIfGp_getLinkPlayer();
|
||||
inline BOOL dComIfGs_isEventBit(const u16);
|
||||
inline u32 dComIfGs_getLastSceneMode();
|
||||
#endif
|
||||
|
||||
class daPy_py_c : public fopAc_ac_c {
|
||||
public:
|
||||
|
||||
+934
-78
File diff suppressed because it is too large
Load Diff
@@ -123,6 +123,14 @@ inline int __builtin_clz(unsigned int v) {
|
||||
#endif
|
||||
#define DUSK_GAME_EXTERN extern DUSK_GAME_DATA
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define DUSK_NOINLINE __declspec(noinline)
|
||||
#elif defined(__GNUC__)
|
||||
#define DUSK_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#define DUSK_NOINLINE
|
||||
#endif
|
||||
|
||||
#define FAST_DIV(x, n) (x >> (n / 2))
|
||||
|
||||
#define SQUARE(x) ((x) * (x))
|
||||
|
||||
@@ -56,16 +56,20 @@ RandomizerContext::FlowNodeType parse_flow_node_type(const YAML::Node& node) {
|
||||
throw std::runtime_error("Unknown flow node type: " + type);
|
||||
}
|
||||
|
||||
void write_flow_reference(
|
||||
YAML::Node node, const RandomizerContext::FlowReference& reference) {
|
||||
YAML::Node write_flow_reference(const RandomizerContext::FlowReference& reference) {
|
||||
YAML::Node node{};
|
||||
if (reference.nativeId.has_value()) {
|
||||
node = reference.nativeId.value();
|
||||
} else {
|
||||
node = reference.name;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
RandomizerContext::FlowReference parse_flow_reference(const YAML::Node& node) {
|
||||
if (!node.IsScalar()) {
|
||||
throw std::runtime_error("Flow reference must be a node ID or name");
|
||||
}
|
||||
RandomizerContext::FlowReference reference{};
|
||||
try {
|
||||
reference.nativeId = node.as<u16>();
|
||||
@@ -78,15 +82,15 @@ RandomizerContext::FlowReference parse_flow_reference(const YAML::Node& node) {
|
||||
void write_message_style(
|
||||
YAML::Node node, const RandomizerContext::MessageStyleData& style) {
|
||||
node["eventLabelId"] = style.eventLabelId;
|
||||
node["speaker"] = style.speaker;
|
||||
node["boxKind"] = style.boxKind;
|
||||
node["drawType"] = style.drawType;
|
||||
node["boxPosition"] = style.boxPosition;
|
||||
node["lineAlignment"] = style.lineAlignment;
|
||||
node["speakerMood"] = style.speakerMood;
|
||||
node["cameraAttr"] = style.cameraAttr;
|
||||
node["talkAnim"] = style.talkAnim;
|
||||
node["faceAnim"] = style.faceAnim;
|
||||
node["speaker"] = static_cast<u16>(style.speaker);
|
||||
node["boxKind"] = static_cast<u16>(style.boxKind);
|
||||
node["drawType"] = static_cast<u16>(style.drawType);
|
||||
node["boxPosition"] = static_cast<u16>(style.boxPosition);
|
||||
node["lineAlignment"] = static_cast<u16>(style.lineAlignment);
|
||||
node["speakerMood"] = static_cast<u16>(style.speakerMood);
|
||||
node["cameraAttr"] = static_cast<u16>(style.cameraAttr);
|
||||
node["talkAnim"] = static_cast<u16>(style.talkAnim);
|
||||
node["faceAnim"] = static_cast<u16>(style.faceAnim);
|
||||
node["trailingData"] = style.trailingData;
|
||||
}
|
||||
|
||||
@@ -191,7 +195,7 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
|
||||
for (const auto& flow : mFlowNodes) {
|
||||
YAML::Node node{};
|
||||
node["type"] = flow_node_type_name(flow.type);
|
||||
node["group"] = flow.group;
|
||||
node["group"] = static_cast<u16>(flow.group);
|
||||
if (flow.patchIndex.has_value()) {
|
||||
node["patchIndex"] = flow.patchIndex.value();
|
||||
} else {
|
||||
@@ -202,22 +206,20 @@ std::optional<std::string> RandomizerContext::WriteToFile() {
|
||||
node["operation"] = flow.operation;
|
||||
}
|
||||
if (flow.type == FlowNodeType::MESSAGE) {
|
||||
write_flow_reference(node["message"], flow.message);
|
||||
node["message"] = write_flow_reference(flow.message);
|
||||
}
|
||||
if (flow.type != FlowNodeType::BRANCH) {
|
||||
write_flow_reference(node["next"], flow.next);
|
||||
node["next"] = write_flow_reference(flow.next);
|
||||
}
|
||||
for (const auto& result : flow.results) {
|
||||
YAML::Node resultNode{};
|
||||
write_flow_reference(resultNode, result);
|
||||
node["results"].push_back(resultNode);
|
||||
node["results"].push_back(write_flow_reference(result));
|
||||
}
|
||||
out["mFlowNodes"].push_back(node);
|
||||
}
|
||||
|
||||
for (const auto& message : mCustomMessages) {
|
||||
YAML::Node node{};
|
||||
node["group"] = message.group;
|
||||
node["group"] = static_cast<u16>(message.group);
|
||||
node["name"] = message.name;
|
||||
write_message_style(node["style"], message.style);
|
||||
for (const auto& [language, text] : message.text) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
class RandomizerContext {
|
||||
public:
|
||||
static constexpr u32 FORMAT_VERSION = 2;
|
||||
static constexpr u32 FORMAT_VERSION = 3;
|
||||
static constexpr size_t ACTR_CRC_SIZE = 32;
|
||||
static constexpr size_t TGSC_CRC_SIZE = 35; // 3 extra bytes for scale x, y, z
|
||||
static constexpr size_t OBJ_DELETE_SIZE = 1;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "rando_seed_generation.hpp"
|
||||
#include "config_store.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <map>
|
||||
@@ -107,6 +108,31 @@ UiMenuTabHandle g_menu_tab{};
|
||||
FileSelectGateWindowCtx g_file_select_window_ctx{};
|
||||
|
||||
namespace {
|
||||
std::vector<std::string> get_compatible_seed_hashes() {
|
||||
const std::filesystem::path seedDir = paths::GetRandomizerSeedsPath();
|
||||
std::filesystem::create_directories(seedDir);
|
||||
|
||||
std::vector<std::string> seedHashes;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(seedDir)) {
|
||||
if (!entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const YAML::Node seedData = LoadYAML(entry.path() / "seed.dat");
|
||||
if (seedData["formatVersion"] &&
|
||||
seedData["formatVersion"].as<u32>() == RandomizerContext::FORMAT_VERSION) {
|
||||
seedHashes.push_back(entry.path().filename().string());
|
||||
}
|
||||
} catch (const std::exception&) {
|
||||
// Incomplete or malformed seeds cannot be activated and should not be offered.
|
||||
}
|
||||
}
|
||||
|
||||
std::ranges::sort(seedHashes);
|
||||
return seedHashes;
|
||||
}
|
||||
|
||||
// Control Helpers
|
||||
ModResult add_button(UiElementHandle pane, const char* label, const char* help_rml,
|
||||
UiPressedFn on_pressed, void* userdata = nullptr, UiElementHandle* out_handle = nullptr)
|
||||
@@ -353,18 +379,9 @@ ModResult buildSeedManagementTab(ModContext* ctx, UiWindowHandle, UiElementHandl
|
||||
});
|
||||
|
||||
{
|
||||
std::filesystem::path seed_dir = paths::GetRandomizerSeedsPath();
|
||||
if (!std::filesystem::exists(seed_dir))
|
||||
std::filesystem::create_directory(seed_dir);
|
||||
|
||||
std::string help_rml = "Select a seed above to delete it.";
|
||||
|
||||
std::vector<std::string> seedHashes;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(seed_dir)) {
|
||||
if (entry.is_directory()) {
|
||||
seedHashes.push_back(entry.path().filename().string());
|
||||
}
|
||||
}
|
||||
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
|
||||
|
||||
std::vector<const char*> availableSeeds;
|
||||
for (const auto& hash : seedHashes) {
|
||||
@@ -380,20 +397,16 @@ ModResult buildSeedManagementTab(ModContext* ctx, UiWindowHandle, UiElementHandl
|
||||
out_value->int_value = 0;
|
||||
},
|
||||
[](ModContext*, void*, const UiControlValue* value) {
|
||||
int idx = 0;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
|
||||
if (entry.is_directory()) {
|
||||
if (idx == value->int_value) {
|
||||
std::string hash = entry.path().filename().string();
|
||||
if (randomizer_GetContext().mHash == hash) {
|
||||
randomizer_GetContext() = RandomizerContext{};
|
||||
}
|
||||
std::filesystem::remove_all(entry);
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
|
||||
if (value->int_value < 0 || static_cast<size_t>(value->int_value) >= seedHashes.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& hash = seedHashes[value->int_value];
|
||||
if (randomizer_GetContext().mHash == hash) {
|
||||
randomizer_GetContext() = RandomizerContext{};
|
||||
}
|
||||
std::filesystem::remove_all(paths::GetRandomizerSeedsPath() / hash);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1087,24 +1100,20 @@ void OnMenuTabSelected(ModContext* ctx, void*) {
|
||||
ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane,
|
||||
UiElementHandle rightPane, void*, ModError*)
|
||||
{
|
||||
std::filesystem::path seed_dir = paths::GetRandomizerSeedsPath();
|
||||
if (!std::filesystem::exists(seed_dir))
|
||||
std::filesystem::create_directory(seed_dir);
|
||||
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
|
||||
|
||||
std::string help_rml = "";
|
||||
if (std::filesystem::is_empty(seed_dir)) {
|
||||
if (seedHashes.empty()) {
|
||||
help_rml = "No seeds generated! You can generate a seed from the Seed Management Tab.";
|
||||
} else {
|
||||
help_rml = "Choose which seed you want to play.";
|
||||
}
|
||||
|
||||
std::vector<std::string> seedHashes;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(seed_dir)) {
|
||||
if (entry.is_directory()) {
|
||||
seedHashes.push_back(entry.path().filename().string());
|
||||
}
|
||||
if (!session::g_pending_seed_hash.empty() &&
|
||||
!std::ranges::contains(seedHashes, session::g_pending_seed_hash)) {
|
||||
session::g_pending_seed_hash.clear();
|
||||
}
|
||||
|
||||
|
||||
std::vector<const char*> availableSeeds;
|
||||
for (const auto& hash : seedHashes) {
|
||||
availableSeeds.push_back(hash.c_str());
|
||||
@@ -1116,30 +1125,23 @@ ModResult buildPlayTab(ModContext* ctx, UiWindowHandle, UiElementHandle leftPane
|
||||
availableSeeds.data(),
|
||||
availableSeeds.size(),
|
||||
[](ModContext*, void*, UiControlValue* out_value) {
|
||||
int idx = 0;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
|
||||
if (entry.is_directory()) {
|
||||
std::string hash = entry.path().filename().string();
|
||||
if (session::g_pending_seed_hash == hash) {
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
|
||||
const auto selected = std::ranges::find(seedHashes, session::g_pending_seed_hash);
|
||||
if (selected == seedHashes.end()) {
|
||||
out_value->int_value = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
out_value->int_value = idx;
|
||||
out_value->int_value = static_cast<int32_t>(std::distance(seedHashes.begin(), selected));
|
||||
},
|
||||
[](ModContext*, void*, const UiControlValue* value) {
|
||||
int idx = 0;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(paths::GetRandomizerSeedsPath())) {
|
||||
if (entry.is_directory()) {
|
||||
if (idx == value->int_value) {
|
||||
session::g_pending_seed_hash = entry.path().filename().string();
|
||||
break;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
const std::vector<std::string> seedHashes = get_compatible_seed_hashes();
|
||||
if (value->int_value < 0 || static_cast<size_t>(value->int_value) >= seedHashes.size()) {
|
||||
session::g_pending_seed_hash.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
session::g_pending_seed_hash = seedHashes[value->int_value];
|
||||
});
|
||||
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user