Skip to content
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ add_subdirectory("ps2xRuntime")

add_subdirectory("ps2xAnalyzer")
add_subdirectory("ps2xTest")
add_subdirectory("ps2xStudio")
2 changes: 2 additions & 0 deletions ps2xAnalyzer/include/ps2recomp/elf_analyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ namespace ps2recomp

bool analyze();
bool generateToml(const std::string &outputPath);
bool importGhidraMap(const std::string &csvPath);
const std::vector<Function>& getFunctions() const;
bool isLibrarySymbolNameForHeuristics(const std::string &name) const;
static bool isReliableSymbolNameForHeuristics(const std::string &name);
static bool isSystemSymbolNameForHeuristics(const std::string &name);
Expand Down
84 changes: 84 additions & 0 deletions ps2xAnalyzer/src/elf_analyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2492,4 +2492,88 @@ namespace ps2recomp
return false;
}
}

bool ElfAnalyzer::importGhidraMap(const std::string &csvPath)
{
std::ifstream file(csvPath);
if (!file)
{
std::cerr << "Failed to open Ghidra CSV file: " << csvPath << std::endl;
return false;
}

std::string line;
int lineNum = 0;
int importedCount = 0;

while (std::getline(file, line))
{
lineNum++;

// Skip header line
if (lineNum == 1 && line.find("Name") != std::string::npos)
{
continue;
}

// Parse CSV line: Name,Start,End,Size
std::stringstream ss(line);
std::string name, startStr, endStr, sizeStr;

if (!std::getline(ss, name, ',') ||
!std::getline(ss, startStr, ',') ||
!std::getline(ss, endStr, ','))
{
continue; // Skip malformed lines
}

// Parse hex addresses (e.g., "0x00123456")
uint32_t startAddr = 0;
uint32_t endAddr = 0;

try
{
startAddr = std::stoul(startStr, nullptr, 16);
endAddr = std::stoul(endStr, nullptr, 16);
}
catch (...)
{
continue; // Skip lines with invalid addresses
}

// Update existing function or create new one
bool found = false;
for (auto &func : m_functions)
{
if (func.start == startAddr)
{
// Update with Ghidra's more accurate boundaries
func.name = name;
func.end = endAddr;
found = true;
importedCount++;
break;
}
}

// If not found, create new function from Ghidra data
if (!found)
{
Function newFunc;
newFunc.name = name;
newFunc.start = startAddr;
newFunc.end = endAddr;
m_functions.push_back(newFunc);
importedCount++;
}
}

std::cout << "Imported " << importedCount << " functions from Ghidra CSV: " << csvPath << std::endl;
return true;
}

const std::vector<Function>& ElfAnalyzer::getFunctions() const
{
return m_functions;
}
}
130 changes: 130 additions & 0 deletions ps2xStudio/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Blackline Interactive

cmake_minimum_required(VERSION 3.21)
project(ps2xStudio)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

FetchContent_Declare(
imgui
GIT_REPOSITORY https://github.com/ocornut/imgui.git
GIT_TAG docking
GIT_SHALLOW TRUE
)

FetchContent_Declare(
imgui_colortextedit
GIT_REPOSITORY https://github.com/BalazsJako/ImGuiColorTextEdit.git
GIT_TAG master
GIT_SHALLOW TRUE
)

FetchContent_Declare(
imgui_club
GIT_REPOSITORY https://github.com/ocornut/imgui_club.git
GIT_TAG main
GIT_SHALLOW TRUE
)

FetchContent_Declare(
imgui_file_dialog
GIT_REPOSITORY https://github.com/aiekick/ImGuiFileDialog.git
GIT_TAG master
GIT_SHALLOW TRUE
)

FetchContent_Declare(
SDL2
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
GIT_TAG release-2.30.9
GIT_SHALLOW TRUE
)

set(SDL_SHARED ON CACHE BOOL "" FORCE)
set(SDL_STATIC OFF CACHE BOOL "" FORCE)

FetchContent_MakeAvailable(imgui imgui_colortextedit imgui_club SDL2)

FetchContent_GetProperties(imgui_file_dialog)
if(NOT imgui_file_dialog_POPULATED)
FetchContent_Populate(imgui_file_dialog)
endif()

find_package(OpenGL REQUIRED)

set(IMGUI_SOURCES
${imgui_SOURCE_DIR}/imgui.cpp
${imgui_SOURCE_DIR}/imgui_draw.cpp
${imgui_SOURCE_DIR}/imgui_tables.cpp
${imgui_SOURCE_DIR}/imgui_widgets.cpp
${imgui_SOURCE_DIR}/imgui_demo.cpp
${imgui_SOURCE_DIR}/backends/imgui_impl_sdl2.cpp
${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp
)

add_library(imgui_lib STATIC ${IMGUI_SOURCES})

target_include_directories(imgui_lib PUBLIC
${imgui_SOURCE_DIR}
${imgui_SOURCE_DIR}/backends
)

target_link_libraries(imgui_lib PUBLIC SDL2::SDL2 OpenGL::GL)

add_library(imgui_colortextedit_lib STATIC
${imgui_colortextedit_SOURCE_DIR}/TextEditor.cpp
)

target_include_directories(imgui_colortextedit_lib PUBLIC ${imgui_colortextedit_SOURCE_DIR})

target_link_libraries(imgui_colortextedit_lib PUBLIC imgui_lib)

add_library(imgui_memory_editor_lib INTERFACE)

target_include_directories(imgui_memory_editor_lib INTERFACE
${imgui_club_SOURCE_DIR}/imgui_memory_editor
)

target_link_libraries(imgui_memory_editor_lib INTERFACE imgui_lib)

add_library(imgui_filedialog_lib STATIC
${imgui_file_dialog_SOURCE_DIR}/ImGuiFileDialog.cpp
)

target_include_directories(imgui_filedialog_lib PUBLIC
${imgui_file_dialog_SOURCE_DIR}
${imgui_SOURCE_DIR}
)

target_link_libraries(imgui_filedialog_lib PUBLIC imgui_lib)

file(GLOB_RECURSE STUDIO_SOURCES "src/*.cpp")
file(GLOB_RECURSE STUDIO_HEADERS "include/*.hpp")

add_executable(ps2xStudio ${STUDIO_SOURCES} ${STUDIO_HEADERS})

target_include_directories(ps2xStudio PRIVATE
include
${CMAKE_CURRENT_SOURCE_DIR}/../ps2xAnalyzer/include
${CMAKE_CURRENT_SOURCE_DIR}/../ps2xRecomp/include
${CMAKE_CURRENT_SOURCE_DIR}/../ps2xTest/include
)

target_link_libraries(ps2xStudio PRIVATE
imgui_lib
imgui_colortextedit_lib
imgui_memory_editor_lib
imgui_filedialog_lib
ps2_recomp_lib
ps2_analyzer_lib
ps2_test_lib
SDL2::SDL2
OpenGL::GL
)

if(WIN32)
target_link_libraries(ps2xStudio PRIVATE SDL2::SDL2main)
endif()
Binary file added ps2xStudio/external/Font/Font_1.ttf
Binary file not shown.
Binary file added ps2xStudio/external/Font/Font_2.ttf
Binary file not shown.
16 changes: 16 additions & 0 deletions ps2xStudio/include/GUI.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once
#include "StudioState.hpp"

namespace GUI {
void DrawStudio(StudioState& state);
void ApplySettings(StudioState& state);

// Called from main loop BEFORE NewFrame when fonts need rebuilding
void RebuildFontsIfNeeded(StudioState& state);

// Notify GUI that config editor should resync
void SyncConfigEditor(StudioState& state);

// Returns true when user requested exit (File > Exit)
bool WantsQuit();
}
Loading