Add initial Linux support.
This commit is contained in:
parent
8acfc3d557
commit
1e57195ded
|
@ -14,6 +14,7 @@ include_directories(third-party/imgui/backends)
|
||||||
include_directories(third-party/sdl/)
|
include_directories(third-party/sdl/)
|
||||||
include_directories(third-party/fmt/include)
|
include_directories(third-party/fmt/include)
|
||||||
include_directories(third-party/magic_enum/include)
|
include_directories(third-party/magic_enum/include)
|
||||||
|
include_directories(third-party/zydis/include/Zydis)
|
||||||
add_subdirectory("third-party")
|
add_subdirectory("third-party")
|
||||||
|
|
||||||
#=================== EXAMPLE ===================
|
#=================== EXAMPLE ===================
|
||||||
|
|
36
README.md
36
README.md
|
@ -1,13 +1,13 @@
|
||||||
# shadPS4
|
# shadPS4
|
||||||
|
|
||||||
An early PS4 emulator for Windows
|
An early PS4 emulator for Windows and Linux
|
||||||
|
|
||||||
|
|
||||||
[Check us on twitter](https://twitter.com/shadps4 "Check us on twitter")
|
[Check us on twitter](https://twitter.com/shadps4 "Check us on twitter")
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
|
|
||||||
Currently it can only load PS4 ELF files .
|
Currently it can only load PS4 ELF files.
|
||||||
|
|
||||||
![](https://geps.dev/progress/60) Elf Loader
|
![](https://geps.dev/progress/60) Elf Loader
|
||||||
|
|
||||||
|
@ -21,18 +21,38 @@ The project started as a fun project. Due to short amount of free time probably
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
|
|
||||||
Project is using cmake files. To build, Visual Studio 2022 is enough
|
## Windows
|
||||||
|
|
||||||
|
The project is using cmake files. To build, just use Visual Studio 2022.
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
Generate the build directory in the shadPS4 directory:
|
||||||
|
```
|
||||||
|
cmake -S . -B build/
|
||||||
|
```
|
||||||
|
|
||||||
|
Enter the directory:
|
||||||
|
```
|
||||||
|
cd build/
|
||||||
|
```
|
||||||
|
|
||||||
|
Use make to build the project:
|
||||||
|
```
|
||||||
|
make -j$(nproc)
|
||||||
|
```
|
||||||
|
|
||||||
|Platform|Build status|
|
|Platform|Build status|
|
||||||
|--------|------------|
|
|--------|------------|
|
||||||
|Windows build|[![Windows](https://github.com/georgemoralis/shadPS4/actions/workflows/windows.yml/badge.svg)](https://github.com/georgemoralis/shadPS4/actions/workflows/windows.yml)
|
|Windows build|[![Windows](https://github.com/georgemoralis/shadPS4/actions/workflows/windows.yml/badge.svg)](https://github.com/georgemoralis/shadPS4/actions/workflows/windows.yml)
|
||||||
|
|Linux build| TODO
|
||||||
|
|
||||||
|
|
||||||
To discuss this emulator please join our Discord server: [![Discord](https://img.shields.io/discord/1080089157554155590)](https://discord.gg/MyZRaBngxA)
|
To discuss this emulator please join our Discord server: [![Discord](https://img.shields.io/discord/1080089157554155590)](https://discord.gg/MyZRaBngxA)
|
||||||
|
|
||||||
# Who are you?
|
# Who are you?
|
||||||
|
|
||||||
Old emulator fans and devs can recongnize me as "shadow" . I was the founder and coder for a lot of emulation projects
|
Old emulator fans and devs can recongnize me as "shadow". I was the founder and coder for a lot of emulation projects:
|
||||||
* PCSX
|
* PCSX
|
||||||
* PCSX2
|
* PCSX2
|
||||||
* PCSP
|
* PCSP
|
||||||
|
@ -42,12 +62,12 @@ Old emulator fans and devs can recongnize me as "shadow" . I was the founder and
|
||||||
|
|
||||||
# Contribution
|
# Contribution
|
||||||
|
|
||||||
Currently I accept any kind of contribution some hints for what is might be useful.
|
I currently accept any kind of contribution, here is a list of some items that may be useful:
|
||||||
|
|
||||||
* PKG extractor (there was an initial work on this, just search project history commits)
|
* PKG extractor (there was an initial work on this, just search project history commits).
|
||||||
* Initial GUI with imgui, SDL3 and Vulkan.
|
* Initial GUI with imgui, SDL3 and Vulkan.
|
||||||
* Better logging system with spdlog
|
* Better logging system with spdlog.
|
||||||
* ..to be filled
|
* to be filled...
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,23 @@ FsFile::FsFile()
|
||||||
{
|
{
|
||||||
m_file = nullptr;
|
m_file = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
FsFile::FsFile(const std::string& path, fsOpenMode mode)
|
FsFile::FsFile(const std::string& path, fsOpenMode mode)
|
||||||
{
|
{
|
||||||
Open(path, mode);
|
Open(path, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FsFile::Open(const std::string& path, fsOpenMode mode)
|
bool FsFile::Open(const std::string& path, fsOpenMode mode)
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
fopen_s(&m_file, path.c_str(), getOpenMode(mode));
|
#ifdef _WIN64
|
||||||
|
fopen_s(&m_file, path.c_str(), getOpenMode(mode));
|
||||||
|
#else
|
||||||
|
m_file = std::fopen(path.c_str(), getOpenMode(mode));
|
||||||
|
#endif
|
||||||
return IsOpen();
|
return IsOpen();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FsFile::Close()
|
bool FsFile::Close()
|
||||||
{
|
{
|
||||||
if (!IsOpen() || std::fclose(m_file) != 0) {
|
if (!IsOpen() || std::fclose(m_file) != 0) {
|
||||||
|
@ -24,6 +31,7 @@ bool FsFile::Close()
|
||||||
m_file = nullptr;
|
m_file = nullptr;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
FsFile::~FsFile()
|
FsFile::~FsFile()
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
|
@ -52,16 +60,26 @@ u32 FsFile::ReadBytes(void* dst, u64 size)
|
||||||
|
|
||||||
bool FsFile::Seek(s64 offset, fsSeekMode mode)
|
bool FsFile::Seek(s64 offset, fsSeekMode mode)
|
||||||
{
|
{
|
||||||
if (!IsOpen() || _fseeki64(m_file, offset, getSeekMode(mode)) != 0) {
|
#ifdef _WIN64
|
||||||
return false;
|
if (!IsOpen() || _fseeki64(m_file, offset, getSeekMode(mode)) != 0) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (!IsOpen() || fseeko64(m_file, offset, getSeekMode(mode)) != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 FsFile::Tell() const
|
u64 FsFile::Tell() const
|
||||||
{
|
{
|
||||||
if (IsOpen()) {
|
if (IsOpen()) {
|
||||||
return _ftelli64(m_file);
|
#ifdef _WIN64
|
||||||
|
return _ftelli64(m_file);
|
||||||
|
#else
|
||||||
|
return ftello64(m_file);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -69,17 +87,31 @@ u64 FsFile::Tell() const
|
||||||
}
|
}
|
||||||
u64 FsFile::getFileSize()
|
u64 FsFile::getFileSize()
|
||||||
{
|
{
|
||||||
u64 pos = _ftelli64(m_file);
|
#ifdef _WIN64
|
||||||
if (_fseeki64(m_file, 0, SEEK_END) != 0) {
|
u64 pos = _ftelli64(m_file);
|
||||||
|
if (_fseeki64(m_file, 0, SEEK_END) != 0) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 size = _ftelli64(m_file);
|
u64 size = _ftelli64(m_file);
|
||||||
if (_fseeki64(m_file, pos, SEEK_SET) != 0) {
|
if (_fseeki64(m_file, pos, SEEK_SET) != 0) {
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
u64 pos = ftello64(m_file);
|
||||||
|
if (fseeko64(m_file, 0, SEEK_END) != 0) {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 size = ftello64(m_file);
|
||||||
|
if (fseeko64(m_file, pos, SEEK_SET) != 0) {
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "../Types.h"
|
#include "../types.h"
|
||||||
|
|
||||||
enum fsOpenMode
|
enum fsOpenMode
|
||||||
{
|
{
|
||||||
|
@ -60,4 +60,3 @@ public:
|
||||||
return m_file;
|
return m_file;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
#include "../Core/PS4/Loader/Elf.h"
|
#include "../Core/PS4/Loader/Elf.h"
|
||||||
#include <windows.h>
|
|
||||||
|
#ifdef _WIN64
|
||||||
|
#include <windows.h>
|
||||||
|
#else
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "../Util/Log.h"
|
#include "../Util/Log.h"
|
||||||
|
|
||||||
namespace Memory
|
namespace Memory
|
||||||
|
@ -9,18 +15,31 @@ namespace Memory
|
||||||
u64 memory_alloc(u64 address, u64 size)
|
u64 memory_alloc(u64 address, u64 size)
|
||||||
{
|
{
|
||||||
//TODO it supports only execute_read_write mode
|
//TODO it supports only execute_read_write mode
|
||||||
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)),
|
#ifdef _WIN64
|
||||||
size,
|
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)),
|
||||||
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
|
size,
|
||||||
PAGE_EXECUTE_READWRITE));
|
static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE),
|
||||||
|
PAGE_EXECUTE_READWRITE));
|
||||||
|
|
||||||
if (ptr == 0)
|
if (ptr == 0)
|
||||||
{
|
{
|
||||||
auto err = static_cast<u32>(GetLastError());
|
auto err = static_cast<u32>(GetLastError());
|
||||||
LOG_ERROR_IF(true,"VirtualAlloc() failed: 0x{:X}\n", err);
|
LOG_ERROR_IF(true, "VirtualAlloc() failed: 0x{:X}\n", err);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
auto ptr = reinterpret_cast<uintptr_t>(mmap(reinterpret_cast<void *>(static_cast<uintptr_t>(address)),
|
||||||
|
size,
|
||||||
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
||||||
|
MAP_ANONYMOUS | MAP_PRIVATE,
|
||||||
|
-1,
|
||||||
|
0));
|
||||||
|
|
||||||
|
if (ptr == reinterpret_cast<uintptr_t>MAP_FAILED)
|
||||||
|
{
|
||||||
|
LOG_ERROR_IF(true, "mmap() failed: {}\n", std::strerror(errno));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,6 +7,13 @@
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
#include "../../../Util/Log.h"
|
#include "../../../Util/Log.h"
|
||||||
|
|
||||||
|
#ifdef _WIN64
|
||||||
|
#define DBG_BREAKPOINT __debugbreak();
|
||||||
|
#else
|
||||||
|
#include <signal.h>
|
||||||
|
#define DBG_BREAKPOINT raise(SIGTRAP);
|
||||||
|
#endif
|
||||||
|
|
||||||
constexpr bool debug_elf = true;
|
constexpr bool debug_elf = true;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
@ -29,7 +36,6 @@ static self_header* load_self(FsFile& f)
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static self_segment_header* load_self_segments(FsFile& f, u16 num)
|
static self_segment_header* load_self_segments(FsFile& f, u16 num)
|
||||||
{
|
{
|
||||||
auto* segs = new self_segment_header[num];
|
auto* segs = new self_segment_header[num];
|
||||||
|
@ -39,7 +45,6 @@ static self_segment_header* load_self_segments(FsFile& f, u16 num)
|
||||||
return segs;
|
return segs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static elf_header* load_elf_header(FsFile& f)
|
static elf_header* load_elf_header(FsFile& f)
|
||||||
{
|
{
|
||||||
auto* m_elf_header = new elf_header;
|
auto* m_elf_header = new elf_header;
|
||||||
|
@ -95,6 +100,7 @@ void Elf::Reset()//reset all variables
|
||||||
m_elf_shdr = nullptr;
|
m_elf_shdr = nullptr;
|
||||||
m_self_id_header = nullptr;
|
m_self_id_header = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Elf::Open(const std::string& file_name)
|
void Elf::Open(const std::string& file_name)
|
||||||
{
|
{
|
||||||
Reset();//reset all variables
|
Reset();//reset all variables
|
||||||
|
@ -151,7 +157,6 @@ void Elf::Open(const std::string& file_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DebugDump();
|
DebugDump();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +277,9 @@ bool Elf::isElfFile() const
|
||||||
void Elf::DebugDump() {
|
void Elf::DebugDump() {
|
||||||
std::vector<spdlog::sink_ptr> sinks;
|
std::vector<spdlog::sink_ptr> sinks;
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>());
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(L"output.log", true)); //this might work only in windows ;/
|
#ifdef _WIN64
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(L"output.log", true)); //this might work only in windows ;/
|
||||||
|
#endif
|
||||||
spdlog::set_default_logger(std::make_shared<spdlog::logger>("shadps4 logger", begin(sinks), end(sinks)));
|
spdlog::set_default_logger(std::make_shared<spdlog::logger>("shadps4 logger", begin(sinks), end(sinks)));
|
||||||
auto f = std::make_unique<spdlog::pattern_formatter>("%v", spdlog::pattern_time_type::local, std::string("")); // disable eol
|
auto f = std::make_unique<spdlog::pattern_formatter>("%v", spdlog::pattern_time_type::local, std::string("")); // disable eol
|
||||||
spdlog::set_formatter(std::move(f));
|
spdlog::set_formatter(std::move(f));
|
||||||
|
@ -355,6 +362,7 @@ std::string Elf::SElfHeaderStr() {
|
||||||
header+= fmt::format("padding3 ...........: {:#010x}\n", m_self->padding3);
|
header+= fmt::format("padding3 ...........: {:#010x}\n", m_self->padding3);
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Elf::SELFSegHeader(u16 no)
|
std::string Elf::SELFSegHeader(u16 no)
|
||||||
{
|
{
|
||||||
auto segment_header = m_self_segments[no];
|
auto segment_header = m_self_segments[no];
|
||||||
|
@ -365,6 +373,7 @@ std::string Elf::SELFSegHeader(u16 no)
|
||||||
header += fmt::format("memory size ......: {}\n", segment_header.memory_size);
|
header += fmt::format("memory size ......: {}\n", segment_header.memory_size);
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Elf::ElfHeaderStr()
|
std::string Elf::ElfHeaderStr()
|
||||||
{
|
{
|
||||||
std::string header = fmt::format("======= Elf header ===========\n");
|
std::string header = fmt::format("======= Elf header ===========\n");
|
||||||
|
@ -514,6 +523,7 @@ std::string Elf::ElfPHeaderStr(u16 no)
|
||||||
header += fmt::format("p_align ...: {:#018x}\n", (m_elf_phdr + no)->p_align);
|
header += fmt::format("p_align ...: {:#018x}\n", (m_elf_phdr + no)->p_align);
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Elf::LoadSegment(u64 virtual_addr, u64 file_offset, u64 size)
|
void Elf::LoadSegment(u64 virtual_addr, u64 file_offset, u64 size)
|
||||||
{
|
{
|
||||||
if (m_self!=nullptr)
|
if (m_self!=nullptr)
|
||||||
|
@ -530,13 +540,13 @@ void Elf::LoadSegment(u64 virtual_addr, u64 file_offset, u64 size)
|
||||||
if (file_offset >= phdr.p_offset && file_offset < phdr.p_offset + phdr.p_filesz)
|
if (file_offset >= phdr.p_offset && file_offset < phdr.p_offset + phdr.p_filesz)
|
||||||
{
|
{
|
||||||
auto offset = file_offset - phdr.p_offset;
|
auto offset = file_offset - phdr.p_offset;
|
||||||
m_f->Seek(offset + seg.file_offset,fsSeekMode::fsSeekSet);
|
m_f->Seek(offset + seg.file_offset, fsSeekMode::fsSeekSet);
|
||||||
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(virtual_addr)), size);
|
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(virtual_addr)), size);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
__debugbreak();//hmm we didn't return something...
|
DBG_BREAKPOINT //hmm we didn't return something...
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -545,6 +555,7 @@ void Elf::LoadSegment(u64 virtual_addr, u64 file_offset, u64 size)
|
||||||
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(virtual_addr)), size);
|
m_f->Read(reinterpret_cast<void*>(static_cast<uintptr_t>(virtual_addr)), size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 Elf::GetElfEntry()
|
u64 Elf::GetElfEntry()
|
||||||
{
|
{
|
||||||
return m_elf_header->e_entry;
|
return m_elf_header->e_entry;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "zydis/Zydis.h"
|
#include "Zydis.h"
|
||||||
#include "../types.h"
|
#include "../types.h"
|
||||||
|
|
||||||
class Disassembler
|
class Disassembler
|
||||||
|
|
Loading…
Reference in New Issue