Merge branch 'main' of https://github.com/TwilitRealm/dusk into randomizer

This commit is contained in:
gymnast86
2026-04-15 10:42:17 -07:00
11 changed files with 340 additions and 89 deletions
+1 -1
+1
View File
@@ -1376,6 +1376,7 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStateShare.cpp
src/dusk/iso_validate.cpp
src/dusk/offset_ptr.cpp
src/dusk/OSContext.cpp
src/dusk/OSThread.cpp
+12
View File
@@ -363,6 +363,18 @@ int daMyna_c::destroy() {
mpMorf->stopZelAnime();
}
#ifdef TARGET_PC
// !@bug d_a_myna.rel unload used to zero these file-statics; with static linking they dangle across scenes.
daMyna_LightActor = NULL;
daMyna_evtTagActor0 = NULL;
daMyna_evtTagActor1 = NULL;
daMyna_actor_count = 0;
for (int i = 0; i < 10; i++) {
daMyna_targetActor[i] = NULL;
daMyna_subActor[i] = NULL;
}
#endif
#if DEBUG
l_HOSTIO.removeHIO();
#endif
+7 -1
View File
@@ -40,9 +40,15 @@ namespace dusk {
void ImGuiTextCenter(std::string_view text) {
ImGui::NewLine();
float fontSize = ImGui::CalcTextSize(text.data(), text.data() + text.size()).x;
float fontSize = ImGui::CalcTextSize(
text.data(),
text.data() + text.size(),
false,
ImGui::GetWindowSize().x).x;
ImGui::SameLine(ImGui::GetWindowSize().x / 2 - fontSize + fontSize / 2);
ImGui::PushTextWrapPos(ImGui::GetWindowSize().x);
ImGuiStringViewText(text);
ImGui::PopTextWrapPos();
}
bool ImGuiButtonCenter(std::string_view text) {
+67 -70
View File
@@ -9,107 +9,117 @@ namespace dusk {
void ImGuiMenuEnhancements::draw() {
if (ImGui::BeginMenu("Enhancements")) {
if (ImGui::BeginMenu("Quality of Life")) {
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
if (ImGui::BeginMenu("Gameplay")) {
ImGui::SeparatorText("Preferences");
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows you to quickly transform between forms\n"
"without having to talk to Midna.");
ImGui::SetTooltip("Mirrors the world horizontally, matching the Wii version of the game.");
}
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
config::ImGuiCheckbox("Disable Main HUD", getSettings().game.disableMainHUD);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
ImGui::SetTooltip("Disables the main HUD of the game.\n"
"Useful for recording or a more immersive experience!");
}
ImGui::SeparatorText("Difficulty");
config::ImGuiSliderInt("Damage Multiplier", getSettings().game.damageMultiplier, 1, 8, "x%d");
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Any hit will instantly kill you.");
}
config::ImGuiCheckbox("No Heart Drops", getSettings().game.noHeartDrops);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hearts will never drop from enemies,\n"
"pots and various other places.");
}
ImGui::SeparatorText("Quality of Life");
config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Wallet sizes are like in the HD version. (500, 1000, 2000)");
}
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
}
config::ImGuiCheckbox("Disable Rupee Cutscenes", getSettings().game.disableRupeeCutscenes);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time.");
}
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("Faster Climbing", getSettings().game.fastClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version.");
}
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
}
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
}
config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Prevents Link from playing a struggle animation\n"
"when grabbing ledges or climbing on vines.");
}
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
}
config::ImGuiCheckbox("Hide TV Settings Screen", getSettings().game.hideTvSettingsScreen);
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save.");
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the TV calibration screen shown when loading a save.");
}
config::ImGuiCheckbox("Skip Warning Screen", getSettings().game.skipWarningScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skips the warning screen shown when loading the game.");
ImGui::SetTooltip("Skip the warning screen shown when starting the game.");
}
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Preferences")) {
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game.");
}
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
config::ImGuiCheckbox("Disable Main HUD", getSettings().game.disableMainHUD);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Disables the main HUD of the game.\n"
"Useful for recording or a more immersive experience!");
ImGui::SetTooltip("Transform instantly by pressing R and Y simultaneously.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Graphics")) {
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
}
config::ImGuiCheckbox("Unlock Framerate", getSettings().game.enableFrameInterpolation);
const bool frameInterpolationHovered = ImGui::IsItemHovered();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f));
ImGui::TextUnformatted("[EXPERIMENTAL]");
ImGui::PopStyleColor();
if (frameInterpolationHovered || ImGui::IsItemHovered()) {
ImGui::SetTooltip("Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, animation glitches, or instability may occur.");
}
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
}
ImGui::EndMenu();
}
@@ -128,6 +138,10 @@ namespace dusk {
}
if (ImGui::BeginMenu("Input")) {
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
ImGui::SeparatorText("Gyro");
config::ImGuiCheckbox("Gyro Aim", getSettings().game.enableGyroAim);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Enables the gyroscope on supported controllers while aiming the\n"
@@ -139,9 +153,18 @@ namespace dusk {
config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch);
config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw);
ImGui::SeparatorText("Tools");
config::ImGuiCheckbox("Turbo Key", getSettings().game.enableTurboKeybind);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hold TAB to increase game speed by up to 4x.");
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::BeginMenu("Cheats")) {
config::ImGuiCheckbox("Fast Iron Boots", getSettings().game.enableFastIronBoots);
@@ -163,23 +186,6 @@ namespace dusk {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Difficulty")) {
config::ImGuiSliderInt("Damage Multiplier", getSettings().game.damageMultiplier, 1, 8, "x%d");
config::ImGuiCheckbox("No Heart Drops", getSettings().game.noHeartDrops);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hearts will never drop from enemies,\n"
"pots and various other places.");
}
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Any hit will instantly kill you.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Technical")) {
config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches);
if (ImGui::IsItemHovered()) {
@@ -190,15 +196,6 @@ namespace dusk {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Tools")) {
config::ImGuiCheckbox("Turbo Key", getSettings().game.enableTurboKeybind);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hold TAB to increase game speed by up to 4x.");
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
}
+38 -2
View File
@@ -7,6 +7,7 @@
#include "ImGuiConsole.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "../iso_validate.hpp"
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_error.h>
@@ -26,15 +27,42 @@ static constexpr std::array<SDL_DialogFileFilter, 2> skGameDiscFileFilters{{
{"All Files", "*"},
}};
static std::string ShowIsoInvalidError(const iso::ValidationError code) {
using namespace std::literals::string_literals;
switch (code) {
case iso::ValidationError::IOError:
return "Unknown IO error occurred"s;
case iso::ValidationError::InvalidImage:
return "Unable to interpret selected file as a disc image"s;
case iso::ValidationError::WrongGame:
return "Selected disc image is for a different game"s;
case iso::ValidationError::WrongVersion:
return "Selected disc image is for an unsupported version of the game. Only North American GameCube (NTSC/GZ2E01) is supported at this time."s;
case iso::ValidationError::ExecutableMismatch:
return "Selected disc image contains modified executable files."s;
default:
return "Unknown error"s;
}
}
void fileDialogCallback(void* userdata, const char* const* filelist, [[maybe_unused]] int filter) {
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
self->m_errorString.clear();
if (filelist != nullptr) {
if (filelist[0] == nullptr) {
// Cancelled
self->m_selectedIsoPath.clear();
} else {
self->m_selectedIsoPath = filelist[0];
getSettings().backend.isoPath.setValue(self->m_selectedIsoPath);
const auto path = filelist[0];
const auto ret = iso::validate(path);
if (ret != iso::ValidationError::Success) {
self->m_selectedIsoPath.clear();
self->m_errorString = std::move(ShowIsoInvalidError(ret));
return;
}
self->m_selectedIsoPath = path;
getSettings().backend.isoPath.setValue(path);
config::Save();
}
} else {
@@ -111,6 +139,10 @@ void ImGuiPreLaunchWindow::drawMainMenu() {
ImGui::PushFont(ImGuiEngine::fontLarge);
if (!isSelectedPathValid()) {
if (!m_errorString.empty()) {
ImGuiTextCenter(m_errorString);
}
if (ImGuiButtonCenter("Select disc image...")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
@@ -148,6 +180,10 @@ void ImGuiPreLaunchWindow::drawOptions() {
if (ImGui::BeginChild("OptionsChild", ImVec2(childWidth, endCursorY - cursorY),
ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground))
{
if (!m_errorString.empty()) {
ImGuiTextCenter(m_errorString);
}
ImGui::InputText("Game ISO Path", &m_selectedIsoPath, ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("Set")) {
+67 -13
View File
@@ -445,7 +445,7 @@ namespace dusk {
}
if (ImGui::BeginTabItem("Minigame")) {
//DrawFlagsTab();
drawMinigameTab();
ImGui::EndTabItem();
}
@@ -829,8 +829,8 @@ namespace dusk {
ImGuiBeginGroupPanel("Items", { 200, 100 });
for (int slot = 0; slot < 24; slot++) {
ImGui::Text("Slot %02d: ", slot);
ImGui::SameLine();
ImGui::Text("Slot %02d (%s): ", slot, itemMap.find(getSlotDefault(slot))->second.m_name.c_str());
ImGui::SameLine(240.0f);
if (ImGui::BeginCombo(fmt::format("##ItemComboBox{}", slot).c_str(), itemMap.find(item.mItems[slot])->second.m_name.c_str())) {
if (ImGui::Selectable("None")) {
dComIfGs_setItem(slot, dItemNo_NONE_e);
@@ -856,7 +856,27 @@ namespace dusk {
}
ImGuiEndGroupPanel();
dSv_player_item_record_c& itemRecord = dComIfGs_getSaveData()->getPlayer().getItemRecord();
dSv_player_item_max_c& itemMax = dComIfGs_getSaveData()->getPlayer().getItemMax();
ImGuiBeginGroupPanel("Item Max Capacities", { 200, 100 });
ImGui::InputScalar("Arrows Max", ImGuiDataType_U8, &itemMax.mItemMax[0]);
ImGui::InputScalar("Normal Bombs Max", ImGuiDataType_U8, &itemMax.mItemMax[1]);
ImGui::InputScalar("Water Bombs Max", ImGuiDataType_U8, &itemMax.mItemMax[2]);
ImGui::InputScalar("Bomblings Max", ImGuiDataType_U8, &itemMax.mItemMax[3]);
ImGuiEndGroupPanel();
ImGuiBeginGroupPanel("Item Amounts", { 200, 100 });
ImGui::InputScalar("Arrows Amount", ImGuiDataType_U8, &itemRecord.mArrowNum);
ImGui::InputScalar("Slingshot Amount", ImGuiDataType_U8, &itemRecord.mPachinkoNum);
ImGui::InputScalar("Bomb Bag 1 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[0]);
ImGui::InputScalar("Bomb Bag 2 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[1]);
ImGui::InputScalar("Bomb Bag 3 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[2]);
ImGui::InputScalar("Bottle 1 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[0]);
ImGui::InputScalar("Bottle 2 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[1]);
ImGui::InputScalar("Bottle 3 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[2]);
ImGui::InputScalar("Bottle 4 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[3]);
ImGuiEndGroupPanel();
}
static inline void setItemFirstBit(u8 itemNo, bool owned) {
@@ -902,27 +922,42 @@ namespace dusk {
void ImGuiSaveEditor::drawCollectionTab() {
if (ImGui::TreeNode("Equipment")) {
if (ImGui::TreeNode("Swords")) {
static u8 sword_list[] = {
dItemNo_SWORD_e,
dItemNo_MASTER_SWORD_e,
dItemNo_WOOD_STICK_e,
dItemNo_LIGHT_SWORD_e,
};
for (int i = 0; i < 4; i++) {
bool got = dComIfGs_isCollectSword((u8)i) != 0;
bool got = dComIfGs_isItemFirstBit(sword_list[i]) != 0;
if (ImGui::Checkbox(fmt::format("{0}##sword_{1}", sSwordNames[i], i).c_str(), &got)) {
if (got) dComIfGs_setCollectSword((u8)i);
else dComIfGs_offCollectSword((u8)i);
if (got) dComIfGs_onItemFirstBit(sword_list[i]);
else dComIfGs_offItemFirstBit(sword_list[i]);
}
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Shields")) {
static u8 shield_list[] = {
dItemNo_SHIELD_e,
dItemNo_WOOD_SHIELD_e,
dItemNo_HYLIA_SHIELD_e,
};
for (int i = 0; i < 3; i++) {
bool got = dComIfGs_isCollectShield((u8)i) != 0;
bool got = dComIfGs_isItemFirstBit(shield_list[i]) != 0;
if (ImGui::Checkbox(fmt::format("{0}##shield_{1}", sShieldNames[i], i).c_str(), &got)) {
if (got) dComIfGs_setCollectShield((u8)i);
else dComIfGs_offCollectShield((u8)i);
if (got) dComIfGs_onItemFirstBit(shield_list[i]);
else dComIfGs_offItemFirstBit(shield_list[i]);
}
}
ImGui::TreePop();
}
// TODO: the game checks if you're in ranch clothes, and if so won't display any other tunics in the menu
// find a way to deal with this later
if (ImGui::TreeNode("Tunics")) {
bool ordonClothes = dComIfGs_isItemFirstBit(dItemNo_WEAR_CASUAL_e) != 0;
if (ImGui::Checkbox("Ordon Clothes##tunic_ordon", &ordonClothes)) {
@@ -1235,7 +1270,7 @@ namespace dusk {
}
ImGuiEndGroupPanel();
ImVec2 cursor = ImGui::GetCursorPos();
ImVec2 start_cursor = ImGui::GetCursorPos();
ImGui::SameLine();
@@ -1245,13 +1280,17 @@ namespace dusk {
}
ImGuiEndGroupPanel();
ImGui::SetCursorPos(cursor);
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(start_cursor);
ImGuiBeginGroupPanel("Item", { 100, 100 });
for (int j = 0; j < 1; j++) {
drawFlagList(fmt::format("##_item{}", j).c_str(), membit.mItem[j]);
}
ImGuiEndGroupPanel();
ImGui::SetCursorPos({ start_cursor.x, cursor.y });
}
void ImGuiSaveEditor::drawFlagsTab() {
@@ -1328,7 +1367,7 @@ namespace dusk {
dSv_event_c& event = dComIfGs_getSaveData()->mEvent;
for (int e = 0; e < 255; e++) {
ImGui::Text("%03d ", e);
ImGui::SameLine();
ImGui::SameLine(80.0f);
for (int i = 7; i >= 0; i--) {
bool flag = event.mEvent[e] & (1 << i);
if (ImGui::Checkbox(fmt::format("##event{0}{1}", e, i).c_str(), &flag)) {
@@ -1345,10 +1384,25 @@ namespace dusk {
}
}
void ImGuiSaveEditor::drawMinigameTab() {
dSv_MiniGame_c& minigame = dComIfGs_getSaveData()->getMiniGame();
InputScalarBE("STAR Game Time (Milliseconds)", ImGuiDataType_U32, &minigame.mHookGameTime);
InputScalarBE("Snowboard Race Time (Milliseconds)", ImGuiDataType_U32, &minigame.mRaceGameTime);
InputScalarBE("Fruit-Pop-Flight Score", ImGuiDataType_U32, &minigame.mBalloonScore);
}
void ImGuiSaveEditor::drawConfigTab() {
dSv_player_config_c& config = dComIfGs_getSaveData()->getPlayer().getConfig();
ImGui::Checkbox("Enable Vibration", (bool*)&config.mVibration);
if (ImGui::BeginCombo("Target Type", "Hold")) {
static const char* kTargetTypeNames[] = {"Hold", "Switch"};
if (ImGui::BeginCombo("Target Type", kTargetTypeNames[config.mAttentionType])) {
if (ImGui::Selectable("Hold")) {
config.mAttentionType = 0;
}
if (ImGui::Selectable("Switch")) {
config.mAttentionType = 1;
}
ImGui::EndCombo();
}
+1
View File
@@ -17,6 +17,7 @@ namespace dusk {
void drawInventoryTab();
void drawCollectionTab();
void drawFlagsTab();
void drawMinigameTab();
void drawConfigTab();
private:
+126
View File
@@ -0,0 +1,126 @@
#include "iso_validate.hpp"
#include <nod.h>
#include <span>
#include "SDL3/SDL_iostream.h"
namespace dusk::iso {
constexpr const char* TP_GAME_IDS[] = {
"GZ2E01", // GCN USA
"GZ2P01", // GCN PAL
"GZ2J01", // GCN JPN
"RZDE01", // Wii USA
"RZDP01", // Wii PAL
"RZDJ01", // Wii JPN
"RZDK01", // Wii KOR
};
constexpr const char* SUPPORTED_TP_GAME_IDS[] = {
"GZ2E01", // GCN USA
};
template <size_t N>
constexpr bool matches(const char (&id)[6], const char* const (&valid)[N]) {
for (auto elem : valid) {
if (strncmp(id, elem, 6) == 0) {
return true;
}
}
return false;
}
struct NodHandleWrapper {
NodHandle* handle;
NodHandleWrapper() : handle(nullptr) {
}
~NodHandleWrapper() {
if (handle != nullptr) {
nod_free(handle);
handle = nullptr;
}
}
};
static ValidationError convertNodError(NodResult result) {
switch (result) {
case NOD_RESULT_ERR_IO:
return ValidationError::IOError;
case NOD_RESULT_ERR_FORMAT:
return ValidationError::InvalidImage;
default:
return ValidationError::Unknown;
}
}
s64 StreamReadAt(void* user_data, u64 offset, void* out, size_t len) {
if (len == 0) {
return 0;
}
auto io = static_cast<SDL_IOStream*>(user_data);
auto ret = SDL_SeekIO(io, static_cast<s64>(offset), SDL_IO_SEEK_SET);
if (ret < 0) {
return -1;
}
auto read = SDL_ReadIO(io, out, len);
if (read == 0) {
if (SDL_GetIOStatus(io) == SDL_IO_STATUS_EOF) {
return 0;
}
return -1;
}
return static_cast<s64>(read);
}
s64 StreamLength(void* user_data) {
auto io = static_cast<SDL_IOStream*>(user_data);
return SDL_GetIOSize(io);
}
void StreamClose(void* user_data) {
auto io = static_cast<SDL_IOStream*>(user_data);
SDL_CloseIO(io);
}
ValidationError validate(const char* path) {
NodHandleWrapper disc;
const auto sdlStream = SDL_IOFromFile(path, "rb");
const NodDiscStream nod_stream {
.user_data = sdlStream,
.read_at = StreamReadAt,
.stream_len = StreamLength,
.close = StreamClose,
};
auto result = nod_disc_open_stream(&nod_stream, nullptr, &disc.handle);
if (disc.handle == nullptr || result != NOD_RESULT_OK) {
return convertNodError(result);
}
NodDiscHeader header{};
result = nod_disc_header(disc.handle, &header);
if (result != NOD_RESULT_OK) {
return convertNodError(result);
}
if (!matches(header.game_id, TP_GAME_IDS)) {
return ValidationError::WrongGame;
}
if (!matches(header.game_id, SUPPORTED_TP_GAME_IDS)) {
return ValidationError::WrongVersion;
}
return ValidationError::Success;
}
} // namespace dusk::iso
+18
View File
@@ -0,0 +1,18 @@
#ifndef DUSK_ISO_VALIDATE_HPP
#define DUSK_ISO_VALIDATE_HPP
namespace dusk::iso {
enum class ValidationError : u8 {
Success = 0,
IOError,
InvalidImage,
WrongGame,
WrongVersion,
ExecutableMismatch,
Unknown
};
ValidationError validate(const char* path);
}
#endif // DUSK_ISO_VALIDATE_HPP
+2 -2
View File
@@ -2383,8 +2383,8 @@ void mDoExt_3DlineMat0_c::draw() {
int var_r26 = (field_0x14 << 1) & 0xFFFF;
for (int i = 0; i < field_0x10; i++) {
GXSETARRAY(GX_VA_POS, field_0x18->field_0x8[field_0x16], sizeof(cXyz) * var_r26, sizeof(cXyz), true);
GXSETARRAY(GX_VA_NRM, field_0x18->field_0x10[field_0x16], 3 * var_r26, 3, true);
GXSETARRAY(GX_VA_POS, var_r28->field_0x8[field_0x16], sizeof(cXyz) * var_r26, sizeof(cXyz), true);
GXSETARRAY(GX_VA_NRM, var_r28->field_0x10[field_0x16], 3 * var_r26, 3, true);
GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT0, var_r26);
for (u16 j = 0; j < (u16)var_r26; j++) {