This commit is contained in:
Lurs
2026-03-24 11:20:12 +01:00
17 changed files with 976 additions and 266 deletions
+3
View File
@@ -68,3 +68,6 @@ Pass the disc image as a positional argument. Supported formats: ISO (GCM), RVZ,
build/dusk/dusk /path/to/game.rvz
```
If no path is specified, Dusk defaults to `game.iso` in the current working directory.
#### 30 FPS on Debug
When compiled fully in a Debug the game runs too slowly to hit playable 30 FPS. To avoid this, you can set a CMake cache variable to optimize specific critical files without hampering debuggability in the rest of the program: `-DDUSK_SELECTED_OPT=ON`. When building for MSVC (Windows) you must also modify `CMAKE_CXX_FLAGS_DEBUG` and `CMAKE_C_FLAGS_DEBUG` to remove `/RTC1` from the flags, like so: `-DCMAKE_CXX_FLAGS_DEBUG="/MDd /Zi /Ob0 /Od" -DCMAKE_C_FLAGS_DEBUG="/MDd /Zi /Ob0 /Od"`
+1
View File
@@ -1348,6 +1348,7 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiControllerOverlay.cpp
src/dusk/imgui/ImGuiStubLog.cpp
src/dusk/imgui/ImGuiMapLoader.cpp
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/offset_ptr.cpp
src/dusk/OSContext.cpp
src/dusk/OSThread.cpp
-30
View File
@@ -163,7 +163,6 @@ public:
u8 getTransformStatus() const { return mTransformStatus; }
void setTransformStatus(u8 i_status) { mTransformStatus = i_status; }
private:
/* 0x00 */ BE(u16) mMaxLife;
/* 0x02 */ BE(u16) mLife;
/* 0x04 */ BE(u16) mRupee;
@@ -201,7 +200,6 @@ public:
u16 getDate() const { return mDate; }
void setDate(u16 i_date) { mDate = i_date; }
private:
/* 0x00 */ BE(OSTime) mDateIpl;
/* 0x08 */ u8 mTransformLevelFlag;
/* 0x09 */ u8 mDarkClearLevelFlag;
@@ -228,7 +226,6 @@ public:
const char* getStageName() const { return mName; }
s8 getRoomNo() const { return mRoomNo; }
private:
#if TARGET_PC
/* 0x00 */ BE(Vec) mPos;
#else
@@ -248,7 +245,6 @@ public:
u8 getPlayerStatus() const { return mPlayerStatus; }
s8 getRoomNo() const { return mRoomNo; }
private:
/* 0x00 */ char mName[8];
/* 0x08 */ u8 mPlayerStatus;
/* 0x09 */ s8 mRoomNo;
@@ -277,8 +273,6 @@ public:
void offFieldDataExistFlag() { mFieldDataExistFlag = false; }
void onFieldDataExistFlag() { mFieldDataExistFlag = true; }
private:
#if TARGET_PC
/* 0x00 */ BE(Vec) mPos;
#else
@@ -312,7 +306,6 @@ public:
void setWarpAcceptStage(s8 accept) { mWarpAcceptStage = accept; }
void resetWarpAcceptStage() { mWarpAcceptStage = -1; }
private:
#if TARGET_PC
/* 0x00 */ BE(Vec) mPos;
#else
@@ -356,7 +349,6 @@ public:
static const int BOMB_BAG_MAX = 3;
static const int BOTTLE_MAX = 4;
private:
/* 0x00 */ u8 mItems[24];
/* 0x18 */ u8 mItemSlots[24];
}; // Size: 0x30
@@ -368,7 +360,6 @@ public:
void offFirstBit(u8 i_itemNo);
int isFirstBit(u8 i_itemNo) const;
private:
/* 0x0 */ BE(u32) mItemFlags[8];
}; // Size: 0x20
@@ -386,7 +377,6 @@ public:
u8 getPachinkoNum() const { return mPachinkoNum; }
void setPachinkoNum(u8 i_num) { mPachinkoNum = i_num; }
private:
/* 0x0 */ u8 mArrowNum;
/* 0x1 */ u8 mBombNum[3];
/* 0x4 */ u8 mBottleNum[4];
@@ -403,7 +393,6 @@ public:
u8 getArrowNum() const { return mItemMax[ARROW_MAX]; }
void setArrowNum(u8 i_maxNum) { mItemMax[ARROW_MAX] = i_maxNum; }
private:
/* 0x0 */ u8 mItemMax[8];
}; // Size: 0x8
@@ -429,7 +418,6 @@ public:
}
}
private:
/* 0x0 */ u8 mItem[8];
/* 0x8 */ u8 unk8;
/* 0x9 */ u8 mCrystal;
@@ -443,7 +431,6 @@ class dSv_player_wolf_c {
public:
void init();
private:
/* 0x0 */ u8 unk0[3];
/* 0x3 */ u8 unk3;
}; // Size: 0x4
@@ -457,7 +444,6 @@ public:
void offLightDropGetFlag(u8 i_nowLevel);
BOOL isLightDropGetFlag(u8 i_nowLevel) const;
private:
/* 0x0 */ u8 mLightDropNum[4];
/* 0x4 */ u8 mLightDropGetFlag;
/* 0x5 */ u8 unk5[3];
@@ -473,7 +459,6 @@ public:
u8 getGetNumber(int i_no) { return mGetNumber[i_no]; }
void setGetNumber(int i_no, u8 i_value) { mGetNumber[i_no] = i_value; }
private:
/* 0x00 */ BE(u32) mLetterGetFlags[2];
/* 0x08 */ BE(u32) mLetterReadFlags[2];
/* 0x10 */ u8 mGetNumber[64];
@@ -487,7 +472,6 @@ public:
u8 getMaxSize(int i_sizeIndex) const { return mMaxSize[i_sizeIndex]; }
void setMaxSize(int i_sizeIndex, u8 i_size) { mMaxSize[i_sizeIndex] = i_size; }
private:
/* 0x00 */ BE(u16) mFishCount[16];
/* 0x20 */ u8 mMaxSize[16];
}; // Size: 0x34
@@ -508,7 +492,6 @@ public:
void setHorseName(const char* i_name) { strcpy(mHorseName, i_name); }
u8 getClearCount() const { return mClearCount; }
private:
/* 0x00 */ BE(u64) unk0;
/* 0x08 */ BE(s64) mTotalTime;
/* 0x10 */ BE(u16) unk16;
@@ -555,7 +538,6 @@ public:
LANGUAGE_ITALIAN,
};
private:
/* 0x0 */ u8 mRuby;
/* 0x1 */ u8 mSoundMode;
/* 0x2 */ u8 mAttentionType; // Lock-On Type; 0 : hold, 1 : switch
@@ -682,7 +664,6 @@ public:
void offStageBossEnemy2() { offDungeonItem(STAGE_BOSS_ENEMY_2); }
s32 isStageBossEnemy2() const { return isDungeonItem(STAGE_BOSS_ENEMY_2); }
private:
/* 0x00 */ BE(u32) mTbox[2];
/* 0x08 */ BE(u32) mSwitch[4];
/* 0x18 */ BE(u32) mItem[1];
@@ -701,7 +682,6 @@ public:
void* getPEventBit() { return (void*)mEvent; }
private:
/* 0x0 */ u8 mEvent[256];
}; // Size: 0x100
@@ -716,7 +696,6 @@ public:
u32 getRaceGameTime() const { return mRaceGameTime; }
void setRaceGameTime(u32 i_time) { mRaceGameTime = i_time; }
private:
/* 0x00 */ u8 unk0[1][4];
/* 0x04 */ BE(u32) mHookGameTime;
/* 0x08 */ BE(u32) mBalloonScore;
@@ -732,7 +711,6 @@ public:
dSv_memBit_c& getBit() { return mBit; }
const dSv_memBit_c& getBit() const { return mBit; }
private:
/* 0x0 */ dSv_memBit_c mBit;
}; // Size: 0x20
@@ -746,7 +724,6 @@ public:
void offVisitedRoom(int i_no);
BOOL isVisitedRoom(int i_no);
private:
/* 0x0 */ BE(u32) mVisitedRoom[2];
}; // Size: 0x8
@@ -765,7 +742,6 @@ public:
void reset() { mStageNo = -1; }
private:
/* 0x00 */ s8 mStageNo;
/* 0x01 */ u8 unk1;
/* 0x04 */ u32 mSwitch[2];
@@ -793,7 +769,6 @@ public:
void offOneItem(int i_no);
BOOL isOneItem(int i_no) const;
private:
/* 0x00 */ BE(u16) mSwitch[2];
/* 0x04 */ BE(u16) mRoomSwitch;
/* 0x06 */ BE(u16) mItem[2];
@@ -810,7 +785,6 @@ public:
static const int ACTOR_MAX = 0x80;
private:
/* 0x00 */ u32 mActorFlags[4];
}; // Size: 0x10
@@ -826,7 +800,6 @@ public:
dSv_zoneActor_c& getActor() { return mActor; }
const dSv_zoneActor_c& getActor() const { return mActor; }
private:
/* 0x00 */ s8 mRoomNo;
/* 0x01 */ u8 unk1;
/* 0x02 */ dSv_zoneBit_c mBit;
@@ -855,7 +828,6 @@ public:
mLastAngleY = angle;
}
private:
/* 0x00 */ s8 mRoomNo;
/* 0x01 */ u8 field_0x01[3];
/* 0x04 */ s16 mStartPoint;
@@ -891,7 +863,6 @@ public:
f32 getCameraFvy() const { return mCameraFvy; }
void setCameraFvy(f32 i_fvy) { mCameraFvy = i_fvy; }
private:
/* 0x00 */ cXyz mPosition;
/* 0x0C */ u32 mParam;
/* 0x10 */ s16 mAngleY;
@@ -928,7 +899,6 @@ public:
static const int STAGE_MAX = 32;
static const int STAGE2_MAX = 64;
// private:
/* 0x000 */ dSv_player_c mPlayer;
/* 0x1F0 */ dSv_memory_c mSave[STAGE_MAX];
/* 0x5F0 */ dSv_memory2_c mSave2[STAGE2_MAX];
+6 -47
View File
@@ -14,43 +14,6 @@
#include "JSystem/JKernel/JKRHeap.h"
// ============================================================================
// Malloc-based allocator to bypass JKRHeap operator new/delete
// Without this, side-table allocations call operator new -> JKRHeap::alloc
// -> OSLockMutex -> GetMutexData -> operator new ... infinite recursion.
// ============================================================================
template<typename T>
struct MallocAllocator {
using value_type = T;
MallocAllocator() = default;
template<typename U> MallocAllocator(const MallocAllocator<U>&) noexcept {}
T* allocate(std::size_t n) {
void* p = std::malloc(n * sizeof(T));
if (!p) throw std::bad_alloc();
return static_cast<T*>(p);
}
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
template<typename U> bool operator==(const MallocAllocator<U>&) const noexcept { return true; }
template<typename U> bool operator!=(const MallocAllocator<U>&) const noexcept { return false; }
};
template<typename T>
struct MallocDeleter {
void operator()(T* p) const {
p->~T();
std::free(p);
}
};
template<typename T, typename... Args>
std::unique_ptr<T, MallocDeleter<T>> make_malloc_unique(Args&&... args) {
void* mem = std::malloc(sizeof(T));
if (!mem) throw std::bad_alloc();
T* obj = JKR_NEW_ARGS (mem) T(std::forward<Args>(args)...);
return std::unique_ptr<T, MallocDeleter<T>>(obj);
}
// ============================================================================
// Side-table: native mutex per OSMutex
// ============================================================================
@@ -59,17 +22,13 @@ struct PCMutexData {
std::recursive_mutex nativeMutex;
};
template<typename K, typename V>
using MallocMap = std::unordered_map<K, V, std::hash<K>, std::equal_to<K>,
MallocAllocator<std::pair<const K, V>>>;
// Lazy-initialized to avoid DLL static init crashes
static std::mutex& GetMutexMapMutex() {
static std::mutex mtx;
return mtx;
}
static MallocMap<OSMutex*, std::unique_ptr<PCMutexData, MallocDeleter<PCMutexData>>>& GetMutexMap() {
static MallocMap<OSMutex*, std::unique_ptr<PCMutexData, MallocDeleter<PCMutexData>>> map;
static std::unordered_map<OSMutex*, std::unique_ptr<PCMutexData>>& GetMutexMap() {
static std::unordered_map<OSMutex*, std::unique_ptr<PCMutexData>> map;
return map;
}
@@ -78,7 +37,7 @@ static PCMutexData& GetMutexData(OSMutex* mutex) {
auto& map = GetMutexMap();
auto it = map.find(mutex);
if (it == map.end()) {
auto result = map.emplace(mutex, make_malloc_unique<PCMutexData>());
auto result = map.emplace(mutex, std::make_unique<PCMutexData>());
return *result.first->second;
}
return *it->second;
@@ -97,8 +56,8 @@ static std::mutex& GetCondMapMutex() {
static std::mutex mtx;
return mtx;
}
static MallocMap<OSCond*, std::unique_ptr<PCCondData, MallocDeleter<PCCondData>>>& GetCondMap() {
static MallocMap<OSCond*, std::unique_ptr<PCCondData, MallocDeleter<PCCondData>>> map;
static std::unordered_map<OSCond*, std::unique_ptr<PCCondData>>& GetCondMap() {
static std::unordered_map<OSCond*, std::unique_ptr<PCCondData>> map;
return map;
}
@@ -107,7 +66,7 @@ static PCCondData& GetCondData(OSCond* cond) {
auto& map = GetCondMap();
auto it = map.find(cond);
if (it == map.end()) {
auto result = map.emplace(cond, make_malloc_unique<PCCondData>());
auto result = map.emplace(cond, std::make_unique<PCCondData>());
return *result.first->second;
}
return *it->second;
+8 -57
View File
@@ -18,45 +18,6 @@
#include "JSystem/JKernel/JKRHeap.h"
// ============================================================================
// Malloc-based allocator to bypass JKRHeap operator new/delete
// ============================================================================
template<typename T>
struct MallocAllocator {
using value_type = T;
MallocAllocator() = default;
template<typename U> MallocAllocator(const MallocAllocator<U>&) noexcept {}
T* allocate(std::size_t n) {
void* p = std::malloc(n * sizeof(T));
if (!p) throw std::bad_alloc();
return static_cast<T*>(p);
}
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
template<typename U> bool operator==(const MallocAllocator<U>&) const noexcept { return true; }
template<typename U> bool operator!=(const MallocAllocator<U>&) const noexcept { return false; }
};
template<typename T>
struct MallocDeleter {
void operator()(T* p) const {
p->~T();
std::free(p);
}
};
template<typename T, typename... Args>
std::unique_ptr<T, MallocDeleter<T>> make_malloc_unique(Args&&... args) {
void* mem = std::malloc(sizeof(T));
if (!mem) throw std::bad_alloc();
T* obj = JKR_NEW_ARGS (mem) T(std::forward<Args>(args)...);
return std::unique_ptr<T, MallocDeleter<T>>(obj);
}
template<typename K, typename V>
using MallocMap = std::unordered_map<K, V, std::hash<K>, std::equal_to<K>,
MallocAllocator<std::pair<const K, V>>>;
// ============================================================================
// Side-table: native thread data per OSThread
// ============================================================================
@@ -76,8 +37,8 @@ static std::mutex& GetThreadDataMutex() {
static std::mutex mtx;
return mtx;
}
static MallocMap<OSThread*, std::unique_ptr<PCThreadData, MallocDeleter<PCThreadData>>>& GetThreadDataMap() {
static MallocMap<OSThread*, std::unique_ptr<PCThreadData, MallocDeleter<PCThreadData>>> map;
static std::unordered_map<OSThread*, std::unique_ptr<PCThreadData>>& GetThreadDataMap() {
static std::unordered_map<OSThread*, std::unique_ptr<PCThreadData>> map;
return map;
}
@@ -86,8 +47,8 @@ static std::mutex& GetQueueCvMutex() {
static std::mutex mtx;
return mtx;
}
static MallocMap<OSThreadQueue*, std::unique_ptr<std::condition_variable, MallocDeleter<std::condition_variable>>>& GetQueueCvMap() {
static MallocMap<OSThreadQueue*, std::unique_ptr<std::condition_variable, MallocDeleter<std::condition_variable>>> map;
static std::unordered_map<OSThreadQueue*, std::unique_ptr<std::condition_variable>>& GetQueueCvMap() {
static std::unordered_map<OSThreadQueue*, std::unique_ptr<std::condition_variable>> map;
return map;
}
@@ -96,7 +57,7 @@ static std::condition_variable& GetQueueCV(OSThreadQueue* queue) {
auto& map = GetQueueCvMap();
auto it = map.find(queue);
if (it == map.end()) {
auto result = map.emplace(queue, make_malloc_unique<std::condition_variable>());
auto result = map.emplace(queue, std::make_unique<std::condition_variable>());
return *result.first->second;
}
return *it->second;
@@ -295,7 +256,7 @@ int OSCreateThread(OSThread* thread, void* (*func)(void*), void* param,
// Create side-table entry (but don't start the thread yet)
{
auto data = make_malloc_unique<PCThreadData>();
auto data = std::make_unique<PCThreadData>();
data->func = func;
data->param = param;
@@ -649,24 +610,14 @@ s32 OSEnableScheduler(void) {
// ============================================================================
BOOL OSDisableInterrupts(void) {
GetInterruptMutex().lock();
sInterruptLockCount++;
return (BOOL)(sInterruptLockCount > 1); // TRUE if was already locked
return FALSE;
}
BOOL OSRestoreInterrupts(BOOL level) {
if (sInterruptLockCount > 0) {
sInterruptLockCount--;
GetInterruptMutex().unlock();
}
return level;
return FALSE;
}
BOOL OSEnableInterrupts(void) {
if (sInterruptLockCount > 0) {
sInterruptLockCount--;
GetInterruptMutex().unlock();
}
return FALSE;
}
+86
View File
@@ -7,6 +7,7 @@
#include "fmt/format.h"
#include "imgui.h"
#include "aurora/gfx.h"
#include <imgui_internal.h>
#include "ImGuiConsole.hpp"
@@ -82,6 +83,91 @@ namespace dusk {
return result;
}
// from https://github.com/ocornut/imgui/issues/1496#issuecomment-569892444
void ImGuiBeginGroupPanel(const char* name, const ImVec2& size) {
ImGui::BeginGroup();
auto cursorPos = ImGui::GetCursorScreenPos();
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
auto frameHeight = ImGui::GetFrameHeight();
ImGui::BeginGroup();
ImVec2 effectiveSize = size;
if (size.x < 0.0f)
effectiveSize.x = ImGui::GetContentRegionAvail().x;
else
effectiveSize.x = size.x;
ImGui::Dummy(ImVec2(effectiveSize.x, 0.0f));
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::SameLine(0.0f, 0.0f);
ImGui::BeginGroup();
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::SameLine(0.0f, 0.0f);
ImGui::TextUnformatted(name);
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(0.0, frameHeight + itemSpacing.y));
ImGui::BeginGroup();
ImGui::PopStyleVar(2);
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x -= frameHeight * 0.5f;
ImGui::GetCurrentWindow()->WorkRect.Max.x -= frameHeight * 0.5f;
ImGui::GetCurrentWindow()->Size.x -= frameHeight;
ImGui::PushItemWidth(effectiveSize.x - frameHeight);
}
// from https://github.com/ocornut/imgui/issues/1496#issuecomment-569892444
void ImGuiEndGroupPanel() {
ImGui::PopItemWidth();
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
auto frameHeight = ImGui::GetFrameHeight();
// workaround for incorrect capture of columns/table width by placing
// zero-sized dummy element in the same group, this ensure
// max X cursor position is updated correctly
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(0.0f, 0.0f));
ImGui::EndGroup();
ImGui::EndGroup();
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::Dummy(ImVec2(0.0, frameHeight - frameHeight * 0.5f - itemSpacing.y));
ImGui::EndGroup();
auto itemMin = ImGui::GetItemRectMin();
auto itemMax = ImGui::GetItemRectMax();
ImVec2 halfFrame = ImVec2((frameHeight * 0.25f) * 0.5f, frameHeight * 0.5f);
ImGui::GetWindowDrawList()->AddRect(
ImVec2(itemMin.x + halfFrame.x, itemMin.y + halfFrame.y),
ImVec2(itemMax.x - halfFrame.x, itemMax.y),
ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Border)),
halfFrame.x);
ImGui::PopStyleVar(2);
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x += frameHeight * 0.5f;
ImGui::GetCurrentWindow()->WorkRect.Max.x += frameHeight * 0.5f;
ImGui::GetCurrentWindow()->Size.x += frameHeight;
ImGui::Dummy(ImVec2(0.0f, 0.0f));
ImGui::EndGroup();
}
ImGuiConsole g_imguiConsole;
ImGuiConsole::ImGuiConsole() {}
+2
View File
@@ -34,6 +34,8 @@ namespace dusk {
void SetOverlayWindowLocation(int corner);
bool ShowCornerContextMenu(int& corner, int avoidCorner);
void ImGuiStringViewText(std::string_view text);
void ImGuiBeginGroupPanel(const char* name, const ImVec2& size);
void ImGuiEndGroupPanel();
}
void DuskDebugPad();
+2 -4
View File
@@ -12,19 +12,17 @@ namespace dusk {
return;
}
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav;
ImGui::SetNextWindowBgAlpha(0.65f);
ImGui::SetNextWindowSizeConstraints(ImVec2(300, 0), ImVec2(FLT_MAX, FLT_MAX));
if (!ImGui::Begin("Map Loader", nullptr, windowFlags)) {
if (!ImGui::Begin("Map Loader", &m_showMapLoader, windowFlags)) {
ImGui::End();
return;
}
ImGui::SeparatorText("Map Loader");
ImGui::Checkbox("Show Internal Names", &m_mapLoaderInfo.showInternalNames);
const char* previewRegion = "None";
+1 -86
View File
@@ -73,91 +73,6 @@ namespace dusk {
}
}
// from https://github.com/ocornut/imgui/issues/1496#issuecomment-569892444
void ImGuiBeginGroupPanel(const char* name, const ImVec2& size) {
ImGui::BeginGroup();
auto cursorPos = ImGui::GetCursorScreenPos();
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
auto frameHeight = ImGui::GetFrameHeight();
ImGui::BeginGroup();
ImVec2 effectiveSize = size;
if (size.x < 0.0f)
effectiveSize.x = ImGui::GetContentRegionAvail().x;
else
effectiveSize.x = size.x;
ImGui::Dummy(ImVec2(effectiveSize.x, 0.0f));
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::SameLine(0.0f, 0.0f);
ImGui::BeginGroup();
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::SameLine(0.0f, 0.0f);
ImGui::TextUnformatted(name);
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(0.0, frameHeight + itemSpacing.y));
ImGui::BeginGroup();
ImGui::PopStyleVar(2);
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x -= frameHeight * 0.5f;
ImGui::GetCurrentWindow()->WorkRect.Max.x -= frameHeight * 0.5f;
ImGui::GetCurrentWindow()->Size.x -= frameHeight;
ImGui::PushItemWidth(effectiveSize.x - frameHeight);
}
// from https://github.com/ocornut/imgui/issues/1496#issuecomment-569892444
void ImGuiEndGroupPanel() {
ImGui::PopItemWidth();
auto itemSpacing = ImGui::GetStyle().ItemSpacing;
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
auto frameHeight = ImGui::GetFrameHeight();
// workaround for incorrect capture of columns/table width by placing
// zero-sized dummy element in the same group, this ensure
// max X cursor position is updated correctly
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(0.0f, 0.0f));
ImGui::EndGroup();
ImGui::EndGroup();
ImGui::SameLine(0.0f, 0.0f);
ImGui::Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
ImGui::Dummy(ImVec2(0.0, frameHeight - frameHeight * 0.5f - itemSpacing.y));
ImGui::EndGroup();
auto itemMin = ImGui::GetItemRectMin();
auto itemMax = ImGui::GetItemRectMax();
ImVec2 halfFrame = ImVec2((frameHeight * 0.25f) * 0.5f, frameHeight * 0.5f);
ImGui::GetWindowDrawList()->AddRect(
ImVec2(itemMin.x + halfFrame.x, itemMin.y + halfFrame.y),
ImVec2(itemMax.x - halfFrame.x, itemMax.y),
ImColor(ImGui::GetStyleColorVec4(ImGuiCol_Border)),
halfFrame.x);
ImGui::PopStyleVar(2);
ImGui::GetCurrentWindow()->ContentRegionRect.Max.x += frameHeight * 0.5f;
ImGui::GetCurrentWindow()->WorkRect.Max.x += frameHeight * 0.5f;
ImGui::GetCurrentWindow()->Size.x += frameHeight;
ImGui::Dummy(ImVec2(0.0f, 0.0f));
ImGui::EndGroup();
}
static void drawVirtualStick(const char* id, const ImVec2& stick) {
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 5, ImGui::GetCursorPos().y));
@@ -200,7 +115,7 @@ namespace dusk {
ImGui::SetNextWindowBgAlpha(0.65f);
ImGui::SetNextWindowSizeConstraints(ImVec2(850, 400), ImVec2(850, 400));
if (!ImGui::Begin("Controller Config", nullptr, windowFlags)) {
if (!ImGui::Begin("Controller Config", &m_showControllerConfig, windowFlags)) {
ImGui::End();
return;
}
+6 -1
View File
@@ -44,6 +44,7 @@ namespace dusk {
ImGui::MenuItem("Debug Camera", "F6", &m_showCameraOverlay);
ImGui::MenuItem("Map Loader", nullptr, &m_showMapLoader);
ImGui::MenuItem("Player Info", nullptr, &m_showPlayerInfo);
ImGui::MenuItem("Save Editor", nullptr, &m_showSaveEditor);
ImGui::EndMenu();
}
@@ -59,6 +60,10 @@ namespace dusk {
ShowMapLoader();
ShowPlayerInfo();
if (m_showSaveEditor) {
m_saveEditor.draw(m_showSaveEditor);
}
DuskDebugPad(); // temporary, remove later
}
@@ -136,7 +141,7 @@ namespace dusk {
ImGui::SetNextWindowBgAlpha(0.65f);
ImGui::SetNextWindowSizeConstraints(ImVec2(300, 200), ImVec2(300, 200));
if (ImGui::Begin("Player Info", nullptr, windowFlags)) {
if (ImGui::Begin("Player Info", &m_showPlayerInfo, windowFlags)) {
daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0);
daHorse_c* horse = dComIfGp_getHorseActor();
+4
View File
@@ -5,6 +5,7 @@
#include <string>
#include "imgui.h"
#include "ImGuiSaveEditor.hpp"
namespace dusk {
class ImGuiMenuTools {
@@ -63,6 +64,9 @@ namespace dusk {
bool m_showPlayerInfo = false;
CollisionViewSettings m_collisionViewSettings;
bool m_showSaveEditor = false;
ImGuiSaveEditor m_saveEditor;
};
}
+1 -1
View File
@@ -74,7 +74,7 @@ namespace dusk {
return;
}
if (ImGui::Begin("Processes")) {
if (ImGui::Begin("Processes", &m_showProcessManagement)) {
if (ImGui::BeginTabBar("Tabs")) {
showTreeRecursive = true;
if (ImGui::BeginTabItem("Tree")) {
+816
View File
@@ -0,0 +1,816 @@
#include "fmt/format.h"
#include "imgui.h"
#include "aurora/gfx.h"
#include "ImGuiConsole.hpp"
#include "ImGuiSaveEditor.hpp"
#include "d/d_com_inf_game.h"
#include "d/d_item_data.h"
#include <map>
namespace dusk {
std::map<int, std::string> itemMap = {
{dItemNo_HEART_e, "Heart"},
{dItemNo_GREEN_RUPEE_e, "Green Rupee"},
{dItemNo_BLUE_RUPEE_e, "Blue Rupee"},
{dItemNo_YELLOW_RUPEE_e, "Yellow Rupee"},
{dItemNo_RED_RUPEE_e, "Red Rupee"},
{dItemNo_PURPLE_RUPEE_e, "Purple Rupee"},
{dItemNo_ORANGE_RUPEE_e, "Orange Rupee"},
{dItemNo_SILVER_RUPEE_e, "Silver Rupee"},
{dItemNo_S_MAGIC_e, "Small Magic"},
{dItemNo_L_MAGIC_e, "Large Magic"},
{dItemNo_BOMB_5_e, "Bombs (5)"},
{dItemNo_BOMB_10_e, "Bombs (10)"},
{dItemNo_BOMB_20_e, "Bombs (20)"},
{dItemNo_BOMB_30_e, "Bombs (30)"},
{dItemNo_ARROW_10_e, "Arrows (10)"},
{dItemNo_ARROW_20_e, "Arrows (20)"},
{dItemNo_ARROW_30_e, "Arrows (30)"},
{dItemNo_ARROW_1_e, "Arrows (1)"},
{dItemNo_PACHINKO_SHOT_e, "Pumpkin Seeds"},
{dItemNo_NOENTRY_19_e, "Unknown"},
{dItemNo_NOENTRY_20_e, "Unknown"},
{dItemNo_NOENTRY_21_e, "Unknown"},
{dItemNo_WATER_BOMB_5_e, "Water Bombs (5)"},
{dItemNo_WATER_BOMB_10_e, "Water Bombs (10)"},
{dItemNo_WATER_BOMB_20_e, "Water Bombs (20)"},
{dItemNo_WATER_BOMB_30_e, "Water Bombs (30)"},
{dItemNo_BOMB_INSECT_5_e, "Bomblings (5)"},
{dItemNo_BOMB_INSECT_10_e, "Bomblings (10)"},
{dItemNo_BOMB_INSECT_20_e, "Bomblings (20)"},
{dItemNo_BOMB_INSECT_30_e, "Bomblings (30)"},
{dItemNo_RECOVERY_FAILY_e, "Fairy"},
{dItemNo_TRIPLE_HEART_e, "Triple Hearts"},
{dItemNo_SMALL_KEY_e, "Small Key"},
{dItemNo_KAKERA_HEART_e, "Piece of Heart"},
{dItemNo_UTAWA_HEART_e, "Heart Container"},
{dItemNo_MAP_e, "Map"},
{dItemNo_COMPUS_e, "Compass"},
{dItemNo_DUNGEON_EXIT_e, "Ooccoo Sr."},
{dItemNo_BOSS_KEY_e, "Boss Key"},
{dItemNo_DUNGEON_BACK_e, "Ooccoo Jr."},
{dItemNo_SWORD_e, "Ordon Sword"},
{dItemNo_MASTER_SWORD_e, "Master Sword"},
{dItemNo_WOOD_SHIELD_e, "Wooden Shield"},
{dItemNo_SHIELD_e, "Ordon Shield"},
{dItemNo_HYLIA_SHIELD_e, "Hylian Shield"},
{dItemNo_TKS_LETTER_e, "Ooccoo's Letter"},
{dItemNo_WEAR_CASUAL_e, "Ordon Clothes"},
{dItemNo_WEAR_KOKIRI_e, "Hero's Clothes"},
{dItemNo_ARMOR_e, "Magic Armor"},
{dItemNo_WEAR_ZORA_e, "Zora Armor"},
{dItemNo_MAGIC_LV1_e, "Magic Level 1"},
{dItemNo_DUNGEON_EXIT_2_e, "Ooccoo Sr."},
{dItemNo_WALLET_LV1_e, "Wallet"},
{dItemNo_WALLET_LV2_e, "Big Wallet"},
{dItemNo_WALLET_LV3_e, "Giant Wallet"},
{dItemNo_NOENTRY_55_e, "Unknown"},
{dItemNo_NOENTRY_56_e, "Unknown"},
{dItemNo_NOENTRY_57_e, "Unknown"},
{dItemNo_NOENTRY_58_e, "Unknown"},
{dItemNo_NOENTRY_59_e, "Unknown"},
{dItemNo_NOENTRY_60_e, "Unknown"},
{dItemNo_ZORAS_JEWEL_e, "Coral Earring"},
{dItemNo_HAWK_EYE_e, "Hawkeye"},
{dItemNo_WOOD_STICK_e, "Wooden Sword"},
{dItemNo_BOOMERANG_e, "Gale Boomerang"},
{dItemNo_SPINNER_e, "Spinner"},
{dItemNo_IRONBALL_e, "Ball and Chain"},
{dItemNo_BOW_e, "Hero's Bow"},
{dItemNo_HOOKSHOT_e, "Clawshot"},
{dItemNo_HVY_BOOTS_e, "Iron Boots"},
{dItemNo_COPY_ROD_e, "Dominion Rod"},
{dItemNo_W_HOOKSHOT_e, "Double Clawshots"},
{dItemNo_KANTERA_e, "Lantern"},
{dItemNo_LIGHT_SWORD_e, "Light Sword"},
{dItemNo_FISHING_ROD_1_e, "Fishing Rod"},
{dItemNo_PACHINKO_e, "Slingshot"},
{dItemNo_COPY_ROD_2_e, "Dominion Rod"},
{dItemNo_NOENTRY_77_e, "Unknown"},
{dItemNo_NOENTRY_78_e, "Unknown"},
{dItemNo_BOMB_BAG_LV2_e, "Giant Bomb Bag"},
{dItemNo_BOMB_BAG_LV1_e, "Bomb Bag"},
{dItemNo_BOMB_IN_BAG_e, "Bomb Bag"},
{dItemNo_NOENTRY_82_e, "Unknown"},
{dItemNo_LIGHT_ARROW_e, "Light Arrow"},
{dItemNo_ARROW_LV1_e, "Quiver"},
{dItemNo_ARROW_LV2_e, "Big Quiver"},
{dItemNo_ARROW_LV3_e, "Giant Quiver"},
{dItemNo_NOENTRY_87_e, "Unknown"},
{dItemNo_LURE_ROD_e, "Fishing Rod (Lure)"},
{dItemNo_BOMB_ARROW_e, "Bomb Arrow"},
{dItemNo_HAWK_ARROW_e, "Hawk Arrow"},
{dItemNo_BEE_ROD_e, "Fishing Rod (Bee Larva)"},
{dItemNo_JEWEL_ROD_e, "Fishing Rod (Earring)"},
{dItemNo_WORM_ROD_e, "Fishing Rod (Worm)"},
{dItemNo_JEWEL_BEE_ROD_e, "Fishing Rod (Earring + Bee Larva)"},
{dItemNo_JEWEL_WORM_ROD_e, "Fishing Rod (Earring + Worm)"},
{dItemNo_EMPTY_BOTTLE_e, "Empty Bottle"},
{dItemNo_RED_BOTTLE_e, "Red Potion"},
{dItemNo_GREEN_BOTTLE_e, "Green Potion"},
{dItemNo_BLUE_BOTTLE_e, "Blue Potion"},
{dItemNo_MILK_BOTTLE_e, "Milk Bottle"},
{dItemNo_HALF_MILK_BOTTLE_e, "Half Milk Bottle"},
{dItemNo_OIL_BOTTLE_e, "Lantern Oil"},
{dItemNo_WATER_BOTTLE_e, "Water Bottle"},
{dItemNo_OIL_BOTTLE_2_e, "Oil Bottle"},
{dItemNo_RED_BOTTLE_2_e, "Red Potion"},
{dItemNo_UGLY_SOUP_e, "Nasty Soup"},
{dItemNo_HOT_SPRING_e, "Hotspring Water"},
{dItemNo_FAIRY_e, "Fairy"},
{dItemNo_HOT_SPRING_2_e, "Hotspring Water"},
{dItemNo_OIL2_e, "Lantern Oil"},
{dItemNo_OIL_e, "Lantern Oil"},
{dItemNo_NORMAL_BOMB_e, "Bombs"},
{dItemNo_WATER_BOMB_e, "Water Bombs"},
{dItemNo_POKE_BOMB_e, "Bomblings"},
{dItemNo_FAIRY_DROP_e, "Great Fairy's Tears"},
{dItemNo_WORM_e, "Worm"},
{dItemNo_DROP_BOTTLE_e, ""},
{dItemNo_BEE_CHILD_e, ""},
{dItemNo_CHUCHU_RARE_e, "Rare Chu Jelly"},
{dItemNo_CHUCHU_RED_e, "Red Chu Jelly"},
{dItemNo_CHUCHU_BLUE_e, "Blue Chu Jelly"},
{dItemNo_CHUCHU_GREEN_e, "Green Chu Jelly"},
{dItemNo_CHUCHU_YELLOW_e, "Yellow Chu Jelly"},
{dItemNo_CHUCHU_PURPLE_e, "Purple Chu Jelly"},
{dItemNo_LV1_SOUP_e, "Simple Soup"},
{dItemNo_LV2_SOUP_e, "Good Soup"},
{dItemNo_LV3_SOUP_e, "Superb Soup"},
{dItemNo_LETTER_e, "Renado's Letter"},
{dItemNo_BILL_e, "Invoice"},
{dItemNo_WOOD_STATUE_e, "Wooden Statue"},
{dItemNo_IRIAS_PENDANT_e, "Ilia's Charm"},
{dItemNo_HORSE_FLUTE_e, "Horse Call"},
{dItemNo_NOENTRY_133_e, "Unknown"},
{dItemNo_NOENTRY_134_e, "Unknown"},
{dItemNo_NOENTRY_135_e, "Unknown"},
{dItemNo_NOENTRY_136_e, "Unknown"},
{dItemNo_NOENTRY_137_e, "Unknown"},
{dItemNo_NOENTRY_138_e, "Unknown"},
{dItemNo_NOENTRY_139_e, "Unknown"},
{dItemNo_NOENTRY_140_e, "Unknown"},
{dItemNo_NOENTRY_141_e, "Unknown"},
{dItemNo_NOENTRY_142_e, "Unknown"},
{dItemNo_NOENTRY_143_e, "Unknown"},
{dItemNo_RAFRELS_MEMO_e, "Auru's Memo"},
{dItemNo_ASHS_SCRIBBLING_e, "Ashei's Sketch"},
{dItemNo_NOENTRY_146_e, "Unknown"},
{dItemNo_NOENTRY_147_e, "Unknown"},
{dItemNo_NOENTRY_148_e, "Unknown"},
{dItemNo_NOENTRY_149_e, "Unknown"},
{dItemNo_NOENTRY_150_e, "Unknown"},
{dItemNo_NOENTRY_151_e, "Unknown"},
{dItemNo_NOENTRY_152_e, "Unknown"},
{dItemNo_NOENTRY_153_e, "Unknown"},
{dItemNo_NOENTRY_154_e, "Unknown"},
{dItemNo_NOENTRY_155_e, "Unknown"},
{dItemNo_CHUCHU_YELLOW2_e, "Yellow Chu Jelly"},
{dItemNo_OIL_BOTTLE3_e, "Lantern Oil"},
{dItemNo_SHOP_BEE_CHILD_e, ""},
{dItemNo_CHUCHU_BLACK_e, "Black Chu Jelly"},
{dItemNo_LIGHT_DROP_e, "Tear of Light"},
{dItemNo_DROP_CONTAINER_e, "Vessel of Light (Faron)"},
{dItemNo_DROP_CONTAINER02_e, "Vessel of Light (Eldin)"},
{dItemNo_DROP_CONTAINER03_e, "Vessel of Light (Lanayru)"},
{dItemNo_FILLED_CONTAINER_e, "Vessel of Light (Filled)"},
{dItemNo_MIRROR_PIECE_2_e, "Mirror Shard (Snowpeak Ruins)"},
{dItemNo_MIRROR_PIECE_3_e, "Mirror Shard (Temple of Time)"},
{dItemNo_MIRROR_PIECE_4_e, "Mirror Shard (City in the Sky)"},
{dItemNo_NOENTRY_168_e, "Unknown"},
{dItemNo_NOENTRY_169_e, "Unknown"},
{dItemNo_NOENTRY_170_e, "Unknown"},
{dItemNo_NOENTRY_171_e, "Unknown"},
{dItemNo_NOENTRY_172_e, "Unknown"},
{dItemNo_NOENTRY_173_e, "Unknown"},
{dItemNo_NOENTRY_174_e, "Unknown"},
{dItemNo_NOENTRY_175_e, "Unknown"},
{dItemNo_SMELL_YELIA_POUCH_e, "Scent of Ilia"},
{dItemNo_SMELL_PUMPKIN_e, "Pumpkin Scent"},
{dItemNo_SMELL_POH_e, "Poe Scent"},
{dItemNo_SMELL_FISH_e, "Reekfish Scent"},
{dItemNo_SMELL_CHILDREN_e, "Youth's Scent"},
{dItemNo_SMELL_MEDICINE_e, "Medicine Scent"},
{dItemNo_NOENTRY_182_e, "Unknown"},
{dItemNo_NOENTRY_183_e, "Unknown"},
{dItemNo_NOENTRY_184_e, "Unknown"},
{dItemNo_NOENTRY_185_e, "Unknown"},
{dItemNo_NOENTRY_186_e, "Unknown"},
{dItemNo_NOENTRY_187_e, "Unknown"},
{dItemNo_NOENTRY_188_e, "Unknown"},
{dItemNo_NOENTRY_189_e, "Unknown"},
{dItemNo_NOENTRY_190_e, "Unknown"},
{dItemNo_NOENTRY_191_e, "Unknown"},
{dItemNo_M_BEETLE_e, "Beetle ♂"},
{dItemNo_F_BEETLE_e, "Beetle ♀"},
{dItemNo_M_BUTTERFLY_e, "Butterfly ♂"},
{dItemNo_F_BUTTERFLY_e, "Butterfly ♀"},
{dItemNo_M_STAG_BEETLE_e, "Stag Beetle ♂"},
{dItemNo_F_STAG_BEETLE_e, "Stag Beetle ♀"},
{dItemNo_M_GRASSHOPPER_e, "Grasshopper ♂"},
{dItemNo_F_GRASSHOPPER_e, "Grasshopper ♀"},
{dItemNo_M_NANAFUSHI_e, "Phasmid ♂"},
{dItemNo_F_NANAFUSHI_e, "Phasmid ♀"},
{dItemNo_M_DANGOMUSHI_e, "Pill Bug ♂"},
{dItemNo_F_DANGOMUSHI_e, "Pill Bug ♀"},
{dItemNo_M_MANTIS_e, "Mantis ♂"},
{dItemNo_F_MANTIS_e, "Mantis ♀"},
{dItemNo_M_LADYBUG_e, "Ladybug ♂"},
{dItemNo_F_LADYBUG_e, "Ladybug ♀"},
{dItemNo_M_SNAIL_e, "Snail ♂"},
{dItemNo_F_SNAIL_e, "Snail ♀"},
{dItemNo_M_DRAGONFLY_e, "Dragonfly ♂"},
{dItemNo_F_DRAGONFLY_e, "Dragonfly ♀"},
{dItemNo_M_ANT_e, "Ant ♂"},
{dItemNo_F_ANT_e, "Ant ♀"},
{dItemNo_M_MAYFLY_e, "Mayfly ♂"},
{dItemNo_F_MAYFLY_e, "Mayfly ♀"},
{dItemNo_NOENTRY_216_e, "Unknown"},
{dItemNo_NOENTRY_217_e, "Unknown"},
{dItemNo_NOENTRY_218_e, "Unknown"},
{dItemNo_NOENTRY_219_e, "Unknown"},
{dItemNo_NOENTRY_220_e, "Unknown"},
{dItemNo_NOENTRY_221_e, "Unknown"},
{dItemNo_NOENTRY_222_e, "Unknown"},
{dItemNo_NOENTRY_223_e, "Unknown"},
{dItemNo_POU_SPIRIT_e, ""},
{dItemNo_NOENTRY_225_e, "Unknown"},
{dItemNo_NOENTRY_226_e, "Unknown"},
{dItemNo_NOENTRY_227_e, "Unknown"},
{dItemNo_NOENTRY_228_e, "Unknown"},
{dItemNo_NOENTRY_229_e, "Unknown"},
{dItemNo_NOENTRY_230_e, "Unknown"},
{dItemNo_NOENTRY_231_e, "Unknown"},
{dItemNo_NOENTRY_232_e, "Unknown"},
{dItemNo_ANCIENT_DOCUMENT_e, "Ancient Sky Book"},
{dItemNo_AIR_LETTER_e, "Sky Character"},
{dItemNo_ANCIENT_DOCUMENT2_e, "Ancient Sky Book (Filled)"},
{dItemNo_LV7_DUNGEON_EXIT_e, "Ooccoo Sr. (City in the Sky)"},
{dItemNo_LINKS_SAVINGS_e, "Link's Savings"},
{dItemNo_SMALL_KEY2_e, ""},
{dItemNo_POU_FIRE1_e, ""},
{dItemNo_POU_FIRE2_e, ""},
{dItemNo_POU_FIRE3_e, ""},
{dItemNo_POU_FIRE4_e, ""},
{dItemNo_BOSSRIDER_KEY_e, ""},
{dItemNo_TOMATO_PUREE_e, ""},
{dItemNo_TASTE_e, ""},
{dItemNo_LV5_BOSS_KEY_e, "Bedroom Key"},
{dItemNo_SURFBOARD_e, ""},
{dItemNo_KANTERA2_e, ""},
{dItemNo_L2_KEY_PIECES1_e, "Key Shard (1)"},
{dItemNo_L2_KEY_PIECES2_e, "Key Shard (2)"},
{dItemNo_L2_KEY_PIECES3_e, "Key Shard (3)"},
{dItemNo_KEY_OF_CARAVAN_e, "Bulblin Camp Key"},
{dItemNo_LV2_BOSS_KEY_e, "Key Shard (Completed)"},
{dItemNo_KEY_OF_FILONE_e, "Faron Gate Key"},
{dItemNo_NONE_e, "None"},
};
ImGuiSaveEditor::ImGuiSaveEditor() {}
void ImGuiSaveEditor::draw(bool& open) {
ImGuiIO& io = ImGui::GetIO();
ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoResize;
ImGui::SetNextWindowBgAlpha(0.65f);
ImGui::SetNextWindowSizeConstraints(ImVec2(600, 700), ImVec2(600, 700));
if (ImGui::Begin("Save Editor", &open, windowFlags)) {
if (ImGui::BeginTabBar("SaveEditorTabBar", ImGuiTabBarFlags_NoCloseWithMiddleMouseButton)) {
if (ImGui::BeginTabItem("Player Status")) {
drawPlayerStatusTab();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Location")) {
drawLocationTab();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Inventory")) {
//DrawInventoryTab();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Collection")) {
//DrawFlagsTab();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Flags")) {
drawFlagsTab();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Minigame")) {
//DrawFlagsTab();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Config")) {
drawConfigTab();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
ImGui::End();
}
void InputScalarBE(const char* label, ImGuiDataType dataType, void* pData) {
switch (dataType) {
case ImGuiDataType_U16: {
u16 temp = *(BE(u16)*)pData;
if (ImGui::InputScalar(label, dataType, &temp)) {
*(BE(u16)*)pData = temp;
}
break;
}
case ImGuiDataType_S16: {
s16 temp = *(BE(s16)*)pData;
if (ImGui::InputScalar(label, dataType, &temp)) {
*(BE(s16)*)pData = temp;
}
break;
}
case ImGuiDataType_U32: {
u32 temp = *(BE(u32)*)pData;
if (ImGui::InputScalar(label, dataType, &temp)) {
*(BE(u32)*)pData = temp;
}
break;
}
case ImGuiDataType_S32: {
s32 temp = *(BE(s32)*)pData;
if (ImGui::InputScalar(label, dataType, &temp)) {
*(BE(s32)*)pData = temp;
}
break;
}
case ImGuiDataType_U64: {
u64 temp = *(BE(u64)*)pData;
if (ImGui::InputScalar(label, dataType, &temp)) {
*(BE(u64)*)pData = temp;
}
break;
}
case ImGuiDataType_S64: {
s64 temp = *(BE(s64)*)pData;
if (ImGui::InputScalar(label, dataType, &temp)) {
*(BE(s64)*)pData = temp;
}
break;
}
case ImGuiDataType_Float: {
f32 temp = *(BE(f32)*)pData;
if (ImGui::InputScalar(label, dataType, &temp)) {
*(BE(f32)*)pData = temp;
}
break;
}
}
}
void genSelectItemComboBox(const char* label, u8& selectItemData) {
dSv_player_status_a_c& statusA = dComIfGs_getSaveData()->getPlayer().getPlayerStatusA();
dSv_player_item_c& item = dComIfGs_getSaveData()->getPlayer().getItem();
int currentSlotNo = selectItemData;
std::string defaultLabel =
currentSlotNo != 0xFF
? fmt::format("Slot {0} ({1})", currentSlotNo, itemMap.find(item.mItems[currentSlotNo])->second)
: "None";
// TODO: live update equips
if (ImGui::BeginCombo(label, defaultLabel.c_str())) {
if (ImGui::Selectable("None")) {
selectItemData = 0xFF;
}
for (int i = 0; i < 24; i++) {
u8 itemNo = item.mItems[i];
if (ImGui::Selectable(fmt::format("Slot {0} ({1})", i, itemMap.find(itemNo)->second).c_str())) {
selectItemData = i;
}
}
ImGui::EndCombo();
}
}
void ImGuiSaveEditor::drawPlayerStatusTab() {
const char* playerName = dComIfGs_getPlayerName();
ImGui::Text("Player Name: ");
ImGui::SameLine();
char nameBuffer[8];
snprintf(nameBuffer, sizeof(nameBuffer), "%s", playerName);
if (ImGui::InputText("##PlayerNameInput", nameBuffer, 8)) {
strcpy(dComIfGs_getPlayerName(), nameBuffer);
}
const char* horseName = dComIfGs_getHorseName();
ImGui::Text("Horse Name: ");
ImGui::SameLine();
char horseNameBuffer[8];
snprintf(horseNameBuffer, sizeof(horseNameBuffer), "%s", horseName);
if (ImGui::InputText("##HorseNameInput", horseNameBuffer, 8)) {
strcpy(dComIfGs_getHorseName(), horseNameBuffer);
}
ImGui::Separator();
dSv_player_status_a_c& statusA = dComIfGs_getSaveData()->getPlayer().getPlayerStatusA();
dSv_player_status_b_c& statusB = dComIfGs_getSaveData()->getPlayer().getPlayerStatusB();
InputScalarBE("Max Health", ImGuiDataType_U16, &statusA.mMaxLife);
InputScalarBE("Health", ImGuiDataType_U16, &statusA.mLife);
InputScalarBE("Rupees", ImGuiDataType_U16, &statusA.mRupee);
InputScalarBE("Max Oil", ImGuiDataType_U16, &statusA.mMaxOil);
InputScalarBE("Oil", ImGuiDataType_U16, &statusA.mOil);
genSelectItemComboBox("Equip X", statusA.mSelectItem[0]);
genSelectItemComboBox("Equip Y", statusA.mSelectItem[1]);
genSelectItemComboBox("Combo Equip X", statusA.mMixItem[0]);
genSelectItemComboBox("Combo Equip Y", statusA.mMixItem[1]);
if (ImGui::BeginCombo("Clothes", itemMap.find(statusA.mSelectEquip[0])->second.c_str())) {
if (ImGui::Selectable("None")) {
statusA.mSelectEquip[0] = dItemNo_NONE_e;
}
if (ImGui::Selectable("Ordon Clothes")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_CASUAL_e;
}
if (ImGui::Selectable("Hero's Clothes")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_KOKIRI_e;
}
if (ImGui::Selectable("Zora Armor")) {
statusA.mSelectEquip[0] = dItemNo_WEAR_ZORA_e;
}
if (ImGui::Selectable("Magic Armor")) {
statusA.mSelectEquip[0] = dItemNo_ARMOR_e;
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Sword", itemMap.find(statusA.mSelectEquip[1])->second.c_str())) {
if (ImGui::Selectable("None")) {
statusA.mSelectEquip[1] = dItemNo_NONE_e;
}
if (ImGui::Selectable("Wooden Sword")) {
statusA.mSelectEquip[1] = dItemNo_WOOD_STICK_e;
}
if (ImGui::Selectable("Ordon Sword")) {
statusA.mSelectEquip[1] = dItemNo_SWORD_e;
}
if (ImGui::Selectable("Master Sword")) {
statusA.mSelectEquip[1] = dItemNo_MASTER_SWORD_e;
}
if (ImGui::Selectable("Light Sword")) {
statusA.mSelectEquip[1] = dItemNo_LIGHT_SWORD_e;
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Shield", itemMap.find(statusA.mSelectEquip[2])->second.c_str())) {
if (ImGui::Selectable("None")) {
statusA.mSelectEquip[2] = dItemNo_NONE_e;
}
if (ImGui::Selectable("Wooden Shield")) {
statusA.mSelectEquip[2] = dItemNo_SHIELD_e;
}
if (ImGui::Selectable("Ordon Shield")) {
statusA.mSelectEquip[2] = dItemNo_WOOD_SHIELD_e;
}
if (ImGui::Selectable("Hylian Shield")) {
statusA.mSelectEquip[2] = dItemNo_HYLIA_SHIELD_e;
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Scent", itemMap.find(statusA.mSelectEquip[3])->second.c_str())) {
if (ImGui::Selectable("None")) {
statusA.mSelectEquip[3] = dItemNo_NONE_e;
}
if (ImGui::Selectable("Youth's Scent")) {
statusA.mSelectEquip[3] = dItemNo_SMELL_CHILDREN_e;
}
if (ImGui::Selectable("Scent of Ilia")) {
statusA.mSelectEquip[3] = dItemNo_SMELL_YELIA_POUCH_e;
}
if (ImGui::Selectable("Poe Scent")) {
statusA.mSelectEquip[3] = dItemNo_SMELL_POH_e;
}
if (ImGui::Selectable("Reekfish Scent")) {
statusA.mSelectEquip[3] = dItemNo_SMELL_FISH_e;
}
if (ImGui::Selectable("Medicine Scent")) {
statusA.mSelectEquip[3] = dItemNo_SMELL_MEDICINE_e;
}
ImGui::EndCombo();
}
const char* walletSizeNames[] = {
"Normal",
"Big",
"Giant",
};
int walletSize = statusA.getWalletSize();
if (ImGui::BeginCombo("Wallet Size", walletSizeNames[walletSize])) {
if (ImGui::Selectable(walletSizeNames[WALLET])) {
statusA.setWalletSize(WALLET);
}
if (ImGui::Selectable(walletSizeNames[BIG_WALLET])) {
statusA.setWalletSize(BIG_WALLET);
}
if (ImGui::Selectable(walletSizeNames[GIANT_WALLET])) {
statusA.setWalletSize(GIANT_WALLET);
}
ImGui::EndCombo();
}
if (ImGui::BeginCombo("Form", statusA.mTransformStatus == 0 ? "Human" : "Wolf")) {
if (ImGui::Selectable("Human")) {
statusA.mTransformStatus = TF_STATUS_HUMAN;
}
if (ImGui::Selectable("Wolf")) {
statusA.mTransformStatus = TF_STATUS_WOLF;
}
ImGui::EndCombo();
}
ImGui::Separator();
s32 hours = dKy_getdaytime_hour();
s32 min = dKy_getdaytime_minute();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 2);
if (ImGui::InputScalar("##TimeHours", ImGuiDataType_S32, &hours)) {
hours = std::clamp(hours, 0, 23);
statusB.setTime((hours * 15.0f) + (min / 60.0f * 15.0f));
}
ImGui::SameLine();
ImGui::Text(":");
ImGui::SameLine();
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 2);
if (ImGui::InputScalar("Time##TimeMinutes", ImGuiDataType_S32, &min)) {
min = std::clamp(min, 0, 59);
statusB.setTime((hours * 15.0f) + (min / 60.0f * 15.0f));
}
InputScalarBE("Date", ImGuiDataType_U16, &statusB.mDate);
int transformLevel = 0;
for (int i = 0; i < 4; i++) {
if (statusB.mTransformLevelFlag & (1 << i)) {
transformLevel++;
}
}
if (ImGui::SliderInt("Transform Level", &transformLevel, 0, 3)) {
u8 newFlags = 0;
for (int i = 0; i < transformLevel; i++) {
newFlags |= (1 << i);
}
statusB.mTransformLevelFlag = newFlags;
}
int darkClearLevel = 0;
for (int i = 0; i < 4; i++) {
if (statusB.mDarkClearLevelFlag & (1 << i)) {
darkClearLevel++;
}
}
if (ImGui::SliderInt("Twilight Clear Level", &darkClearLevel, 0, 3)) {
u8 newFlags = 0;
for (int i = 0; i < darkClearLevel; i++) {
newFlags |= (1 << i);
}
statusB.mDarkClearLevelFlag = newFlags;
}
}
void ImGuiSaveEditor::drawLocationTab() {
dSv_player_return_place_c& returnPlace = dComIfGs_getSaveData()->getPlayer().getPlayerReturnPlace();
dSv_horse_place_c& horsePlace = dComIfGs_getSaveData()->getPlayer().getHorsePlace();
ImGui::Text("Save Location");
ImGui::Text("Stage: ");
ImGui::SameLine();
char nameBuffer[8];
snprintf(nameBuffer, sizeof(nameBuffer), "%s", returnPlace.mName);
if (ImGui::InputText("##SaveStageNameInput", nameBuffer, 8)) {
strcpy(returnPlace.mName, nameBuffer);
}
ImGui::Text("Room: ");
ImGui::SameLine();
int tempRoom = returnPlace.mRoomNo;
if (ImGui::InputInt("##SaveRoomInput", &tempRoom)) {
returnPlace.mRoomNo = tempRoom;
}
ImGui::Text("Spawn ID: ");
ImGui::SameLine();
int tempSpawn = returnPlace.mPlayerStatus;
if (ImGui::InputInt("##SaveSpawnInput", &tempSpawn)) {
returnPlace.mPlayerStatus = tempSpawn;
}
ImGui::Separator();
ImGui::Text("Horse Location");
ImGui::Text("Position: ");
ImGui::SameLine();
Vec tempPos = horsePlace.mPos;
if (ImGui::InputFloat3("##HorsePosition", &tempPos.x)) {
horsePlace.mPos.x = tempPos.x;
horsePlace.mPos.y = tempPos.y;
horsePlace.mPos.z = tempPos.z;
}
ImGui::Text("Angle: ");
ImGui::SameLine();
int tempAngle = horsePlace.mAngleY;
if (ImGui::InputInt("##HorsePosition", &tempAngle)) {
horsePlace.mAngleY = tempAngle;
}
ImGui::Text("Stage: ");
ImGui::SameLine();
char horseStageBuffer[8];
snprintf(horseStageBuffer, sizeof(horseStageBuffer), "%s", horsePlace.mName);
if (ImGui::InputText("##HorseStageNameInput", horseStageBuffer, 8)) {
strcpy(horsePlace.mName, horseStageBuffer);
}
ImGui::Text("Room: ");
ImGui::SameLine();
int tempHorseRoom = horsePlace.mRoomNo;
if (ImGui::InputInt("##HorseRoomInput", &tempHorseRoom)) {
horsePlace.mRoomNo = tempHorseRoom;
}
ImGui::Text("Spawn ID: ");
ImGui::SameLine();
int tempHorseSpawn = horsePlace.mSpawnId;
if (ImGui::InputInt("##HorseSpawnInput", &tempHorseSpawn)) {
horsePlace.mSpawnId = tempHorseSpawn;
}
}
void ImGuiSaveEditor::drawInventoryTab() {
}
void drawFlagList(const char* id, BE(u32)& flags) {
u32 tempFlagField = flags;
for (int i = 31; i >= 0; i--) {
if ((31 - i) % 8) {
ImGui::SameLine();
}
bool flag = tempFlagField & (1 << i);
if (ImGui::Checkbox(fmt::format("{0}{1}", id, i).c_str(), &flag)) {
if (flag)
tempFlagField |= (1 << i);
else
tempFlagField &= ~(1 << i);
flags = tempFlagField;
}
}
}
void genMembitFlags(const char* id, dSv_memBit_c& membit) {
ImGuiBeginGroupPanel("Chest", { 100, 100 });
for (int j = 0; j < 2; j++) {
drawFlagList(fmt::format("##_tbox{}", j).c_str(), membit.mTbox[j]);
}
ImGuiEndGroupPanel();
ImVec2 cursor = ImGui::GetCursorPos();
ImGui::SameLine();
ImGuiBeginGroupPanel("Switch", { 100, 100 });
for (int j = 0; j < 4; j++) {
drawFlagList(fmt::format("##_switch{}", j).c_str(), membit.mSwitch[j]);
}
ImGuiEndGroupPanel();
ImGui::SetCursorPos(cursor);
ImGuiBeginGroupPanel("Item", { 100, 100 });
for (int j = 0; j < 1; j++) {
drawFlagList(fmt::format("##_item{}", j).c_str(), membit.mItem[j]);
}
ImGuiEndGroupPanel();
}
void ImGuiSaveEditor::drawFlagsTab() {
if (ImGui::TreeNode("Current Region Flags")) {
dSv_memBit_c& membit = g_dComIfG_gameInfo.info.mMemory.mBit;
genMembitFlags("##TempSceneFlags", membit);
int stageNo = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo());
if (ImGui::Button("Save##SaveTempFlags")) {
dComIfGs_putSave(stageNo);
}
ImGui::SameLine();
if (ImGui::Button("Load##LoadSaveFlags")) {
dComIfGs_getSave(stageNo);
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Region Saved Flags")) {
static std::array<const char*, 27> regionNames = {
"Ordon",
"Hyrule Sewers",
"Faron",
"Eldin",
"Lanayru",
"Unknown",
"Hyrule Field",
"Sacred Grove",
"Snowpeak",
"Castle Town",
"Gerudo Desert",
"Fishing Pond",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Forest Temple",
"Goron Mines",
"Lakebed Temple",
"Arbiter's Grounds",
"Snowpeak Ruins",
"Temple of Time",
"City in the Sky",
"Palace of Twilight",
"Hyrule Castle",
"Caves",
"Grottos",
};
if (ImGui::BeginCombo("Region", regionNames[m_selectedRegion])) {
for (int i = 0; i < regionNames.size(); i++) {
if (strcmp(regionNames[i], "Reserved") == 0) continue;
if (ImGui::Selectable(regionNames[i])) {
m_selectedRegion = i;
}
}
ImGui::EndCombo();
}
dSv_memBit_c* membit = &dComIfGs_getSaveData()->mSave[m_selectedRegion].mBit;
if (membit != nullptr) {
genMembitFlags("##SaveSceneFlags", *membit);
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Event Flags")) {
dSv_event_c& event = dComIfGs_getSaveData()->mEvent;
for (int e = 0; e < 255; e++) {
ImGui::Text("%03d ", e);
ImGui::SameLine();
for (int i = 8; i >= 0; i--) {
bool flag = event.mEvent[e] & (1 << i);
if (ImGui::Checkbox(fmt::format("##event{0}{1}", e, i).c_str(), &flag)) {
if (flag)
event.mEvent[e] |= (1 << i);
else
event.mEvent[e] &= ~(1 << i);
}
ImGui::SameLine();
}
ImGui::NewLine();
}
ImGui::TreePop();
}
}
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")) {
ImGui::EndCombo();
}
}
}
+26
View File
@@ -0,0 +1,26 @@
#ifndef DUSK_IMGUI_SAVEEDITOR_HPP
#define DUSK_IMGUI_SAVEEDITOR_HPP
#include <aurora/aurora.h>
#include <string>
#include "imgui.h"
namespace dusk {
class ImGuiSaveEditor {
public:
ImGuiSaveEditor();
void draw(bool& open);
void drawPlayerStatusTab();
void drawLocationTab();
void drawInventoryTab();
void drawFlagsTab();
void drawConfigTab();
private:
int m_selectedRegion = 0;
};
}
#endif // DUSK_IMGUI_SAVEEDITOR_HPP
+4 -39
View File
@@ -5,6 +5,7 @@
#include <string.h>
#include <cstdlib>
#include <cstdint>
#include <memory>
#include <mutex>
#include <condition_variable>
#include <unordered_map>
@@ -55,42 +56,6 @@ u32 OSGetSoundMode() {
// Message Queue (thread-safe implementation)
// ==========================================================================
// Malloc-based allocator to bypass JKRHeap operator new/delete
template<typename T>
struct MallocAllocator {
using value_type = T;
MallocAllocator() = default;
template<typename U> MallocAllocator(const MallocAllocator<U>&) noexcept {}
T* allocate(std::size_t n) {
void* p = std::malloc(n * sizeof(T));
if (!p) throw std::bad_alloc();
return static_cast<T*>(p);
}
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
template<typename U> bool operator==(const MallocAllocator<U>&) const noexcept { return true; }
template<typename U> bool operator!=(const MallocAllocator<U>&) const noexcept { return false; }
};
template<typename T>
struct MallocDeleter {
void operator()(T* p) const {
p->~T();
std::free(p);
}
};
template<typename T, typename... Args>
std::unique_ptr<T, MallocDeleter<T>> make_malloc_unique(Args&&... args) {
void* mem = std::malloc(sizeof(T));
if (!mem) throw std::bad_alloc();
T* obj = new (mem) T(std::forward<Args>(args)...);
return std::unique_ptr<T, MallocDeleter<T>>(obj);
}
template<typename K, typename V>
using MallocMap = std::unordered_map<K, V, std::hash<K>, std::equal_to<K>,
MallocAllocator<std::pair<const K, V>>>;
// Side-table for native synchronization per OSMessageQueue
struct PCMessageQueueData {
std::mutex mtx;
@@ -103,8 +68,8 @@ static std::mutex& GetMsgQueueMapMutex() {
static std::mutex mtx;
return mtx;
}
static MallocMap<OSMessageQueue*, std::unique_ptr<PCMessageQueueData, MallocDeleter<PCMessageQueueData>>>& GetMsgQueueMap() {
static MallocMap<OSMessageQueue*, std::unique_ptr<PCMessageQueueData, MallocDeleter<PCMessageQueueData>>> map;
static std::unordered_map<OSMessageQueue*, std::unique_ptr<PCMessageQueueData>>& GetMsgQueueMap() {
static std::unordered_map<OSMessageQueue*, std::unique_ptr<PCMessageQueueData>> map;
return map;
}
@@ -113,7 +78,7 @@ static PCMessageQueueData& GetMsgQueueData(OSMessageQueue* mq) {
auto& map = GetMsgQueueMap();
auto it = map.find(mq);
if (it == map.end()) {
auto result = map.emplace(mq, make_malloc_unique<PCMessageQueueData>());
auto result = map.emplace(mq, std::make_unique<PCMessageQueueData>());
return *result.first->second;
}
return *it->second;
+9 -1
View File
@@ -442,6 +442,14 @@ void darwFilter(GXColor matColor) {
GXLoadPosMtxImm(cMtx_getIdentity(), GX_PNMTX0);
GXSetChanMatColor(GX_COLOR0A0, matColor);
GXSetCurrentMtx(0);
#if TARGET_PC
f32 width = mDoGph_gInf_c::getWidth();
f32 height = mDoGph_gInf_c::getHeight();
GXSetViewport(0.0f, 0.0f, width, height, 0.0f, 1.0f);
GXSetScissor(0, 0, width, height);
#endif
GXClearVtxDesc();
GXSetVtxDesc(GX_VA_POS, GX_DIRECT);
GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_CLR_RGBA, GX_RGB8, 0);
@@ -478,7 +486,7 @@ void mDoGph_gInf_c::calcFade() {
}
if (mFadeColor.a != 0) {
//darwFilter(mFadeColor);
darwFilter(mFadeColor);
}
}
+1
View File
@@ -272,6 +272,7 @@ int game_main(int argc, char* argv[]) {
config.logLevel = (AuroraLogLevel)parsed_arg_options["log-level"].as<uint8_t>();
config.mem1Size = 256 * 1024 * 1024;
config.mem2Size = 24 * 1024 * 1024;
config.allowJoystickBackgroundEvents = true;
auroraInfo = aurora_initialize(argc, argv, &config);