System: PSF (Portable Sound Format) loading support
This commit is contained in:
parent
38847de8ee
commit
205297ac27
|
@ -54,6 +54,8 @@ add_library(core
|
|||
memory_card.h
|
||||
pad.cpp
|
||||
pad.h
|
||||
psf_loader.cpp
|
||||
psf_loader.h
|
||||
save_state_version.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
|
@ -83,7 +85,7 @@ set(RECOMPILER_SRCS
|
|||
|
||||
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_link_libraries(core PUBLIC Threads::Threads common imgui tinyxml2)
|
||||
target_link_libraries(core PUBLIC Threads::Threads common imgui tinyxml2 zlib)
|
||||
target_link_libraries(core PRIVATE glad stb)
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
<ClCompile Include="memory_card.cpp" />
|
||||
<ClCompile Include="pad.cpp" />
|
||||
<ClCompile Include="controller.cpp" />
|
||||
<ClCompile Include="psf_loader.cpp" />
|
||||
<ClCompile Include="settings.cpp" />
|
||||
<ClCompile Include="sio.cpp" />
|
||||
<ClCompile Include="spu.cpp" />
|
||||
|
@ -116,6 +117,7 @@
|
|||
<ClInclude Include="memory_card.h" />
|
||||
<ClInclude Include="pad.h" />
|
||||
<ClInclude Include="controller.h" />
|
||||
<ClInclude Include="psf_loader.h" />
|
||||
<ClInclude Include="save_state_version.h" />
|
||||
<ClInclude Include="settings.h" />
|
||||
<ClInclude Include="sio.h" />
|
||||
|
@ -288,7 +290,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -313,7 +315,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -338,7 +340,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -366,7 +368,7 @@
|
|||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -393,7 +395,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -419,7 +421,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -446,7 +448,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -472,7 +474,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\tinyxml2\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
<ClCompile Include="host_display.cpp" />
|
||||
<ClCompile Include="timing_event.cpp" />
|
||||
<ClCompile Include="cdrom_async_reader.cpp" />
|
||||
<ClCompile Include="psf_loader.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -81,6 +82,7 @@
|
|||
<ClInclude Include="analog_controller.h" />
|
||||
<ClInclude Include="timing_event.h" />
|
||||
<ClInclude Include="cdrom_async_reader.h" />
|
||||
<ClInclude Include="psf_loader.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpu_core.inl" />
|
||||
|
|
|
@ -201,6 +201,12 @@ bool GameList::IsExeFileName(const char* path)
|
|||
(StringUtil::Strcasecmp(extension, ".exe") == 0 || StringUtil::Strcasecmp(extension, ".psexe") == 0));
|
||||
}
|
||||
|
||||
bool GameList::IsPsfFileName(const char* path)
|
||||
{
|
||||
const char* extension = std::strrchr(path, '.');
|
||||
return (extension && StringUtil::Strcasecmp(extension, ".psf") == 0);
|
||||
}
|
||||
|
||||
static std::string_view GetFileNameFromPath(const char* path)
|
||||
{
|
||||
const char* filename_end = path + std::strlen(path);
|
||||
|
|
|
@ -50,6 +50,9 @@ public:
|
|||
/// Returns true if the filename is a PlayStation executable we can inject.
|
||||
static bool IsExeFileName(const char* path);
|
||||
|
||||
/// Returns true if the filename is a Portable Sound Format file we can uncompress/load.
|
||||
static bool IsPsfFileName(const char* path);
|
||||
|
||||
static std::string GetGameCodeForImage(CDImage* cdi);
|
||||
static std::string GetGameCodeForPath(const char* image_path);
|
||||
static DiscRegion GetRegionForCode(std::string_view code);
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
#include "psf_loader.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "zlib.h"
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
Log_SetChannel(PSFLoader);
|
||||
|
||||
namespace PSFLoader {
|
||||
|
||||
std::string File::GetTagString(const char* tag_name, const char* default_value) const
|
||||
{
|
||||
auto it = m_tags.find(tag_name);
|
||||
if (it == m_tags.end())
|
||||
return default_value;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
int File::GetTagInt(const char* tag_name, int default_value) const
|
||||
{
|
||||
auto it = m_tags.find(tag_name);
|
||||
if (it == m_tags.end())
|
||||
return default_value;
|
||||
|
||||
return std::atoi(it->second.c_str());
|
||||
}
|
||||
|
||||
float File::GetTagFloat(const char* tag_name, float default_value) const
|
||||
{
|
||||
auto it = m_tags.find(tag_name);
|
||||
if (it == m_tags.end())
|
||||
return default_value;
|
||||
|
||||
return static_cast<float>(std::atof(it->second.c_str()));
|
||||
}
|
||||
|
||||
bool File::Load(const char* path)
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(path, "rb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
// we could mmap this instead
|
||||
std::fseek(fp.get(), 0, SEEK_END);
|
||||
const u32 file_size = static_cast<u32>(std::ftell(fp.get()));
|
||||
std::fseek(fp.get(), 0, SEEK_SET);
|
||||
|
||||
std::vector<u8> file_data(file_size);
|
||||
if (std::fread(file_data.data(), 1, file_size, fp.get()) != file_size)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to read data from PSF '%s'", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
const u8* file_pointer = file_data.data();
|
||||
const u8* file_pointer_end = file_data.data() + file_data.size();
|
||||
|
||||
PSFHeader header;
|
||||
std::memcpy(&header, file_pointer, sizeof(header));
|
||||
file_pointer += sizeof(header);
|
||||
if (header.id[0] != 'P' || header.id[1] != 'S' || header.id[2] != 'F' || header.version != 0x01 ||
|
||||
header.compressed_program_size == 0 ||
|
||||
(sizeof(header) + header.reserved_area_size + header.compressed_program_size) > file_size)
|
||||
{
|
||||
Log_ErrorPrintf("Invalid or incompatible header in PSF '%s'", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
file_pointer += header.reserved_area_size;
|
||||
|
||||
m_program_data.resize(MAX_PROGRAM_SIZE);
|
||||
|
||||
z_stream strm = {};
|
||||
strm.avail_in = static_cast<uInt>(file_pointer_end - file_pointer);
|
||||
strm.next_in = static_cast<Bytef*>(const_cast<u8*>(file_pointer));
|
||||
strm.avail_out = static_cast<uInt>(m_program_data.size());
|
||||
strm.next_out = static_cast<Bytef*>(m_program_data.data());
|
||||
|
||||
int err = inflateInit(&strm);
|
||||
if (err != Z_OK)
|
||||
{
|
||||
Log_ErrorPrintf("inflateInit() failed: %d", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// we can do this in one pass because we preallocate the max size
|
||||
err = inflate(&strm, Z_NO_FLUSH);
|
||||
if (err != Z_STREAM_END)
|
||||
{
|
||||
Log_ErrorPrintf("inflate() failed: %d", err);
|
||||
inflateEnd(&strm);
|
||||
return false;
|
||||
}
|
||||
else if (strm.total_in != header.compressed_program_size)
|
||||
{
|
||||
Log_WarningPrintf("Mismatch between compressed size in header and stream %u/%u", header.compressed_program_size,
|
||||
static_cast<u32>(strm.total_in));
|
||||
}
|
||||
|
||||
m_program_data.resize(strm.total_out);
|
||||
file_pointer += header.compressed_program_size;
|
||||
inflateEnd(&strm);
|
||||
|
||||
u32 remaining_tag_data = static_cast<u32>(file_pointer_end - file_pointer);
|
||||
static constexpr char tag_signature[] = {'[', 'T', 'A', 'G', ']'};
|
||||
if (remaining_tag_data >= sizeof(tag_signature) &&
|
||||
std::memcmp(file_pointer, tag_signature, sizeof(tag_signature)) == 0)
|
||||
{
|
||||
file_pointer += sizeof(tag_signature);
|
||||
|
||||
while (file_pointer < file_pointer_end)
|
||||
{
|
||||
// skip whitespace
|
||||
while (file_pointer < file_pointer_end && *file_pointer <= 0x20)
|
||||
file_pointer++;
|
||||
|
||||
std::string tag_key;
|
||||
while (file_pointer < file_pointer_end && *file_pointer != '=')
|
||||
tag_key += (static_cast<char>(*(file_pointer++)));
|
||||
|
||||
// skip =
|
||||
if (file_pointer < file_pointer_end)
|
||||
file_pointer++;
|
||||
|
||||
std::string tag_value;
|
||||
while (file_pointer < file_pointer_end && *file_pointer != '\n')
|
||||
tag_value += (static_cast<char>(*(file_pointer++)));
|
||||
|
||||
if (!tag_key.empty())
|
||||
{
|
||||
Log_InfoPrintf("PSF Tag: '%s' = '%s'", tag_key.c_str(), tag_value.c_str());
|
||||
m_tags.emplace(std::move(tag_key), std::move(tag_value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace PSFLoader
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include "types.h"
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace PSFLoader {
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct PSFHeader
|
||||
{
|
||||
u8 id[3];
|
||||
u8 version;
|
||||
u32 reserved_area_size;
|
||||
u32 compressed_program_size;
|
||||
u32 program_crc32;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class File
|
||||
{
|
||||
public:
|
||||
using TagMap = std::map<std::string, std::string>;
|
||||
using ProgramData = std::vector<u8>;
|
||||
|
||||
ALWAYS_INLINE const ProgramData& GetProgramData() const { return m_program_data; }
|
||||
ALWAYS_INLINE const TagMap& GetTagMap() const { return m_tags; }
|
||||
|
||||
std::string GetTagString(const char* tag_name, const char* default_value) const;
|
||||
int GetTagInt(const char* tag_name, int default_value) const;
|
||||
float GetTagFloat(const char* tag_name, float default_value) const;
|
||||
|
||||
bool Load(const char* path);
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
MAX_PROGRAM_SIZE = 2 * 1024 * 1024
|
||||
};
|
||||
|
||||
ProgramData m_program_data;
|
||||
TagMap m_tags;
|
||||
};
|
||||
|
||||
} // namespace PSFLoader
|
|
@ -17,6 +17,7 @@
|
|||
#include "mdec.h"
|
||||
#include "memory_card.h"
|
||||
#include "pad.h"
|
||||
#include "psf_loader.h"
|
||||
#include "save_state_version.h"
|
||||
#include "sio.h"
|
||||
#include "spu.h"
|
||||
|
@ -131,11 +132,14 @@ bool System::Boot(const SystemBootParameters& params)
|
|||
// Load CD image up and detect region.
|
||||
std::unique_ptr<CDImage> media;
|
||||
bool exe_boot = false;
|
||||
bool psf_boot = false;
|
||||
if (!params.filename.empty())
|
||||
{
|
||||
exe_boot = GameList::IsExeFileName(params.filename.c_str());
|
||||
if (exe_boot)
|
||||
psf_boot = (!exe_boot && GameList::IsPsfFileName(params.filename.c_str()));
|
||||
if (exe_boot || psf_boot)
|
||||
{
|
||||
// TODO: Pull region from PSF
|
||||
if (m_region == ConsoleRegion::Auto)
|
||||
{
|
||||
Log_InfoPrintf("Defaulting to NTSC-U region for executable.");
|
||||
|
@ -203,6 +207,11 @@ bool System::Boot(const SystemBootParameters& params)
|
|||
m_host_interface->ReportFormattedError("Failed to load EXE file '%s'", params.filename.c_str());
|
||||
return false;
|
||||
}
|
||||
else if (psf_boot && !LoadPSF(params.filename.c_str(), *bios_image))
|
||||
{
|
||||
m_host_interface->ReportFormattedError("Failed to load EXE file '%s'", params.filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Notify change of disc.
|
||||
UpdateRunningGame(params.filename.c_str(), media.get());
|
||||
|
@ -604,6 +613,69 @@ bool System::LoadEXE(const char* filename, std::vector<u8>& bios_image)
|
|||
return BIOS::PatchBIOSForEXE(bios_image, r_pc, r_gp, r_sp, r_fp);
|
||||
}
|
||||
|
||||
bool System::LoadEXEFromBuffer(const void* buffer, u32 buffer_size, std::vector<u8>& bios_image)
|
||||
{
|
||||
const u8* buffer_ptr = static_cast<const u8*>(buffer);
|
||||
const u8* buffer_end = static_cast<const u8*>(buffer) + buffer_size;
|
||||
|
||||
BIOS::PSEXEHeader header;
|
||||
if (buffer_size < sizeof(header))
|
||||
return false;
|
||||
|
||||
std::memcpy(&header, buffer_ptr, sizeof(header));
|
||||
buffer_ptr += sizeof(header);
|
||||
|
||||
if (!BIOS::IsValidPSExeHeader(header, static_cast<u32>(buffer_end - buffer_ptr)))
|
||||
return false;
|
||||
|
||||
if (header.memfill_size > 0)
|
||||
{
|
||||
const u32 words_to_write = header.memfill_size / 4;
|
||||
u32 address = header.memfill_start & ~UINT32_C(3);
|
||||
for (u32 i = 0; i < words_to_write; i++)
|
||||
{
|
||||
m_cpu->SafeWriteMemoryWord(address, 0);
|
||||
address += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
if (header.file_size >= 4)
|
||||
{
|
||||
std::vector<u32> data_words((header.file_size + 3) / 4);
|
||||
if ((buffer_end - buffer_ptr) < header.file_size)
|
||||
return false;
|
||||
|
||||
std::memcpy(data_words.data(), buffer_ptr, header.file_size);
|
||||
|
||||
const u32 num_words = header.file_size / 4;
|
||||
u32 address = header.load_address;
|
||||
for (u32 i = 0; i < num_words; i++)
|
||||
{
|
||||
m_cpu->SafeWriteMemoryWord(address, data_words[i]);
|
||||
address += sizeof(u32);
|
||||
}
|
||||
}
|
||||
|
||||
// patch the BIOS to jump to the executable directly
|
||||
const u32 r_pc = header.initial_pc;
|
||||
const u32 r_gp = header.initial_gp;
|
||||
const u32 r_sp = header.initial_sp_base + header.initial_sp_offset;
|
||||
const u32 r_fp = header.initial_sp_base + header.initial_sp_offset;
|
||||
return BIOS::PatchBIOSForEXE(bios_image, r_pc, r_gp, r_sp, r_fp);
|
||||
}
|
||||
|
||||
bool System::LoadPSF(const char* filename, std::vector<u8>& bios_image)
|
||||
{
|
||||
Log_InfoPrintf("Loading PSF file from '%s'", filename);
|
||||
|
||||
PSFLoader::File psf;
|
||||
if (!psf.Load(filename))
|
||||
return false;
|
||||
|
||||
const std::vector<u8>& exe_data = psf.GetProgramData();
|
||||
return LoadEXEFromBuffer(exe_data.data(), static_cast<u32>(exe_data.size()), bios_image);
|
||||
}
|
||||
|
||||
bool System::SetExpansionROM(const char* filename)
|
||||
{
|
||||
std::FILE* fp = std::fopen(filename, "rb");
|
||||
|
|
|
@ -120,6 +120,8 @@ public:
|
|||
void ResetPerformanceCounters();
|
||||
|
||||
bool LoadEXE(const char* filename, std::vector<u8>& bios_image);
|
||||
bool LoadEXEFromBuffer(const void* buffer, u32 buffer_size, std::vector<u8>& bios_image);
|
||||
bool LoadPSF(const char* filename, std::vector<u8>& bios_image);
|
||||
bool SetExpansionROM(const char* filename);
|
||||
|
||||
// Adds ticks to the global tick counter, simulating the CPU being stalled.
|
||||
|
|
Loading…
Reference in New Issue