mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 03:25:16 +00:00
Partial commit: Emu (the rest)
This commit is contained in:
parent
c7738b8b37
commit
f8f067ca7c
28 changed files with 1043 additions and 4765 deletions
|
@ -1,951 +0,0 @@
|
|||
#pragma once
|
||||
#include "CPUInstrTable.h"
|
||||
|
||||
class CPUDecoder
|
||||
{
|
||||
public:
|
||||
virtual u32 DecodeMemory(const u32 address) = 0;
|
||||
|
||||
virtual ~CPUDecoder() = default;
|
||||
};
|
||||
|
||||
template<typename TO>
|
||||
class InstrCaller
|
||||
{
|
||||
public:
|
||||
virtual ~InstrCaller<TO>() = default;
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const = 0;
|
||||
|
||||
virtual u32 operator [](u32) const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO>
|
||||
class InstrBinder_0 : public InstrCaller<TO>
|
||||
{
|
||||
typedef void (TO::*func_t)();
|
||||
func_t m_func;
|
||||
|
||||
public:
|
||||
InstrBinder_0(func_t func)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
(op->*m_func)();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, typename T1>
|
||||
class InstrBinder_1 : public InstrCaller<TO>
|
||||
{
|
||||
typedef void (TO::*func_t)(T1);
|
||||
func_t m_func;
|
||||
const CodeFieldBase& m_arg_func_1;
|
||||
|
||||
public:
|
||||
InstrBinder_1(func_t func, const CodeFieldBase& arg_func_1)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
, m_arg_func_1(arg_func_1)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
(op->*m_func)(static_cast<T1>(m_arg_func_1(code)));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, typename T1, typename T2>
|
||||
class InstrBinder_2 : public InstrCaller<TO>
|
||||
{
|
||||
typedef void (TO::*func_t)(T1, T2);
|
||||
func_t m_func;
|
||||
const CodeFieldBase& m_arg_func_1;
|
||||
const CodeFieldBase& m_arg_func_2;
|
||||
|
||||
public:
|
||||
InstrBinder_2(func_t func, const CodeFieldBase& arg_func_1, const CodeFieldBase& arg_func_2)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
, m_arg_func_1(arg_func_1)
|
||||
, m_arg_func_2(arg_func_2)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
(op->*m_func)(
|
||||
static_cast<T1>(m_arg_func_1(code)),
|
||||
static_cast<T2>(m_arg_func_2(code))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3>
|
||||
class InstrBinder_3 : public InstrCaller<TO>
|
||||
{
|
||||
typedef void (TO::*func_t)(T1, T2, T3);
|
||||
func_t m_func;
|
||||
const CodeFieldBase& m_arg_func_1;
|
||||
const CodeFieldBase& m_arg_func_2;
|
||||
const CodeFieldBase& m_arg_func_3;
|
||||
|
||||
public:
|
||||
InstrBinder_3(func_t func,
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
, m_arg_func_1(arg_func_1)
|
||||
, m_arg_func_2(arg_func_2)
|
||||
, m_arg_func_3(arg_func_3)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
(op->*m_func)(
|
||||
static_cast<T1>(m_arg_func_1(code)),
|
||||
static_cast<T2>(m_arg_func_2(code)),
|
||||
static_cast<T3>(m_arg_func_3(code))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3, typename T4>
|
||||
class InstrBinder_4 : public InstrCaller<TO>
|
||||
{
|
||||
typedef void (TO::*func_t)(T1, T2, T3, T4);
|
||||
func_t m_func;
|
||||
const CodeFieldBase& m_arg_func_1;
|
||||
const CodeFieldBase& m_arg_func_2;
|
||||
const CodeFieldBase& m_arg_func_3;
|
||||
const CodeFieldBase& m_arg_func_4;
|
||||
|
||||
public:
|
||||
InstrBinder_4(func_t func,
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3,
|
||||
const CodeFieldBase& arg_func_4)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
, m_arg_func_1(arg_func_1)
|
||||
, m_arg_func_2(arg_func_2)
|
||||
, m_arg_func_3(arg_func_3)
|
||||
, m_arg_func_4(arg_func_4)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
(op->*m_func)(
|
||||
static_cast<T1>(m_arg_func_1(code)),
|
||||
static_cast<T2>(m_arg_func_2(code)),
|
||||
static_cast<T3>(m_arg_func_3(code)),
|
||||
static_cast<T4>(m_arg_func_4(code))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
class InstrBinder_5 : public InstrCaller<TO>
|
||||
{
|
||||
typedef void (TO::*func_t)(T1, T2, T3, T4, T5);
|
||||
func_t m_func;
|
||||
const CodeFieldBase& m_arg_func_1;
|
||||
const CodeFieldBase& m_arg_func_2;
|
||||
const CodeFieldBase& m_arg_func_3;
|
||||
const CodeFieldBase& m_arg_func_4;
|
||||
const CodeFieldBase& m_arg_func_5;
|
||||
|
||||
public:
|
||||
InstrBinder_5(func_t func,
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3,
|
||||
const CodeFieldBase& arg_func_4,
|
||||
const CodeFieldBase& arg_func_5)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
, m_arg_func_1(arg_func_1)
|
||||
, m_arg_func_2(arg_func_2)
|
||||
, m_arg_func_3(arg_func_3)
|
||||
, m_arg_func_4(arg_func_4)
|
||||
, m_arg_func_5(arg_func_5)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
(op->*m_func)(
|
||||
static_cast<T1>(m_arg_func_1(code)),
|
||||
static_cast<T2>(m_arg_func_2(code)),
|
||||
static_cast<T3>(m_arg_func_3(code)),
|
||||
static_cast<T4>(m_arg_func_4(code)),
|
||||
static_cast<T5>(m_arg_func_5(code))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
class InstrBinder_6 : public InstrCaller<TO>
|
||||
{
|
||||
typedef void (TO::*func_t)(T1, T2, T3, T4, T5, T6);
|
||||
func_t m_func;
|
||||
const CodeFieldBase& m_arg_func_1;
|
||||
const CodeFieldBase& m_arg_func_2;
|
||||
const CodeFieldBase& m_arg_func_3;
|
||||
const CodeFieldBase& m_arg_func_4;
|
||||
const CodeFieldBase& m_arg_func_5;
|
||||
const CodeFieldBase& m_arg_func_6;
|
||||
|
||||
public:
|
||||
InstrBinder_6(func_t func,
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3,
|
||||
const CodeFieldBase& arg_func_4,
|
||||
const CodeFieldBase& arg_func_5,
|
||||
const CodeFieldBase& arg_func_6)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
, m_arg_func_1(arg_func_1)
|
||||
, m_arg_func_2(arg_func_2)
|
||||
, m_arg_func_3(arg_func_3)
|
||||
, m_arg_func_4(arg_func_4)
|
||||
, m_arg_func_5(arg_func_5)
|
||||
, m_arg_func_6(arg_func_6)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
(op->*m_func)(
|
||||
static_cast<T1>(m_arg_func_1(code)),
|
||||
static_cast<T2>(m_arg_func_2(code)),
|
||||
static_cast<T3>(m_arg_func_3(code)),
|
||||
static_cast<T4>(m_arg_func_4(code)),
|
||||
static_cast<T5>(m_arg_func_5(code)),
|
||||
static_cast<T6>(m_arg_func_6(code))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO>
|
||||
InstrCaller<TO>* instr_bind(void (TO::*func)())
|
||||
{
|
||||
return new InstrBinder_0<TO>(func);
|
||||
}
|
||||
|
||||
template<typename TO, typename T1>
|
||||
InstrCaller<TO>* instr_bind(void (TO::*func)(T1), const CodeFieldBase& arg_func_1)
|
||||
{
|
||||
return new InstrBinder_1<TO, T1>(func, arg_func_1);
|
||||
}
|
||||
|
||||
template<typename TO, typename T1, typename T2>
|
||||
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2),
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2)
|
||||
{
|
||||
return new InstrBinder_2<TO, T1, T2>(func, arg_func_1, arg_func_2);
|
||||
}
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3>
|
||||
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3),
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3)
|
||||
{
|
||||
return new InstrBinder_3<TO, T1, T2, T3>(func, arg_func_1, arg_func_2, arg_func_3);
|
||||
}
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3, typename T4>
|
||||
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3, T4),
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3,
|
||||
const CodeFieldBase& arg_func_4)
|
||||
{
|
||||
return new InstrBinder_4<TO, T1, T2, T3, T4>(func, arg_func_1, arg_func_2, arg_func_3, arg_func_4);
|
||||
}
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3, T4, T5),
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3,
|
||||
const CodeFieldBase& arg_func_4,
|
||||
const CodeFieldBase& arg_func_5)
|
||||
{
|
||||
return new InstrBinder_5<TO, T1, T2, T3, T4, T5>(func, arg_func_1, arg_func_2, arg_func_3, arg_func_4, arg_func_5);
|
||||
}
|
||||
|
||||
template<typename TO, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
InstrCaller<TO>* instr_bind(void (TO::*func)(T1, T2, T3, T4, T5, T6),
|
||||
const CodeFieldBase& arg_func_1,
|
||||
const CodeFieldBase& arg_func_2,
|
||||
const CodeFieldBase& arg_func_3,
|
||||
const CodeFieldBase& arg_func_4,
|
||||
const CodeFieldBase& arg_func_5,
|
||||
const CodeFieldBase& arg_func_6)
|
||||
{
|
||||
return new InstrBinder_6<TO, T1, T2, T3, T4, T5, T6>(func, arg_func_1, arg_func_2, arg_func_3, arg_func_4, arg_func_5, arg_func_6);
|
||||
}
|
||||
|
||||
template<typename TO>
|
||||
class InstrBase : public InstrCaller<TO>
|
||||
{
|
||||
protected:
|
||||
std::string m_name;
|
||||
const u32 m_opcode;
|
||||
CodeFieldBase** m_args;
|
||||
const uint m_args_count;
|
||||
|
||||
public:
|
||||
InstrBase(const std::string& name, int opcode, uint args_count)
|
||||
: InstrCaller<TO>()
|
||||
, m_name(name)
|
||||
, m_opcode(opcode)
|
||||
, m_args_count(args_count)
|
||||
, m_args(args_count ? new CodeFieldBase*[args_count] : nullptr)
|
||||
{
|
||||
std::transform(
|
||||
name.begin(),
|
||||
name.end(),
|
||||
m_name.begin(),
|
||||
[](const char &a)
|
||||
{
|
||||
char b = tolower(a);
|
||||
if (b == '_') b = '.';
|
||||
return b;
|
||||
});
|
||||
}
|
||||
|
||||
InstrBase(const InstrBase &source)
|
||||
: InstrCaller<TO>(source)
|
||||
, m_name(source.m_name)
|
||||
, m_opcode(source.m_opcode)
|
||||
, m_args_count(source.m_args_count)
|
||||
, m_args(source.m_args_count ? new CodeFieldBase*[source.m_args_count] : nullptr)
|
||||
{
|
||||
for(uint i = 0; i < source.m_args_count; ++i)
|
||||
m_args[i] = source.m_args[i];
|
||||
}
|
||||
|
||||
virtual ~InstrBase()
|
||||
{
|
||||
if (m_args) {
|
||||
// m_args contains pointers to statically allocated CodeFieldBase objects
|
||||
// We shouldn't call delete on these, they aren't allocated with new
|
||||
|
||||
// The m_args array itself, however, should be deleted
|
||||
delete[] m_args;
|
||||
}
|
||||
}
|
||||
|
||||
force_inline const std::string& GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
force_inline const uint GetArgCount() const
|
||||
{
|
||||
return m_args_count;
|
||||
}
|
||||
|
||||
force_inline const CodeFieldBase& GetArg(uint index) const
|
||||
{
|
||||
assert(index < m_args_count);
|
||||
return *m_args[index];
|
||||
}
|
||||
|
||||
void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
decode(op, code);
|
||||
}
|
||||
|
||||
u32 operator()(const std::vector<u32>& args) const
|
||||
{
|
||||
return encode(args);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const=0;
|
||||
virtual u32 encode(const std::vector<u32>& args) const=0;
|
||||
};
|
||||
|
||||
template<int _count, typename TO>
|
||||
class InstrList : public InstrCaller<TO>
|
||||
{
|
||||
public:
|
||||
static const int count = _count;
|
||||
|
||||
protected:
|
||||
const CodeFieldBase& m_func;
|
||||
InstrCaller<TO>* m_instrs[count];
|
||||
InstrBase<TO>* m_instrs_info[count];
|
||||
InstrCaller<TO>* m_error_func;
|
||||
InstrCaller<TO>* m_parent;
|
||||
int m_opcode;
|
||||
|
||||
public:
|
||||
InstrList(const CodeFieldBase& func, InstrCaller<TO>* error_func)
|
||||
: InstrCaller<TO>()
|
||||
, m_func(func)
|
||||
, m_error_func(error_func)
|
||||
, m_parent(nullptr)
|
||||
, m_opcode(-1)
|
||||
{
|
||||
for(int i=0; i<count; ++i)
|
||||
{
|
||||
m_instrs[i] = error_func;
|
||||
}
|
||||
|
||||
memset(m_instrs_info, 0, sizeof(InstrBase<TO>*) * count);
|
||||
}
|
||||
|
||||
virtual ~InstrList()
|
||||
{
|
||||
bool deletedErrorFunc = false;
|
||||
|
||||
// Clean up m_instrs
|
||||
for(int i = 0; i < count; ++i)
|
||||
{
|
||||
InstrCaller<TO>* deleteMe = m_instrs[i];
|
||||
|
||||
if (deleteMe) { // deleteMe will be a nullptr if we've already deleted it through another reference
|
||||
// Remove any instances of pointers to this instruction caller from our m_instrs list
|
||||
m_instrs[i] = nullptr;
|
||||
for (int j = i + 1; j < count; j++) {
|
||||
if (m_instrs[j] == deleteMe) {
|
||||
m_instrs[j] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're deleting the error handler here, remember it so we don't try to delete it again later
|
||||
if (deleteMe == m_error_func) {
|
||||
deletedErrorFunc = true;
|
||||
}
|
||||
|
||||
// Delete the instruction caller
|
||||
delete deleteMe;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up m_instrs_info
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
InstrBase<TO>* deleteMe = m_instrs_info[i];
|
||||
|
||||
if (deleteMe) {
|
||||
m_instrs_info[i] = nullptr;
|
||||
for (int j = i + 1; j < count; j++) {
|
||||
if (m_instrs_info[j] == deleteMe) {
|
||||
m_instrs[j] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
delete deleteMe;
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't already deleted our error handler, and we have one, then delete it now
|
||||
if (!deletedErrorFunc && m_error_func)
|
||||
{
|
||||
delete m_error_func;
|
||||
}
|
||||
}
|
||||
|
||||
void set_parent(InstrCaller<TO>* parent, int opcode)
|
||||
{
|
||||
m_opcode = opcode;
|
||||
m_parent = parent;
|
||||
}
|
||||
|
||||
InstrCaller<TO>* get_parent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
u32 get_opcode() const
|
||||
{
|
||||
return m_opcode;
|
||||
}
|
||||
|
||||
void set_error_func(InstrCaller<TO>* error_func)
|
||||
{
|
||||
for(int i=0; i<count; ++i)
|
||||
{
|
||||
if(m_instrs[i] == m_error_func || !m_instrs[i])
|
||||
{
|
||||
m_instrs[i] = error_func;
|
||||
}
|
||||
}
|
||||
|
||||
m_error_func = error_func;
|
||||
}
|
||||
|
||||
void set_instr(uint pos, InstrCaller<TO>* func, InstrBase<TO>* info = nullptr)
|
||||
{
|
||||
assert(pos < count);
|
||||
m_instrs[pos] = func;
|
||||
m_instrs_info[pos] = info;
|
||||
}
|
||||
|
||||
InstrCaller<TO>* get_instr(int pos) const
|
||||
{
|
||||
assert(pos < count);
|
||||
return m_instrs[pos];
|
||||
}
|
||||
|
||||
InstrBase<TO>* get_instr_info(int pos) const
|
||||
{
|
||||
assert(pos < count);
|
||||
return m_instrs_info[pos];
|
||||
}
|
||||
|
||||
u32 encode(u32 entry) const
|
||||
{
|
||||
return m_func[entry] | (m_parent ? (*m_parent)[m_opcode] : 0);
|
||||
}
|
||||
|
||||
void decode(TO* op, u32 entry, u32 code) const
|
||||
{
|
||||
(*m_instrs[entry])(op, code);
|
||||
}
|
||||
|
||||
virtual void operator ()(TO* op, u32 code) const
|
||||
{
|
||||
decode(op, m_func(code) & (count - 1), code);
|
||||
}
|
||||
|
||||
virtual u32 operator [](u32 entry) const
|
||||
{
|
||||
return encode(entry);
|
||||
}
|
||||
};
|
||||
|
||||
template<int count1, int count2, typename TO>
|
||||
static InstrList<count2, TO>* connect_list(InstrList<count1, TO>* parent, InstrList<count2, TO>* child, int opcode)
|
||||
{
|
||||
parent->set_instr(opcode, child);
|
||||
child->set_parent(parent, opcode);
|
||||
return child;
|
||||
}
|
||||
|
||||
template<int count1, int count2, typename TO>
|
||||
static InstrList<count2, TO>* connect_list(InstrList<count1, TO>* parent, InstrList<count2, TO>* child)
|
||||
{
|
||||
parent->set_error_func(child);
|
||||
child->set_parent(parent->get_parent(), parent->get_opcode());
|
||||
return child;
|
||||
}
|
||||
|
||||
template<typename TO, int opcode, int count>
|
||||
class Instr0 : public InstrBase<TO>
|
||||
{
|
||||
InstrList<count, TO>& m_list;
|
||||
|
||||
public:
|
||||
Instr0(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)())
|
||||
: InstrBase<TO>(name, opcode, 0)
|
||||
, m_list(*list)
|
||||
{
|
||||
m_list.set_instr(opcode, instr_bind(func), this);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const
|
||||
{
|
||||
m_list.decode(op, opcode, code);
|
||||
}
|
||||
|
||||
virtual u32 encode(const std::vector<u32>& args) const
|
||||
{
|
||||
assert(args.size() == InstrBase<TO>::m_args_count);
|
||||
return m_list.encode(opcode);
|
||||
}
|
||||
|
||||
u32 encode() const
|
||||
{
|
||||
return m_list.encode(opcode);
|
||||
}
|
||||
|
||||
u32 operator()() const
|
||||
{
|
||||
return encode();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, int opcode, int count, typename T1>
|
||||
class Instr1 : public InstrBase<TO>
|
||||
{
|
||||
InstrList<count, TO>& m_list;
|
||||
|
||||
public:
|
||||
Instr1(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1),
|
||||
CodeFieldBase& arg_1)
|
||||
: InstrBase<TO>(name, opcode, 1)
|
||||
, m_list(*list)
|
||||
{
|
||||
InstrBase<TO>::m_args[0] = &arg_1;
|
||||
|
||||
m_list.set_instr(opcode, instr_bind(func, arg_1), this);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const
|
||||
{
|
||||
m_list.decode(op, opcode, code);
|
||||
}
|
||||
|
||||
virtual u32 encode(const std::vector<u32>& args) const
|
||||
{
|
||||
assert(args.size() == InstrBase<TO>::m_args_count);
|
||||
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[args[0]];
|
||||
}
|
||||
|
||||
u32 encode(T1 a1) const
|
||||
{
|
||||
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[a1];
|
||||
}
|
||||
|
||||
u32 operator()(T1 a1) const
|
||||
{
|
||||
return encode(a1);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, int opcode, int count, typename T1, typename T2>
|
||||
class Instr2 : public InstrBase<TO>
|
||||
{
|
||||
InstrList<count, TO>& m_list;
|
||||
|
||||
public:
|
||||
Instr2(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2)
|
||||
: InstrBase<TO>(name, opcode, 2)
|
||||
, m_list(*list)
|
||||
{
|
||||
InstrBase<TO>::m_args[0] = &arg_1;
|
||||
InstrBase<TO>::m_args[1] = &arg_2;
|
||||
|
||||
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2), this);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const
|
||||
{
|
||||
m_list.decode(op, opcode, code);
|
||||
}
|
||||
|
||||
virtual u32 encode(const std::vector<u32>& args) const
|
||||
{
|
||||
assert(args.size() == InstrBase<TO>::m_args_count);
|
||||
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[args[0]] | (*InstrBase<TO>::m_args[1])[args[1]];
|
||||
}
|
||||
|
||||
u32 encode(T1 a1, T2 a2) const
|
||||
{
|
||||
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[a1] | (*InstrBase<TO>::m_args[1])[a2];
|
||||
}
|
||||
|
||||
u32 operator()(T1 a1, T2 a2) const
|
||||
{
|
||||
return encode(a1, a2);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3>
|
||||
class Instr3 : public InstrBase<TO>
|
||||
{
|
||||
InstrList<count, TO>& m_list;
|
||||
|
||||
public:
|
||||
Instr3(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3)
|
||||
: InstrBase<TO>(name, opcode, 3)
|
||||
, m_list(*list)
|
||||
{
|
||||
InstrBase<TO>::m_args[0] = &arg_1;
|
||||
InstrBase<TO>::m_args[1] = &arg_2;
|
||||
InstrBase<TO>::m_args[2] = &arg_3;
|
||||
|
||||
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3), this);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const
|
||||
{
|
||||
m_list.decode(op, opcode, code);
|
||||
}
|
||||
|
||||
virtual u32 encode(const std::vector<u32>& args) const
|
||||
{
|
||||
assert(args.size() == InstrBase<TO>::m_args_count);
|
||||
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[args[0]] | (*InstrBase<TO>::m_args[1])[args[1]] | (*InstrBase<TO>::m_args[2])[args[2]];
|
||||
}
|
||||
|
||||
u32 encode(T1 a1, T2 a2, T3 a3) const
|
||||
{
|
||||
return m_list.encode(opcode) | (*InstrBase<TO>::m_args[0])[a1] | (*InstrBase<TO>::m_args[1])[a2] | (*InstrBase<TO>::m_args[2])[a3];
|
||||
}
|
||||
|
||||
u32 operator()(T1 a1, T2 a2, T3 a3) const
|
||||
{
|
||||
return encode(a1, a2, a3);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3, typename T4>
|
||||
class Instr4 : public InstrBase<TO>
|
||||
{
|
||||
InstrList<count, TO>& m_list;
|
||||
|
||||
public:
|
||||
Instr4(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3, T4),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3,
|
||||
CodeFieldBase& arg_4)
|
||||
: InstrBase<TO>(name, opcode, 4)
|
||||
, m_list(*list)
|
||||
{
|
||||
InstrBase<TO>::m_args[0] = &arg_1;
|
||||
InstrBase<TO>::m_args[1] = &arg_2;
|
||||
InstrBase<TO>::m_args[2] = &arg_3;
|
||||
InstrBase<TO>::m_args[3] = &arg_4;
|
||||
|
||||
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3, arg_4), this);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const
|
||||
{
|
||||
m_list.decode(op, opcode, code);
|
||||
}
|
||||
|
||||
virtual u32 encode(const std::vector<u32>& args) const
|
||||
{
|
||||
assert(args.size() == InstrBase<TO>::m_args_count);
|
||||
return m_list.encode(opcode) |
|
||||
(*InstrBase<TO>::m_args[0])[args[0]] |
|
||||
(*InstrBase<TO>::m_args[1])[args[1]] |
|
||||
(*InstrBase<TO>::m_args[2])[args[2]] |
|
||||
(*InstrBase<TO>::m_args[3])[args[3]];
|
||||
}
|
||||
|
||||
u32 encode(T1 a1, T2 a2, T3 a3, T4 a4) const
|
||||
{
|
||||
return m_list.encode(opcode) |
|
||||
(*InstrBase<TO>::m_args[0])[a1] |
|
||||
(*InstrBase<TO>::m_args[1])[a2] |
|
||||
(*InstrBase<TO>::m_args[2])[a3] |
|
||||
(*InstrBase<TO>::m_args[3])[a4];
|
||||
}
|
||||
|
||||
u32 operator()(T1 a1, T2 a2, T3 a3, T4 a4) const
|
||||
{
|
||||
return encode(a1, a2, a3, a4);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
class Instr5 : public InstrBase<TO>
|
||||
{
|
||||
InstrList<count, TO>& m_list;
|
||||
|
||||
public:
|
||||
Instr5(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3, T4, T5),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3,
|
||||
CodeFieldBase& arg_4,
|
||||
CodeFieldBase& arg_5)
|
||||
: InstrBase<TO>(name, opcode, 5)
|
||||
, m_list(*list)
|
||||
{
|
||||
InstrBase<TO>::m_args[0] = &arg_1;
|
||||
InstrBase<TO>::m_args[1] = &arg_2;
|
||||
InstrBase<TO>::m_args[2] = &arg_3;
|
||||
InstrBase<TO>::m_args[3] = &arg_4;
|
||||
InstrBase<TO>::m_args[4] = &arg_5;
|
||||
|
||||
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3, arg_4, arg_5), this);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const
|
||||
{
|
||||
m_list.decode(op, opcode, code);
|
||||
}
|
||||
|
||||
virtual u32 encode(const std::vector<u32>& args) const
|
||||
{
|
||||
assert(args.size() == InstrBase<TO>::m_args_count);
|
||||
return m_list.encode(opcode) |
|
||||
(*InstrBase<TO>::m_args[0])[args[0]] |
|
||||
(*InstrBase<TO>::m_args[1])[args[1]] |
|
||||
(*InstrBase<TO>::m_args[2])[args[2]] |
|
||||
(*InstrBase<TO>::m_args[3])[args[3]] |
|
||||
(*InstrBase<TO>::m_args[4])[args[4]];
|
||||
}
|
||||
|
||||
u32 encode(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) const
|
||||
{
|
||||
return m_list.encode(opcode) |
|
||||
(*InstrBase<TO>::m_args[0])[a1] |
|
||||
(*InstrBase<TO>::m_args[1])[a2] |
|
||||
(*InstrBase<TO>::m_args[2])[a3] |
|
||||
(*InstrBase<TO>::m_args[3])[a4] |
|
||||
(*InstrBase<TO>::m_args[4])[a5];
|
||||
}
|
||||
|
||||
u32 operator()(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) const
|
||||
{
|
||||
return encode(a1, a2, a3, a4, a5);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TO, int opcode, int count, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
class Instr6 : public InstrBase<TO>
|
||||
{
|
||||
InstrList<count, TO>& m_list;
|
||||
|
||||
public:
|
||||
Instr6(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3, T4, T5, T6),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3,
|
||||
CodeFieldBase& arg_4,
|
||||
CodeFieldBase& arg_5,
|
||||
CodeFieldBase& arg_6)
|
||||
: InstrBase<TO>(name, opcode, 6)
|
||||
, m_list(*list)
|
||||
{
|
||||
InstrBase<TO>::m_args[0] = &arg_1;
|
||||
InstrBase<TO>::m_args[1] = &arg_2;
|
||||
InstrBase<TO>::m_args[2] = &arg_3;
|
||||
InstrBase<TO>::m_args[3] = &arg_4;
|
||||
InstrBase<TO>::m_args[4] = &arg_5;
|
||||
InstrBase<TO>::m_args[5] = &arg_6;
|
||||
|
||||
m_list.set_instr(opcode, instr_bind(func, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6), this);
|
||||
}
|
||||
|
||||
virtual void decode(TO* op, u32 code) const
|
||||
{
|
||||
m_list.decode(op, opcode, code);
|
||||
}
|
||||
|
||||
virtual u32 encode(const std::vector<u32>& args) const
|
||||
{
|
||||
assert(args.size() == InstrBase<TO>::m_args_count);
|
||||
return m_list.encode(opcode) |
|
||||
(*InstrBase<TO>::m_args[0])[args[0]] |
|
||||
(*InstrBase<TO>::m_args[1])[args[1]] |
|
||||
(*InstrBase<TO>::m_args[2])[args[2]] |
|
||||
(*InstrBase<TO>::m_args[3])[args[3]] |
|
||||
(*InstrBase<TO>::m_args[4])[args[4]] |
|
||||
(*InstrBase<TO>::m_args[5])[args[5]];
|
||||
}
|
||||
|
||||
u32 encode(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T5 a6) const
|
||||
{
|
||||
return m_list.encode(opcode) |
|
||||
(*InstrBase<TO>::m_args[0])[a1] |
|
||||
(*InstrBase<TO>::m_args[1])[a2] |
|
||||
(*InstrBase<TO>::m_args[2])[a3] |
|
||||
(*InstrBase<TO>::m_args[3])[a4] |
|
||||
(*InstrBase<TO>::m_args[4])[a5] |
|
||||
(*InstrBase<TO>::m_args[5])[a6];
|
||||
}
|
||||
|
||||
u32 operator()(T1 a1, T2 a2, T3 a3, T4 a4, T4 a5, T4 a6) const
|
||||
{
|
||||
return encode(a1, a2, a3, a4, a5, a6);
|
||||
}
|
||||
};
|
||||
|
||||
template<int opcode, typename TO, int count>
|
||||
static Instr0<TO, opcode, count>& make_instr(InstrList<count, TO>* list, const std::string& name, void (TO::*func)())
|
||||
{
|
||||
return *new Instr0<TO, opcode, count>(list, name, func);
|
||||
}
|
||||
|
||||
template<int opcode, typename TO, int count, typename T1>
|
||||
static Instr1<TO, opcode, count, T1>& make_instr(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1),
|
||||
CodeFieldBase& arg_1)
|
||||
{
|
||||
return *new Instr1<TO, opcode, count, T1>(list, name, func, arg_1);
|
||||
}
|
||||
|
||||
template<int opcode, typename TO, int count, typename T1, typename T2>
|
||||
static Instr2<TO, opcode, count, T1, T2>& make_instr(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2)
|
||||
{
|
||||
return *new Instr2<TO, opcode, count, T1, T2>(list, name, func, arg_1, arg_2);
|
||||
}
|
||||
|
||||
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3>
|
||||
static Instr3<TO, opcode, count, T1, T2, T3>& make_instr(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3)
|
||||
{
|
||||
return *new Instr3<TO, opcode, count, T1, T2, T3>(list, name, func, arg_1, arg_2, arg_3);
|
||||
}
|
||||
|
||||
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3, typename T4>
|
||||
static Instr4<TO, opcode, count, T1, T2, T3, T4>& make_instr(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3, T4),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3,
|
||||
CodeFieldBase& arg_4)
|
||||
{
|
||||
return *new Instr4<TO, opcode, count, T1, T2, T3, T4>(list, name, func, arg_1, arg_2, arg_3, arg_4);
|
||||
}
|
||||
|
||||
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3, typename T4, typename T5>
|
||||
static Instr5<TO, opcode, count, T1, T2, T3, T4, T5>& make_instr(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3, T4, T5),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3,
|
||||
CodeFieldBase& arg_4,
|
||||
CodeFieldBase& arg_5)
|
||||
{
|
||||
return *new Instr5<TO, opcode, count, T1, T2, T3, T4, T5>(list, name, func, arg_1, arg_2, arg_3, arg_4, arg_5);
|
||||
}
|
||||
|
||||
template<int opcode, typename TO, int count, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
||||
static Instr6<TO, opcode, count, T1, T2, T3, T4, T5, T6>& make_instr(InstrList<count, TO>* list, const std::string& name,
|
||||
void (TO::*func)(T1, T2, T3, T4, T5, T6),
|
||||
CodeFieldBase& arg_1,
|
||||
CodeFieldBase& arg_2,
|
||||
CodeFieldBase& arg_3,
|
||||
CodeFieldBase& arg_4,
|
||||
CodeFieldBase& arg_5,
|
||||
CodeFieldBase& arg_6)
|
||||
{
|
||||
return *new Instr6<TO, opcode, count, T1, T2, T3, T4, T5, T6>(list, name, func, arg_1, arg_2, arg_3, arg_4, arg_5, arg_6);
|
||||
}
|
|
@ -51,11 +51,14 @@ protected:
|
|||
{
|
||||
}
|
||||
|
||||
virtual u32 DisAsmBranchTarget(const s32 imm)=0;
|
||||
virtual u32 DisAsmBranchTarget(const s32 imm) = 0;
|
||||
|
||||
std::string FixOp(std::string op)
|
||||
{
|
||||
op.append(std::max<int>(10 - (int)op.length(), 0),' ');
|
||||
op.resize(std::max<std::size_t>(op.length(), 10), ' ');
|
||||
return op;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual u32 disasm(u32 pc) = 0;
|
||||
};
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
template<uint size, typename T> force_inline static T sign(const T value)
|
||||
{
|
||||
static_assert(size > 0 && size < sizeof(T) * 8, "Bad sign size");
|
||||
|
||||
if(value & (T(1) << (size - 1)))
|
||||
{
|
||||
return value - (T(1) << size);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
class CodeFieldBase
|
||||
{
|
||||
public:
|
||||
u32 m_type;
|
||||
|
||||
public:
|
||||
CodeFieldBase(u32 type) : m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
virtual u32 operator ()(u32 data) const=0;
|
||||
virtual void operator()(u32& data, u32 value) const=0;
|
||||
|
||||
virtual u32 operator[](u32 value) const
|
||||
{
|
||||
u32 result = 0;
|
||||
(*this)(result, value);
|
||||
return result;
|
||||
}
|
||||
};
|
|
@ -1,14 +1,16 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "CPUDecoder.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Cell/RawSPUThread.h"
|
||||
#include "Emu/ARMv7/ARMv7Thread.h"
|
||||
#include "CPUThread.h"
|
||||
|
||||
thread_local CPUThread* g_tls_current_cpu_thread = nullptr;
|
||||
thread_local cpu_thread* g_tls_current_cpu_thread = nullptr;
|
||||
|
||||
void CPUThread::on_task()
|
||||
void cpu_thread::on_task()
|
||||
{
|
||||
g_tls_current_cpu_thread = this;
|
||||
|
||||
|
@ -16,13 +18,13 @@ void CPUThread::on_task()
|
|||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
|
||||
// check thread status
|
||||
while (is_alive())
|
||||
// Check thread status
|
||||
while (!(state & cpu_state::exit))
|
||||
{
|
||||
CHECK_EMU_STATUS;
|
||||
|
||||
// check stop status
|
||||
if (!is_stopped())
|
||||
if (!(state & cpu_state::stop))
|
||||
{
|
||||
if (lock) lock.unlock();
|
||||
|
||||
|
@ -30,26 +32,17 @@ void CPUThread::on_task()
|
|||
{
|
||||
cpu_task();
|
||||
}
|
||||
catch (CPUThreadReturn)
|
||||
catch (cpu_state _s)
|
||||
{
|
||||
;
|
||||
state += _s;
|
||||
}
|
||||
catch (CPUThreadStop)
|
||||
catch (const std::exception&)
|
||||
{
|
||||
m_state |= CPU_STATE_STOPPED;
|
||||
}
|
||||
catch (CPUThreadExit)
|
||||
{
|
||||
m_state |= CPU_STATE_DEAD;
|
||||
break;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
dump_info();
|
||||
LOG_NOTICE(GENERAL, "\n%s", dump());
|
||||
throw;
|
||||
}
|
||||
|
||||
m_state &= ~CPU_STATE_RETURN;
|
||||
state -= cpu_state::ret;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -63,201 +56,7 @@ void CPUThread::on_task()
|
|||
}
|
||||
}
|
||||
|
||||
CPUThread::CPUThread(CPUThreadType type, const std::string& name)
|
||||
: m_id(idm::get_last_id())
|
||||
, m_type(type)
|
||||
, m_name(name)
|
||||
{
|
||||
}
|
||||
|
||||
CPUThread::~CPUThread()
|
||||
{
|
||||
Emu.SendDbgCommand(DID_REMOVE_THREAD, this);
|
||||
}
|
||||
|
||||
std::string CPUThread::get_name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool CPUThread::is_paused() const
|
||||
{
|
||||
return (m_state & CPU_STATE_PAUSED) != 0 || Emu.IsPaused();
|
||||
}
|
||||
|
||||
void CPUThread::dump_info() const
|
||||
{
|
||||
if (!Emu.IsStopped())
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "%s", RegsToString());
|
||||
}
|
||||
}
|
||||
|
||||
void CPUThread::run()
|
||||
{
|
||||
Emu.SendDbgCommand(DID_START_THREAD, this);
|
||||
|
||||
init_stack();
|
||||
init_regs();
|
||||
do_run();
|
||||
|
||||
Emu.SendDbgCommand(DID_STARTED_THREAD, this);
|
||||
}
|
||||
|
||||
void CPUThread::pause()
|
||||
{
|
||||
Emu.SendDbgCommand(DID_PAUSE_THREAD, this);
|
||||
|
||||
m_state |= CPU_STATE_PAUSED;
|
||||
|
||||
Emu.SendDbgCommand(DID_PAUSED_THREAD, this);
|
||||
}
|
||||
|
||||
void CPUThread::resume()
|
||||
{
|
||||
Emu.SendDbgCommand(DID_RESUME_THREAD, this);
|
||||
|
||||
{
|
||||
// lock for reliable notification
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
m_state &= ~CPU_STATE_PAUSED;
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
Emu.SendDbgCommand(DID_RESUMED_THREAD, this);
|
||||
}
|
||||
|
||||
void CPUThread::stop()
|
||||
{
|
||||
Emu.SendDbgCommand(DID_STOP_THREAD, this);
|
||||
|
||||
if (is_current())
|
||||
{
|
||||
throw CPUThreadStop{};
|
||||
}
|
||||
else
|
||||
{
|
||||
// lock for reliable notification
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
m_state |= CPU_STATE_STOPPED;
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
Emu.SendDbgCommand(DID_STOPED_THREAD, this);
|
||||
}
|
||||
|
||||
void CPUThread::exec()
|
||||
{
|
||||
Emu.SendDbgCommand(DID_EXEC_THREAD, this);
|
||||
|
||||
m_state &= ~CPU_STATE_STOPPED;
|
||||
|
||||
{
|
||||
// lock for reliable notification
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void CPUThread::exit()
|
||||
{
|
||||
m_state |= CPU_STATE_DEAD;
|
||||
|
||||
if (!is_current())
|
||||
{
|
||||
// lock for reliable notification
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void CPUThread::step()
|
||||
{
|
||||
if (m_state.atomic_op([](u64& state) -> bool
|
||||
{
|
||||
const bool was_paused = (state & CPU_STATE_PAUSED) != 0;
|
||||
|
||||
state |= CPU_STATE_STEP;
|
||||
state &= ~CPU_STATE_PAUSED;
|
||||
|
||||
return was_paused;
|
||||
}))
|
||||
{
|
||||
if (is_current()) return;
|
||||
|
||||
// lock for reliable notification (only if PAUSE was removed)
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void CPUThread::sleep()
|
||||
{
|
||||
m_state += CPU_STATE_MAX;
|
||||
m_state |= CPU_STATE_SLEEP;
|
||||
}
|
||||
|
||||
void CPUThread::awake()
|
||||
{
|
||||
// must be called after the balanced sleep() call
|
||||
|
||||
if (m_state.atomic_op([](u64& state) -> bool
|
||||
{
|
||||
if (state < CPU_STATE_MAX)
|
||||
{
|
||||
throw EXCEPTION("sleep()/awake() inconsistency");
|
||||
}
|
||||
|
||||
if ((state -= CPU_STATE_MAX) < CPU_STATE_MAX)
|
||||
{
|
||||
state &= ~CPU_STATE_SLEEP;
|
||||
|
||||
// notify the condition variable as well
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
if (is_current()) return;
|
||||
|
||||
// lock for reliable notification; the condition being checked is probably externally set
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
bool CPUThread::signal()
|
||||
{
|
||||
// try to set SIGNAL
|
||||
if (m_state._or(CPU_STATE_SIGNAL) & CPU_STATE_SIGNAL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not truly responsible for signal delivery, requires additional measures like LV2_LOCK
|
||||
cv.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CPUThread::unsignal()
|
||||
{
|
||||
// remove SIGNAL and return its old value
|
||||
return (m_state._and_not(CPU_STATE_SIGNAL) & CPU_STATE_SIGNAL) != 0;
|
||||
}
|
||||
|
||||
bool CPUThread::check_status()
|
||||
bool cpu_thread::check_status()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex, std::defer_lock);
|
||||
|
||||
|
@ -265,12 +64,12 @@ bool CPUThread::check_status()
|
|||
{
|
||||
CHECK_EMU_STATUS; // check at least once
|
||||
|
||||
if (!is_alive())
|
||||
if (state & cpu_state::exit)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!is_paused() && (m_state & CPU_STATE_INTR) == 0)
|
||||
if (!state.test(cpu_state_pause) && !state.test(cpu_state::interrupt))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -281,7 +80,7 @@ bool CPUThread::check_status()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!is_paused() && (m_state & CPU_STATE_INTR) != 0 && handle_interrupt())
|
||||
if (!state.test(cpu_state_pause) && state & cpu_state::interrupt && handle_interrupt())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -289,17 +88,45 @@ bool CPUThread::check_status()
|
|||
cv.wait(lock);
|
||||
}
|
||||
|
||||
if (m_state & CPU_STATE_RETURN || is_stopped())
|
||||
const auto state_ = state.load();
|
||||
|
||||
if (state_ & to_mset(cpu_state::ret, cpu_state::stop))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_state & CPU_STATE_STEP)
|
||||
if (state_ & cpu_state::dbg_step)
|
||||
{
|
||||
// set PAUSE, but allow to execute once
|
||||
m_state |= CPU_STATE_PAUSED;
|
||||
m_state &= ~CPU_STATE_STEP;
|
||||
state += cpu_state::dbg_pause;
|
||||
state -= cpu_state::dbg_step;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<cpu_thread>> get_all_cpu_threads()
|
||||
{
|
||||
std::vector<std::shared_ptr<cpu_thread>> result;
|
||||
|
||||
for (auto& t : idm::get_all<PPUThread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
for (auto& t : idm::get_all<SPUThread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
for (auto& t : idm::get_all<RawSPUThread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
for (auto& t : idm::get_all<ARMv7Thread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -2,174 +2,97 @@
|
|||
|
||||
#include "Utilities/Thread.h"
|
||||
|
||||
enum CPUThreadType
|
||||
// CPU Thread Type
|
||||
enum class cpu_type : u32
|
||||
{
|
||||
CPU_THREAD_PPU,
|
||||
CPU_THREAD_SPU,
|
||||
CPU_THREAD_RAW_SPU,
|
||||
CPU_THREAD_ARMv7,
|
||||
ppu, // PPU Thread
|
||||
spu, // SPU Thread
|
||||
arm, // ARMv7 Thread
|
||||
};
|
||||
|
||||
// CPU Thread State Flags
|
||||
enum : u64
|
||||
// CPU Thread State flags
|
||||
enum struct cpu_state : u32
|
||||
{
|
||||
CPU_STATE_STOPPED = (1ull << 0), // basic execution state (stopped by default), removed by Exec()
|
||||
CPU_STATE_PAUSED = (1ull << 1), // pauses thread execution, set by the debugger (manually or after step execution)
|
||||
CPU_STATE_SLEEP = (1ull << 2), // shouldn't affect thread execution, set by sleep(), removed by the latest awake(), may possibly indicate waiting state of the thread
|
||||
CPU_STATE_STEP = (1ull << 3), // forces the thread to pause after executing just one instruction or something appropriate, set by the debugger
|
||||
CPU_STATE_DEAD = (1ull << 4), // indicates irreversible exit of the thread
|
||||
CPU_STATE_RETURN = (1ull << 5), // used for callback return
|
||||
CPU_STATE_SIGNAL = (1ull << 6), // used for HLE signaling
|
||||
CPU_STATE_INTR = (1ull << 7), // thread interrupted
|
||||
stop, // Thread not running (HLE, initial state)
|
||||
exit, // Irreversible exit
|
||||
suspend, // Thread paused
|
||||
ret, // Callback return requested
|
||||
signal, // Thread received a signal (HLE)
|
||||
interrupt, // Thread interrupted
|
||||
|
||||
CPU_STATE_MAX = (1ull << 8), // added to (subtracted from) m_state by sleep()/awake() calls to trigger status check
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_global_stop, // Emulation stopped
|
||||
dbg_pause, // Thread paused
|
||||
dbg_step, // Thread forced to pause after one step (one instruction, etc)
|
||||
};
|
||||
|
||||
class CPUThreadReturn {}; // "HLE return" exception event
|
||||
class CPUThreadStop {}; // CPUThread::Stop exception event
|
||||
class CPUThreadExit {}; // CPUThread::Exit exception event
|
||||
// CPU Thread State flags: pause state union
|
||||
constexpr mset<cpu_state> cpu_state_pause = to_mset(cpu_state::suspend, cpu_state::dbg_global_pause, cpu_state::dbg_pause);
|
||||
|
||||
class CPUDecoder;
|
||||
|
||||
class CPUThread : public named_thread_t
|
||||
class cpu_thread : public named_thread
|
||||
{
|
||||
void on_task() override;
|
||||
void on_id_aux_finalize() override { exit(); } // call exit() instead of join()
|
||||
|
||||
protected:
|
||||
atomic_t<u64> m_state{ CPU_STATE_STOPPED }; // thread state flags
|
||||
|
||||
std::unique_ptr<CPUDecoder> m_dec;
|
||||
|
||||
const u32 m_id;
|
||||
const CPUThreadType m_type;
|
||||
const std::string m_name; // changing m_name is unsafe because it can be read at any moment
|
||||
|
||||
CPUThread(CPUThreadType type, const std::string& name);
|
||||
|
||||
public:
|
||||
virtual ~CPUThread() override;
|
||||
virtual void on_init() override
|
||||
{
|
||||
named_thread::on_init();
|
||||
}
|
||||
|
||||
virtual std::string get_name() const override;
|
||||
u32 get_id() const { return m_id; }
|
||||
CPUThreadType get_type() const { return m_type; }
|
||||
virtual void on_stop() override
|
||||
{
|
||||
state += cpu_state::exit;
|
||||
safe_notify();
|
||||
}
|
||||
|
||||
bool is_alive() const { return (m_state & CPU_STATE_DEAD) == 0; }
|
||||
bool is_stopped() const { return (m_state & CPU_STATE_STOPPED) != 0; }
|
||||
virtual bool is_paused() const;
|
||||
const std::string name;
|
||||
const u32 id{};
|
||||
const cpu_type type;
|
||||
|
||||
virtual void dump_info() const;
|
||||
virtual u32 get_pc() const = 0;
|
||||
virtual u32 get_offset() const = 0;
|
||||
virtual void do_run() = 0;
|
||||
virtual void cpu_task() = 0;
|
||||
cpu_thread(cpu_type type, const std::string& name)
|
||||
: type(type)
|
||||
, name(name)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void init_regs() = 0;
|
||||
virtual void init_stack() = 0;
|
||||
virtual void close_stack() = 0;
|
||||
// Public thread state
|
||||
atomic_t<mset<cpu_state>> state{ cpu_state::stop };
|
||||
|
||||
// initialize thread
|
||||
void run();
|
||||
// Recursively enter sleep state
|
||||
void sleep()
|
||||
{
|
||||
if (!++m_sleep) xsleep();
|
||||
}
|
||||
|
||||
// called by the debugger, don't use
|
||||
void pause();
|
||||
// Leave sleep state
|
||||
void awake()
|
||||
{
|
||||
if (!m_sleep--) xsleep();
|
||||
}
|
||||
|
||||
// called by the debugger, don't use
|
||||
void resume();
|
||||
|
||||
// stop thread execution
|
||||
void stop();
|
||||
|
||||
// start thread execution (removing STOP status)
|
||||
void exec();
|
||||
|
||||
// exit thread execution
|
||||
void exit();
|
||||
|
||||
// called by the debugger, don't use
|
||||
void step();
|
||||
|
||||
// trigger thread status check
|
||||
void sleep();
|
||||
|
||||
// untrigger thread status check
|
||||
void awake();
|
||||
|
||||
// set SIGNAL and notify (returns true if set)
|
||||
bool signal();
|
||||
|
||||
// test SIGNAL and reset
|
||||
bool unsignal();
|
||||
|
||||
// process m_state flags, returns true if the checker must return
|
||||
// Process thread state, return true if the checker must return
|
||||
bool check_status();
|
||||
|
||||
virtual std::string dump() const = 0; // Print CPU state
|
||||
virtual void cpu_init() {}
|
||||
virtual void cpu_task() = 0;
|
||||
virtual bool handle_interrupt() { return false; }
|
||||
|
||||
std::string GetFName() const
|
||||
private:
|
||||
[[noreturn]] void xsleep()
|
||||
{
|
||||
return fmt::format("%s[0x%x] Thread (%s)", GetTypeString(), m_id, m_name);
|
||||
throw std::runtime_error("cpu_thread: sleep()/awake() inconsistency");
|
||||
}
|
||||
|
||||
static const char* CPUThreadTypeToString(CPUThreadType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case CPU_THREAD_PPU: return "PPU";
|
||||
case CPU_THREAD_SPU: return "SPU";
|
||||
case CPU_THREAD_RAW_SPU: return "RawSPU";
|
||||
case CPU_THREAD_ARMv7: return "ARMv7";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const char* ThreadStatusToString() const
|
||||
{
|
||||
// TODO
|
||||
|
||||
//switch (ThreadStatus())
|
||||
//{
|
||||
//case CPUThread_Ready: return "Ready";
|
||||
//case CPUThread_Running: return "Running";
|
||||
//case CPUThread_Paused: return "Paused";
|
||||
//case CPUThread_Stopped: return "Stopped";
|
||||
//case CPUThread_Sleeping: return "Sleeping";
|
||||
//case CPUThread_Break: return "Break";
|
||||
//case CPUThread_Step: return "Step";
|
||||
//}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const char* GetTypeString() const
|
||||
{
|
||||
return CPUThreadTypeToString(m_type);
|
||||
}
|
||||
|
||||
CPUDecoder* GetDecoder()
|
||||
{
|
||||
return m_dec.get();
|
||||
};
|
||||
|
||||
virtual std::string RegsToString() const = 0;
|
||||
virtual std::string ReadRegString(const std::string& reg) const = 0;
|
||||
virtual bool WriteRegString(const std::string& reg, std::string value) = 0;
|
||||
// Sleep/Awake counter
|
||||
atomic_t<u32> m_sleep{};
|
||||
};
|
||||
|
||||
inline CPUThread* get_current_cpu_thread()
|
||||
inline cpu_thread* get_current_cpu_thread() noexcept
|
||||
{
|
||||
extern thread_local CPUThread* g_tls_current_cpu_thread;
|
||||
extern thread_local cpu_thread* g_tls_current_cpu_thread;
|
||||
|
||||
return g_tls_current_cpu_thread;
|
||||
}
|
||||
|
||||
class cpu_thread
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<CPUThread> thread;
|
||||
|
||||
public:
|
||||
virtual cpu_thread& args(std::initializer_list<std::string> values) = 0;
|
||||
virtual cpu_thread& run() = 0;
|
||||
};
|
||||
extern std::vector<std::shared_ptr<cpu_thread>> get_all_cpu_threads();
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Cell/RawSPUThread.h"
|
||||
#include "Emu/ARMv7/ARMv7Thread.h"
|
||||
#include "CPUThreadManager.h"
|
||||
|
||||
CPUThreadManager::CPUThreadManager()
|
||||
{
|
||||
}
|
||||
|
||||
CPUThreadManager::~CPUThreadManager()
|
||||
{
|
||||
}
|
||||
|
||||
void CPUThreadManager::Close()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
for (auto& x : m_raw_spu)
|
||||
{
|
||||
x.reset();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<CPUThread>> CPUThreadManager::GetAllThreads()
|
||||
{
|
||||
std::vector<std::shared_ptr<CPUThread>> result;
|
||||
|
||||
for (auto& t : idm::get_all<PPUThread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
for (auto& t : idm::get_all<SPUThread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
for (auto& t : idm::get_all<RawSPUThread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
for (auto& t : idm::get_all<ARMv7Thread>())
|
||||
{
|
||||
result.emplace_back(t);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void CPUThreadManager::Exec()
|
||||
{
|
||||
for (auto& t : idm::get_all<PPUThread>())
|
||||
{
|
||||
t->exec();
|
||||
}
|
||||
|
||||
for (auto& t : idm::get_all<ARMv7Thread>())
|
||||
{
|
||||
t->exec();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RawSPUThread> CPUThreadManager::NewRawSPUThread()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
std::shared_ptr<RawSPUThread> result;
|
||||
|
||||
for (u32 i = 0; i < m_raw_spu.size(); i++)
|
||||
{
|
||||
if (m_raw_spu[i].expired())
|
||||
{
|
||||
m_raw_spu[i] = result = idm::make_ptr<RawSPUThread>(std::to_string(i), i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<RawSPUThread> CPUThreadManager::GetRawSPUThread(u32 index)
|
||||
{
|
||||
if (index >= m_raw_spu.size())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
return m_raw_spu[index].lock();
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
class CPUThread;
|
||||
class RawSPUThread;
|
||||
|
||||
class CPUThreadManager final
|
||||
{
|
||||
std::mutex m_mutex;
|
||||
|
||||
std::array<std::weak_ptr<RawSPUThread>, 5> m_raw_spu;
|
||||
|
||||
public:
|
||||
CPUThreadManager();
|
||||
~CPUThreadManager();
|
||||
|
||||
void Close();
|
||||
|
||||
static std::vector<std::shared_ptr<CPUThread>> GetAllThreads();
|
||||
|
||||
static void Exec();
|
||||
|
||||
std::shared_ptr<RawSPUThread> NewRawSPUThread();
|
||||
|
||||
std::shared_ptr<RawSPUThread> GetRawSPUThread(u32 index);
|
||||
};
|
|
@ -1,54 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "Emu/SysCalls/lv2/sys_event.h"
|
||||
#include "Event.h"
|
||||
|
||||
void EventManager::Init()
|
||||
{
|
||||
}
|
||||
|
||||
void EventManager::Clear()
|
||||
{
|
||||
m_map.clear();
|
||||
}
|
||||
|
||||
bool EventManager::UnregisterKey(u64 key)
|
||||
{
|
||||
if (!key)
|
||||
{
|
||||
// always ok
|
||||
return true;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
auto f = m_map.find(key);
|
||||
if (f != m_map.end())
|
||||
{
|
||||
m_map.erase(f);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<lv2_event_queue_t> EventManager::GetEventQueue(u64 key)
|
||||
{
|
||||
if (!key)
|
||||
{
|
||||
// never exists
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
auto f = m_map.find(key);
|
||||
if (f != m_map.end())
|
||||
{
|
||||
return f->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/IdManager.h"
|
||||
|
||||
struct lv2_event_queue_t;
|
||||
|
||||
class EventManager
|
||||
{
|
||||
std::mutex m_mutex;
|
||||
std::unordered_map<u64, std::shared_ptr<lv2_event_queue_t>> m_map;
|
||||
|
||||
public:
|
||||
void Init();
|
||||
void Clear();
|
||||
bool UnregisterKey(u64 key);
|
||||
|
||||
template<typename... Args, typename = std::enable_if_t<std::is_constructible<lv2_event_queue_t, Args...>::value>> std::shared_ptr<lv2_event_queue_t> MakeEventQueue(u64 key, Args&&... args)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
|
||||
if (key && m_map.find(key) != m_map.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto queue = idm::make_ptr<lv2_event_queue_t>(std::forward<Args>(args)...);
|
||||
|
||||
if (key)
|
||||
{
|
||||
m_map[key] = queue;
|
||||
}
|
||||
|
||||
return queue;
|
||||
}
|
||||
|
||||
std::shared_ptr<lv2_event_queue_t> GetEventQueue(u64 key);
|
||||
};
|
|
@ -1,820 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "HDD.h"
|
||||
|
||||
void vfsHDDManager::CreateBlock(vfsHDD_Block& block)
|
||||
{
|
||||
block.is_used = true;
|
||||
block.next_block = 0;
|
||||
}
|
||||
|
||||
void vfsHDDManager::CreateEntry(vfsHDD_Entry& entry)
|
||||
{
|
||||
memset(&entry, 0, sizeof(vfsHDD_Entry));
|
||||
u64 ctime = time(nullptr);
|
||||
entry.atime = ctime;
|
||||
entry.ctime = ctime;
|
||||
entry.mtime = ctime;
|
||||
entry.access = 0666;
|
||||
CreateBlock(entry);
|
||||
}
|
||||
|
||||
void vfsHDDManager::CreateHDD(const std::string& path, u64 size, u64 block_size)
|
||||
{
|
||||
fs::file f(path, fom::rewrite);
|
||||
|
||||
static const u64 cur_dir_block = 1;
|
||||
|
||||
vfsHDD_Hdr hdr;
|
||||
CreateBlock(hdr);
|
||||
hdr.next_block = cur_dir_block;
|
||||
hdr.magic = g_hdd_magic;
|
||||
hdr.version = g_hdd_version;
|
||||
hdr.block_count = (size + block_size) / block_size;
|
||||
hdr.block_size = block_size;
|
||||
f.write(&hdr, sizeof(vfsHDD_Hdr));
|
||||
|
||||
{
|
||||
vfsHDD_Entry entry;
|
||||
CreateEntry(entry);
|
||||
entry.type = vfsHDD_Entry_Dir;
|
||||
entry.data_block = hdr.next_block;
|
||||
entry.next_block = 0;
|
||||
|
||||
f.seek(cur_dir_block * hdr.block_size);
|
||||
f.write(&entry, sizeof(vfsHDD_Entry));
|
||||
f.write(".", 1);
|
||||
}
|
||||
|
||||
u8 null = 0;
|
||||
|
||||
CHECK_ASSERTION(f.seek(hdr.block_count * hdr.block_size - sizeof(null)) != -1);
|
||||
|
||||
f.write(&null, sizeof(null));
|
||||
}
|
||||
|
||||
void vfsHDDManager::Format()
|
||||
{
|
||||
}
|
||||
|
||||
void vfsHDDManager::AppendEntry(vfsHDD_Entry entry)
|
||||
{
|
||||
}
|
||||
|
||||
bool vfsHDDFile::goto_block(u64 n)
|
||||
{
|
||||
vfsHDD_Block block_info;
|
||||
|
||||
if (m_info.data_block >= m_hdd_info.block_count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(m_info.data_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
block_info.next_block = m_info.data_block;
|
||||
|
||||
for (u64 i = 0; i < n; ++i)
|
||||
{
|
||||
if (!block_info.next_block || !block_info.is_used || block_info.next_block >= m_hdd_info.block_count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(block_info.next_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Read(&block_info, sizeof(vfsHDD_Block));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vfsHDDFile::RemoveBlocks(u64 start_block)
|
||||
{
|
||||
vfsHDD_Block block_info;
|
||||
block_info.next_block = start_block;
|
||||
|
||||
while (block_info.next_block && block_info.is_used)
|
||||
{
|
||||
u64 offset = block_info.next_block * m_hdd_info.block_size;
|
||||
|
||||
ReadBlock(offset, block_info);
|
||||
WriteBlock(offset, g_null_block);
|
||||
}
|
||||
}
|
||||
|
||||
void vfsHDDFile::WriteBlock(u64 block, const vfsHDD_Block& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Write(&data, sizeof(vfsHDD_Block));
|
||||
}
|
||||
|
||||
void vfsHDDFile::ReadBlock(u64 block, vfsHDD_Block& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Read(&data, sizeof(vfsHDD_Block));
|
||||
}
|
||||
|
||||
void vfsHDDFile::WriteEntry(u64 block, const vfsHDD_Entry& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Write(&data, sizeof(vfsHDD_Entry));
|
||||
}
|
||||
|
||||
void vfsHDDFile::ReadEntry(u64 block, vfsHDD_Entry& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Read(&data, sizeof(vfsHDD_Entry));
|
||||
}
|
||||
|
||||
void vfsHDDFile::ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Read(&data, sizeof(vfsHDD_Entry));
|
||||
name.resize(GetMaxNameLen());
|
||||
m_hdd.Read(&name.front(), GetMaxNameLen());
|
||||
}
|
||||
|
||||
void vfsHDDFile::ReadEntry(u64 block, std::string& name)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size + sizeof(vfsHDD_Entry)) != -1);
|
||||
|
||||
name.resize(GetMaxNameLen());
|
||||
m_hdd.Read(&name.front(), GetMaxNameLen());
|
||||
}
|
||||
|
||||
void vfsHDDFile::WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Write(&data, sizeof(vfsHDD_Entry));
|
||||
m_hdd.Write(name.c_str(), std::min<size_t>(GetMaxNameLen() - 1, name.length() + 1));
|
||||
}
|
||||
|
||||
void vfsHDDFile::Open(u64 info_block)
|
||||
{
|
||||
m_info_block = info_block;
|
||||
ReadEntry(m_info_block, m_info);
|
||||
m_position = 0;
|
||||
m_cur_block = m_info.data_block;
|
||||
}
|
||||
|
||||
u64 vfsHDDFile::FindFreeBlock()
|
||||
{
|
||||
vfsHDD_Block block_info;
|
||||
|
||||
for (u64 i = 0; i < m_hdd_info.block_count; ++i)
|
||||
{
|
||||
ReadBlock(i, block_info);
|
||||
|
||||
if (!block_info.is_used)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool vfsHDDFile::Seek(u64 pos)
|
||||
{
|
||||
if (!goto_block(pos / m_hdd_info.block_size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
m_position = pos % m_hdd_info.block_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
void vfsHDDFile::SaveInfo()
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(m_info_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(m_info_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Write(&m_info, sizeof(vfsHDD_Entry));
|
||||
}
|
||||
|
||||
u64 vfsHDDFile::Read(void* dst, u64 size)
|
||||
{
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
//vfsDeviceLocker lock(m_hdd);
|
||||
|
||||
const u32 block_size = m_hdd_info.block_size - sizeof(vfsHDD_Block);
|
||||
u64 rsize = std::min<u64>(block_size - m_position, size);
|
||||
|
||||
vfsHDD_Block cur_block_info;
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Read(&cur_block_info, sizeof(vfsHDD_Block));
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size + sizeof(vfsHDD_Block) + m_position) != -1);
|
||||
|
||||
m_hdd.Read(dst, rsize);
|
||||
size -= rsize;
|
||||
m_position += rsize;
|
||||
if (!size)
|
||||
{
|
||||
return rsize;
|
||||
}
|
||||
|
||||
u64 offset = rsize;
|
||||
|
||||
for (; size; size -= rsize, offset += rsize)
|
||||
{
|
||||
if (!cur_block_info.is_used || !cur_block_info.next_block || cur_block_info.next_block >= m_hdd_info.block_count)
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
m_cur_block = cur_block_info.next_block;
|
||||
rsize = std::min<u64>(block_size, size);
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(cur_block_info.next_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd.Read(&cur_block_info, sizeof(vfsHDD_Block));
|
||||
|
||||
if (m_hdd.Read((u8*)dst + offset, rsize) != rsize)
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
m_position = rsize;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
u64 vfsHDDFile::Write(const void* src, u64 size)
|
||||
{
|
||||
if (!size)
|
||||
return 0;
|
||||
|
||||
//vfsDeviceLocker lock(m_hdd);
|
||||
|
||||
const u32 block_size = m_hdd_info.block_size - sizeof(vfsHDD_Block);
|
||||
|
||||
if (!m_cur_block)
|
||||
{
|
||||
if (!m_info.data_block)
|
||||
{
|
||||
u64 new_block = FindFreeBlock();
|
||||
|
||||
if (!new_block)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
WriteBlock(new_block, g_used_block);
|
||||
m_info.data_block = new_block;
|
||||
m_info.size = 0;
|
||||
SaveInfo();
|
||||
}
|
||||
|
||||
m_cur_block = m_info.data_block;
|
||||
m_position = 0;
|
||||
}
|
||||
|
||||
u64 wsize = std::min<u64>(block_size - m_position, size);
|
||||
|
||||
vfsHDD_Block block_info;
|
||||
ReadBlock(m_cur_block, block_info);
|
||||
|
||||
if (wsize)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size + sizeof(vfsHDD_Block) + m_position) != -1);
|
||||
|
||||
m_hdd.Write(src, wsize);
|
||||
size -= wsize;
|
||||
m_info.size += wsize;
|
||||
m_position += wsize;
|
||||
SaveInfo();
|
||||
|
||||
if (!size)
|
||||
return wsize;
|
||||
}
|
||||
|
||||
u64 last_block = m_cur_block;
|
||||
block_info.is_used = true;
|
||||
u64 offset = wsize;
|
||||
|
||||
for (; size; size -= wsize, offset += wsize, m_info.size += wsize)
|
||||
{
|
||||
u64 new_block = FindFreeBlock();
|
||||
|
||||
if (!new_block)
|
||||
{
|
||||
m_position = 0;
|
||||
SaveInfo();
|
||||
return offset;
|
||||
}
|
||||
|
||||
m_cur_block = new_block;
|
||||
wsize = std::min<u64>(block_size, size);
|
||||
|
||||
block_info.next_block = m_cur_block;
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(last_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
if (m_hdd.Write(&block_info, sizeof(vfsHDD_Block)) != sizeof(vfsHDD_Block))
|
||||
{
|
||||
m_position = 0;
|
||||
SaveInfo();
|
||||
return offset;
|
||||
}
|
||||
|
||||
block_info.next_block = 0;
|
||||
|
||||
CHECK_ASSERTION(m_hdd.Seek(m_cur_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
if (m_hdd.Write(&block_info, sizeof(vfsHDD_Block)) != sizeof(vfsHDD_Block))
|
||||
{
|
||||
m_position = 0;
|
||||
SaveInfo();
|
||||
return offset;
|
||||
}
|
||||
|
||||
if ((m_position = m_hdd.Write((u8*)src + offset, wsize)) != wsize)
|
||||
{
|
||||
m_info.size += wsize;
|
||||
SaveInfo();
|
||||
return offset;
|
||||
}
|
||||
|
||||
last_block = m_cur_block;
|
||||
}
|
||||
|
||||
SaveInfo();
|
||||
m_position = wsize;
|
||||
return offset;
|
||||
}
|
||||
|
||||
vfsDeviceHDD::vfsDeviceHDD(const std::string& hdd_path) : m_hdd_path(hdd_path)
|
||||
{
|
||||
}
|
||||
|
||||
vfsFileBase* vfsDeviceHDD::GetNewFileStream()
|
||||
{
|
||||
return new vfsHDD(this, m_hdd_path);
|
||||
}
|
||||
|
||||
vfsDirBase* vfsDeviceHDD::GetNewDirStream()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
vfsHDD::vfsHDD(vfsDevice* device, const std::string& hdd_path)
|
||||
: m_hdd_file(device)
|
||||
, m_file(m_hdd_file, m_hdd_info)
|
||||
, m_hdd_path(hdd_path)
|
||||
, vfsFileBase(device)
|
||||
{
|
||||
m_hdd_file.Open(hdd_path, fom::read | fom::write);
|
||||
m_hdd_file.Read(&m_hdd_info, sizeof(vfsHDD_Hdr));
|
||||
m_cur_dir_block = m_hdd_info.next_block;
|
||||
if (!m_hdd_info.block_size)
|
||||
{
|
||||
LOG_ERROR(HLE, "Bad block size!");
|
||||
m_hdd_info.block_size = 2048;
|
||||
}
|
||||
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(m_cur_dir_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd_file.Read(&m_cur_dir, sizeof(vfsHDD_Entry));
|
||||
}
|
||||
|
||||
bool vfsHDD::SearchEntry(const std::string& name, u64& entry_block, u64* parent_block)
|
||||
{
|
||||
u64 last_block = 0;
|
||||
u64 block = m_cur_dir_block;
|
||||
vfsHDD_Entry entry;
|
||||
std::string buf;
|
||||
|
||||
while (block)
|
||||
{
|
||||
ReadEntry(block, entry, buf);
|
||||
|
||||
if (fmt::CmpNoCase(name, buf) == 0)
|
||||
{
|
||||
entry_block = block;
|
||||
if (parent_block)
|
||||
*parent_block = last_block;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
last_block = block;
|
||||
block = entry.is_used ? entry.next_block : 0ULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
s32 vfsHDD::OpenDir(const std::string& name)
|
||||
{
|
||||
LOG_WARNING(HLE, "OpenDir(%s)", name.c_str());
|
||||
u64 entry_block;
|
||||
|
||||
if (!SearchEntry(name, entry_block))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(entry_block * m_hdd_info.block_size) != -1);
|
||||
|
||||
vfsHDD_Entry entry;
|
||||
m_hdd_file.Read(&entry, sizeof(vfsHDD_Entry));
|
||||
|
||||
if (entry.type == vfsHDD_Entry_File)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
m_cur_dir_block = entry.data_block;
|
||||
ReadEntry(m_cur_dir_block, m_cur_dir);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool vfsHDD::Rename(const std::string& from, const std::string& to)
|
||||
{
|
||||
u64 entry_block;
|
||||
if (!SearchEntry(from, entry_block))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
vfsHDD_Entry entry;
|
||||
ReadEntry(entry_block, entry);
|
||||
WriteEntry(entry_block, entry, to);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 vfsHDD::FindFreeBlock()
|
||||
{
|
||||
vfsHDD_Block block_info;
|
||||
|
||||
for (u64 i = 0; i < m_hdd_info.block_count; ++i)
|
||||
{
|
||||
ReadBlock(i, block_info);
|
||||
|
||||
if (!block_info.is_used)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vfsHDD::WriteBlock(u64 block, const vfsHDD_Block& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd_file.Write(&data, sizeof(vfsHDD_Block));
|
||||
}
|
||||
|
||||
void vfsHDD::ReadBlock(u64 block, vfsHDD_Block& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd_file.Read(&data, sizeof(vfsHDD_Block));
|
||||
}
|
||||
|
||||
void vfsHDD::WriteEntry(u64 block, const vfsHDD_Entry& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd_file.Write(&data, sizeof(vfsHDD_Entry));
|
||||
}
|
||||
|
||||
void vfsHDD::ReadEntry(u64 block, vfsHDD_Entry& data)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd_file.Read(&data, sizeof(vfsHDD_Entry));
|
||||
}
|
||||
|
||||
void vfsHDD::ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd_file.Read(&data, sizeof(vfsHDD_Entry));
|
||||
name.resize(GetMaxNameLen());
|
||||
m_hdd_file.Read(&name.front(), GetMaxNameLen());
|
||||
}
|
||||
|
||||
void vfsHDD::ReadEntry(u64 block, std::string& name)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size + sizeof(vfsHDD_Entry)) != -1);
|
||||
|
||||
name.resize(GetMaxNameLen());
|
||||
m_hdd_file.Read(&name.front(), GetMaxNameLen());
|
||||
}
|
||||
|
||||
void vfsHDD::WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name)
|
||||
{
|
||||
CHECK_ASSERTION(m_hdd_file.Seek(block * m_hdd_info.block_size) != -1);
|
||||
|
||||
m_hdd_file.Write(&data, sizeof(vfsHDD_Entry));
|
||||
m_hdd_file.Write(name.c_str(), std::min<size_t>(GetMaxNameLen() - 1, name.length() + 1));
|
||||
}
|
||||
|
||||
bool vfsHDD::Create(vfsHDD_EntryType type, const std::string& name)
|
||||
{
|
||||
if (HasEntry(name))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 new_block = FindFreeBlock();
|
||||
if (!new_block)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_NOTICE(HLE, "CREATING ENTRY AT 0x%llx", new_block);
|
||||
WriteBlock(new_block, g_used_block);
|
||||
|
||||
{
|
||||
vfsHDD_Entry new_entry;
|
||||
vfsHDDManager::CreateEntry(new_entry);
|
||||
new_entry.next_block = 0;
|
||||
new_entry.type = type;
|
||||
|
||||
if (type == vfsHDD_Entry_Dir)
|
||||
{
|
||||
u64 block_cur = FindFreeBlock();
|
||||
|
||||
if (!block_cur)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WriteBlock(block_cur, g_used_block);
|
||||
|
||||
u64 block_last = FindFreeBlock();
|
||||
|
||||
if (!block_last)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
WriteBlock(block_last, g_used_block);
|
||||
|
||||
vfsHDD_Entry entry_cur, entry_last;
|
||||
vfsHDDManager::CreateEntry(entry_cur);
|
||||
vfsHDDManager::CreateEntry(entry_last);
|
||||
|
||||
entry_cur.type = vfsHDD_Entry_Dir;
|
||||
entry_cur.data_block = block_cur;
|
||||
entry_cur.next_block = block_last;
|
||||
|
||||
entry_last.type = vfsHDD_Entry_Dir;
|
||||
entry_last.data_block = m_cur_dir_block;
|
||||
entry_last.next_block = 0;
|
||||
|
||||
new_entry.data_block = block_cur;
|
||||
|
||||
WriteEntry(block_cur, entry_cur, ".");
|
||||
WriteEntry(block_last, entry_last, "..");
|
||||
}
|
||||
|
||||
WriteEntry(new_block, new_entry, name);
|
||||
}
|
||||
|
||||
{
|
||||
u64 block = m_cur_dir_block;
|
||||
|
||||
vfsHDD_Block tmp;
|
||||
while (block)
|
||||
{
|
||||
ReadBlock(block, tmp);
|
||||
|
||||
if (!tmp.next_block)
|
||||
break;
|
||||
|
||||
block = tmp.next_block;
|
||||
}
|
||||
|
||||
tmp.next_block = new_block;
|
||||
WriteBlock(block, tmp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vfsHDD::GetFirstEntry(u64& block, vfsHDD_Entry& entry, std::string& name)
|
||||
{
|
||||
if (!m_cur_dir_block)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadEntry(m_cur_dir_block, entry, name);
|
||||
block = entry.is_used ? entry.next_block : 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vfsHDD::GetNextEntry(u64& block, vfsHDD_Entry& entry, std::string& name)
|
||||
{
|
||||
if (!block)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ReadEntry(block, entry, name);
|
||||
|
||||
block = entry.is_used ? entry.next_block : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vfsHDD::Open(const std::string& path, u32 mode)
|
||||
{
|
||||
const char* s = path.c_str();
|
||||
u64 from = 0;
|
||||
u64 pos = 0;
|
||||
u64 file_pos = -1;
|
||||
|
||||
do
|
||||
{
|
||||
if (s[pos] == '\\' || s[pos] == '/' || s[pos] == '\0') // ???
|
||||
{
|
||||
if (file_pos != -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (from != -1)
|
||||
{
|
||||
if (pos - from > 1)
|
||||
{
|
||||
s32 res = OpenDir(std::string(s + from, pos));
|
||||
|
||||
if (res == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (res == 1)
|
||||
{
|
||||
file_pos = from;
|
||||
}
|
||||
}
|
||||
|
||||
from = pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
from = pos;
|
||||
}
|
||||
}
|
||||
} while (s[pos++] != '\0');
|
||||
|
||||
if (file_pos == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
u64 file_block;
|
||||
if (!SearchEntry(std::string(s + file_pos), file_block))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_NOTICE(HLE, "ENTRY FOUND AT 0x%llx", file_block);
|
||||
m_file.Open(file_block);
|
||||
|
||||
return vfsFileBase::Open(path, mode);
|
||||
}
|
||||
|
||||
bool vfsHDD::HasEntry(const std::string& name)
|
||||
{
|
||||
u64 file_block;
|
||||
if (!SearchEntry(name, file_block))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void vfsHDD::RemoveBlocksDir(u64 start_block)
|
||||
{
|
||||
std::string name;
|
||||
u64 block = start_block;
|
||||
vfsHDD_Entry entry;
|
||||
|
||||
while (block)
|
||||
{
|
||||
ReadEntry(block, entry, name);
|
||||
WriteBlock(block, g_null_block);
|
||||
|
||||
if (entry.type == vfsHDD_Entry_Dir && name != "." && name != "..")
|
||||
{
|
||||
LOG_WARNING(HLE, "Removing sub folder '%s'", name.c_str());
|
||||
RemoveBlocksDir(entry.data_block);
|
||||
}
|
||||
else if (entry.type == vfsHDD_Entry_File)
|
||||
{
|
||||
RemoveBlocksFile(entry.data_block);
|
||||
}
|
||||
|
||||
block = entry.next_block;
|
||||
}
|
||||
}
|
||||
|
||||
void vfsHDD::RemoveBlocksFile(u64 start_block)
|
||||
{
|
||||
u64 block = start_block;
|
||||
vfsHDD_Block block_data;
|
||||
|
||||
while (block)
|
||||
{
|
||||
ReadBlock(block, block_data);
|
||||
WriteBlock(block, g_null_block);
|
||||
|
||||
block = block_data.next_block;
|
||||
}
|
||||
}
|
||||
|
||||
bool vfsHDD::RemoveEntry(const std::string& name)
|
||||
{
|
||||
u64 entry_block, parent_entry;
|
||||
if (!SearchEntry(name, entry_block, &parent_entry))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
vfsHDD_Entry entry;
|
||||
ReadEntry(entry_block, entry);
|
||||
if (entry.type == vfsHDD_Entry_Dir)
|
||||
{
|
||||
RemoveBlocksDir(entry.data_block);
|
||||
}
|
||||
else if (entry.type == vfsHDD_Entry_File)
|
||||
{
|
||||
RemoveBlocksFile(entry.data_block);
|
||||
}
|
||||
|
||||
if (parent_entry)
|
||||
{
|
||||
u64 next = entry.next_block;
|
||||
ReadEntry(parent_entry, entry);
|
||||
entry.next_block = next;
|
||||
WriteEntry(parent_entry, entry);
|
||||
}
|
||||
WriteBlock(entry_block, g_null_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
u64 vfsHDD::Write(const void* src, u64 size)
|
||||
{
|
||||
return m_file.Write(src, size); // ???
|
||||
}
|
||||
|
||||
u64 vfsHDD::Read(void* dst, u64 size)
|
||||
{
|
||||
return m_file.Read(dst, size); // ???
|
||||
}
|
||||
|
||||
u64 vfsHDD::Seek(s64 offset, fs::seek_mode whence)
|
||||
{
|
||||
switch (whence)
|
||||
{
|
||||
case fs::seek_set: return m_file.Seek(offset);
|
||||
case fs::seek_cur: return m_file.Seek(Tell() + offset);
|
||||
case fs::seek_end: return m_file.Seek(m_file.GetSize() + offset);
|
||||
}
|
||||
|
||||
throw EXCEPTION("Unknown whence (0x%x)", whence);
|
||||
}
|
||||
|
||||
u64 vfsHDD::Tell() const
|
||||
{
|
||||
return m_file.Tell(); // ???
|
||||
}
|
||||
|
||||
bool vfsHDD::Eof() const
|
||||
{
|
||||
return m_file.Eof();
|
||||
}
|
||||
|
||||
bool vfsHDD::IsOpened() const
|
||||
{
|
||||
return true; // ???
|
||||
}
|
||||
|
||||
u64 vfsHDD::GetSize() const
|
||||
{
|
||||
return m_file.GetSize();
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
#pragma once
|
||||
#include "Emu/FS/vfsDevice.h"
|
||||
#include "Emu/FS/vfsLocalFile.h"
|
||||
|
||||
static const u64 g_hdd_magic = *(u64*)"PS3eHDD\0";
|
||||
static const u16 g_hdd_version = 0x0001;
|
||||
|
||||
struct vfsHDD_Block
|
||||
{
|
||||
struct
|
||||
{
|
||||
u64 is_used : 1;
|
||||
u64 next_block : 63;
|
||||
};
|
||||
} static const g_null_block = {0}, g_used_block = {1};
|
||||
|
||||
struct vfsHDD_Hdr : public vfsHDD_Block
|
||||
{
|
||||
u64 magic;
|
||||
u16 version;
|
||||
u64 block_count;
|
||||
u32 block_size;
|
||||
};
|
||||
|
||||
enum vfsHDD_EntryType : u8
|
||||
{
|
||||
vfsHDD_Entry_Dir = 0,
|
||||
vfsHDD_Entry_File = 1,
|
||||
vfsHDD_Entry_Link = 2,
|
||||
};
|
||||
|
||||
struct vfsHDD_Entry : public vfsHDD_Block
|
||||
{
|
||||
u64 data_block;
|
||||
u32 access;
|
||||
vfsHDD_EntryType type;
|
||||
u64 size;
|
||||
u64 ctime;
|
||||
u64 mtime;
|
||||
u64 atime;
|
||||
};
|
||||
|
||||
class vfsHDDManager
|
||||
{
|
||||
public:
|
||||
static void CreateBlock(vfsHDD_Block& block);
|
||||
|
||||
static void CreateEntry(vfsHDD_Entry& entry);
|
||||
|
||||
static void CreateHDD(const std::string& path, u64 size, u64 block_size);
|
||||
|
||||
void Format();
|
||||
|
||||
void AppendEntry(vfsHDD_Entry entry);
|
||||
};
|
||||
|
||||
|
||||
class vfsHDDFile
|
||||
{
|
||||
u64 m_info_block;
|
||||
vfsHDD_Entry m_info;
|
||||
const vfsHDD_Hdr& m_hdd_info;
|
||||
vfsLocalFile& m_hdd;
|
||||
u32 m_position;
|
||||
u64 m_cur_block;
|
||||
|
||||
bool goto_block(u64 n);
|
||||
|
||||
void RemoveBlocks(u64 start_block);
|
||||
|
||||
void WriteBlock(u64 block, const vfsHDD_Block& data);
|
||||
|
||||
void ReadBlock(u64 block, vfsHDD_Block& data);
|
||||
|
||||
void WriteEntry(u64 block, const vfsHDD_Entry& data);
|
||||
|
||||
void ReadEntry(u64 block, vfsHDD_Entry& data);
|
||||
|
||||
void ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name);
|
||||
|
||||
void ReadEntry(u64 block, std::string& name);
|
||||
|
||||
void WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name);
|
||||
|
||||
force_inline u32 GetMaxNameLen() const
|
||||
{
|
||||
return m_hdd_info.block_size - sizeof(vfsHDD_Entry);
|
||||
}
|
||||
|
||||
public:
|
||||
vfsHDDFile(vfsLocalFile& hdd, const vfsHDD_Hdr& hdd_info)
|
||||
: m_hdd(hdd)
|
||||
, m_hdd_info(hdd_info)
|
||||
{
|
||||
}
|
||||
|
||||
~vfsHDDFile()
|
||||
{
|
||||
}
|
||||
|
||||
void Open(u64 info_block);
|
||||
|
||||
u64 FindFreeBlock();
|
||||
|
||||
u64 GetSize() const
|
||||
{
|
||||
return m_info.size;
|
||||
}
|
||||
|
||||
bool Seek(u64 pos);
|
||||
|
||||
u64 Tell() const
|
||||
{
|
||||
return m_cur_block * m_hdd_info.block_size + m_position; // ???
|
||||
}
|
||||
|
||||
void SaveInfo();
|
||||
|
||||
u64 Read(void* dst, u64 size);
|
||||
|
||||
u64 Write(const void* src, u64 size);
|
||||
|
||||
bool Eof() const
|
||||
{
|
||||
return m_info.size <= (m_cur_block * m_hdd_info.block_size + m_position);
|
||||
}
|
||||
};
|
||||
|
||||
class vfsDeviceHDD : public vfsDevice
|
||||
{
|
||||
std::string m_hdd_path;
|
||||
|
||||
public:
|
||||
vfsDeviceHDD(const std::string& hdd_path);
|
||||
|
||||
virtual vfsFileBase* GetNewFileStream() override;
|
||||
virtual vfsDirBase* GetNewDirStream() override;
|
||||
};
|
||||
|
||||
class vfsHDD : public vfsFileBase
|
||||
{
|
||||
vfsHDD_Hdr m_hdd_info;
|
||||
vfsLocalFile m_hdd_file;
|
||||
const std::string& m_hdd_path;
|
||||
vfsHDD_Entry m_cur_dir;
|
||||
u64 m_cur_dir_block;
|
||||
vfsHDDFile m_file;
|
||||
|
||||
public:
|
||||
vfsHDD(vfsDevice* device, const std::string& hdd_path);
|
||||
|
||||
force_inline u32 GetMaxNameLen() const
|
||||
{
|
||||
return m_hdd_info.block_size - sizeof(vfsHDD_Entry);
|
||||
}
|
||||
|
||||
bool SearchEntry(const std::string& name, u64& entry_block, u64* parent_block = nullptr);
|
||||
|
||||
s32 OpenDir(const std::string& name);
|
||||
|
||||
bool Rename(const std::string& from, const std::string& to);
|
||||
|
||||
u64 FindFreeBlock();
|
||||
|
||||
void WriteBlock(u64 block, const vfsHDD_Block& data);
|
||||
|
||||
void ReadBlock(u64 block, vfsHDD_Block& data);
|
||||
|
||||
void WriteEntry(u64 block, const vfsHDD_Entry& data);
|
||||
|
||||
void ReadEntry(u64 block, vfsHDD_Entry& data);
|
||||
|
||||
void ReadEntry(u64 block, vfsHDD_Entry& data, std::string& name);
|
||||
|
||||
void ReadEntry(u64 block, std::string& name);
|
||||
|
||||
void WriteEntry(u64 block, const vfsHDD_Entry& data, const std::string& name);
|
||||
|
||||
bool Create(vfsHDD_EntryType type, const std::string& name);
|
||||
|
||||
bool GetFirstEntry(u64& block, vfsHDD_Entry& entry, std::string& name);
|
||||
|
||||
bool GetNextEntry(u64& block, vfsHDD_Entry& entry, std::string& name);
|
||||
|
||||
virtual bool Open(const std::string& path, u32 mode = fom::read) override;
|
||||
|
||||
bool HasEntry(const std::string& name);
|
||||
|
||||
void RemoveBlocksDir(u64 start_block);
|
||||
|
||||
void RemoveBlocksFile(u64 start_block);
|
||||
|
||||
bool RemoveEntry(const std::string& name);
|
||||
|
||||
virtual u64 Write(const void* src, u64 count) override;
|
||||
|
||||
virtual u64 Read(void* dst, u64 count) override;
|
||||
|
||||
virtual u64 Seek(s64 offset, fs::seek_mode whence = fs::seek_set) override;
|
||||
|
||||
virtual u64 Tell() const override;
|
||||
|
||||
virtual bool Eof() const override;
|
||||
|
||||
virtual bool IsOpened() const override;
|
||||
|
||||
virtual u64 GetSize() const override;
|
||||
};
|
|
@ -1,156 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "IdManager.h"
|
||||
|
||||
namespace idm
|
||||
{
|
||||
shared_mutex g_mutex;
|
||||
|
||||
idm::map_t g_map;
|
||||
|
||||
u32 g_last_raw_id = 0;
|
||||
|
||||
thread_local u32 g_tls_last_id = 0xdeadbeef;
|
||||
}
|
||||
|
||||
namespace fxm
|
||||
{
|
||||
shared_mutex g_mutex;
|
||||
|
||||
fxm::map_t g_map;
|
||||
}
|
||||
|
||||
void idm::clear()
|
||||
{
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
// Call recorded finalization functions for all IDs
|
||||
for (auto& id : idm::map_t(std::move(g_map)))
|
||||
{
|
||||
(*id.second.type_index)(id.second.data.get());
|
||||
}
|
||||
|
||||
g_last_raw_id = 0;
|
||||
}
|
||||
|
||||
bool idm::check(u32 in_id, id_type_index_t type)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
const auto found = g_map.find(in_id);
|
||||
|
||||
return found != g_map.end() && found->second.type_index == type;
|
||||
}
|
||||
|
||||
const std::type_info* idm::get_type(u32 raw_id)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
const auto found = g_map.find(raw_id);
|
||||
|
||||
return found == g_map.end() ? nullptr : found->second.info;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> idm::get(u32 in_id, id_type_index_t type)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
const auto found = g_map.find(in_id);
|
||||
|
||||
if (found == g_map.end() || found->second.type_index != type)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return found->second.data;
|
||||
}
|
||||
|
||||
idm::map_t idm::get_all(id_type_index_t type)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
idm::map_t result;
|
||||
|
||||
for (auto& id : g_map)
|
||||
{
|
||||
if (id.second.type_index == type)
|
||||
{
|
||||
result.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> idm::withdraw(u32 in_id, id_type_index_t type)
|
||||
{
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
const auto found = g_map.find(in_id);
|
||||
|
||||
if (found == g_map.end() || found->second.type_index != type)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ptr = std::move(found->second.data);
|
||||
|
||||
g_map.erase(found);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
u32 idm::get_count(id_type_index_t type)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
u32 result = 0;
|
||||
|
||||
for (auto& id : g_map)
|
||||
{
|
||||
if (id.second.type_index == type)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void fxm::clear()
|
||||
{
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
// Call recorded finalization functions for all IDs
|
||||
for (auto& id : fxm::map_t(std::move(g_map)))
|
||||
{
|
||||
if (id.second) (*id.first)(id.second.get());
|
||||
}
|
||||
}
|
||||
|
||||
bool fxm::check(id_type_index_t type)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
const auto found = g_map.find(type);
|
||||
|
||||
return found != g_map.end() && found->second;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> fxm::get(id_type_index_t type)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
const auto found = g_map.find(type);
|
||||
|
||||
return found != g_map.end() ? found->second : nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> fxm::withdraw(id_type_index_t type)
|
||||
{
|
||||
std::unique_lock<shared_mutex> lock(g_mutex);
|
||||
|
||||
const auto found = g_map.find(type);
|
||||
|
||||
return found != g_map.end() ? std::move(found->second) : nullptr;
|
||||
}
|
|
@ -2,86 +2,154 @@
|
|||
|
||||
#include "Utilities/SharedMutex.h"
|
||||
|
||||
#define ID_MANAGER_INCLUDED
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
// TODO: make id_aux_initialize and id_aux_finalize safer against a possible ODR violation
|
||||
|
||||
// Function called after the successfull creation of an ID (does nothing by default, provide an overload)
|
||||
inline void id_aux_initialize(void*)
|
||||
// Mostly helper namespace
|
||||
namespace id_manager
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
// Function called after the ID removal (does nothing by default, provide an overload)
|
||||
inline void id_aux_finalize(void*)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
// Type-erased id_aux_* function type
|
||||
using id_aux_func_t = void(*)(void*);
|
||||
|
||||
template<typename T>
|
||||
struct id_type_info_t
|
||||
{
|
||||
static const auto size = sizeof(T); // forbid forward declarations
|
||||
|
||||
static const id_aux_func_t on_remove;
|
||||
};
|
||||
|
||||
// Type-erased finalization function
|
||||
template<typename T>
|
||||
const id_aux_func_t id_type_info_t<T>::on_remove = [](void* ptr)
|
||||
{
|
||||
return id_aux_finalize(static_cast<T*>(ptr));
|
||||
};
|
||||
|
||||
using id_type_index_t = const id_aux_func_t*;
|
||||
|
||||
// Get a unique pointer to the on_remove value (will be unique for each type)
|
||||
template<typename T>
|
||||
inline constexpr id_type_index_t get_id_type_index()
|
||||
{
|
||||
return &id_type_info_t<T>::on_remove;
|
||||
}
|
||||
|
||||
// Default ID traits for any arbitrary type
|
||||
template<typename T>
|
||||
struct id_traits
|
||||
{
|
||||
static const auto size = sizeof(T); // forbid forward declarations
|
||||
|
||||
// Get next mapped id (may return 0 if out of IDs)
|
||||
static u32 next_id(u32 raw_id) { return raw_id < 0x80000000 ? (raw_id + 1) & 0x7fffffff : 0; }
|
||||
|
||||
// Convert "public" id to mapped id (may return 0 if invalid)
|
||||
static u32 in_id(u32 id) { return id; }
|
||||
|
||||
// Convert mapped id to "public" id
|
||||
static u32 out_id(u32 raw_id) { return raw_id; }
|
||||
};
|
||||
|
||||
// ID Manager
|
||||
// 0 is invalid ID
|
||||
// 1..0x7fffffff : general purpose IDs
|
||||
// 0x80000000+ : reserved (may be used through id_traits specializations)
|
||||
namespace idm
|
||||
{
|
||||
struct id_data_t final
|
||||
// Optional ID traits
|
||||
template<typename T, typename = void>
|
||||
struct id_traits
|
||||
{
|
||||
std::shared_ptr<void> data;
|
||||
const std::type_info* info;
|
||||
id_type_index_t type_index;
|
||||
using tag = void;
|
||||
|
||||
template<typename T> id_data_t(const std::shared_ptr<T>& data)
|
||||
: data(data)
|
||||
, info(&typeid(T))
|
||||
, type_index(get_id_type_index<T>())
|
||||
static constexpr u32 min = 1;
|
||||
static constexpr u32 max = 0x7fffffff;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct id_traits<T, void_t<typename T::id_base, decltype(&T::id_min), decltype(&T::id_max)>>
|
||||
{
|
||||
using tag = typename T::id_base;
|
||||
|
||||
static constexpr u32 min = T::id_min;
|
||||
static constexpr u32 max = T::id_max;
|
||||
};
|
||||
|
||||
// Optional ID storage
|
||||
template<typename T, typename = void>
|
||||
struct id_storage
|
||||
{
|
||||
static const u32* get(T*)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct id_storage<T, void_t<decltype(&T::id)>>
|
||||
{
|
||||
static const u32* get(T* ptr)
|
||||
{
|
||||
return &ptr->id;
|
||||
}
|
||||
};
|
||||
|
||||
// Optional object initialization function (called after ID registration)
|
||||
template<typename T, typename = void>
|
||||
struct on_init
|
||||
{
|
||||
static void func(T*)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Custom hasher for ID values (map to itself)
|
||||
template<typename T>
|
||||
struct on_init<T, decltype(std::declval<T>().on_init())>
|
||||
{
|
||||
static void func(T* ptr)
|
||||
{
|
||||
ptr->on_init();
|
||||
}
|
||||
};
|
||||
|
||||
// Optional object finalization function (called after ID removal)
|
||||
template<typename T, typename = void>
|
||||
struct on_stop
|
||||
{
|
||||
static void func(T*)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct on_stop<T, decltype(std::declval<T>().on_stop())>
|
||||
{
|
||||
static void func(T* ptr)
|
||||
{
|
||||
ptr->on_stop();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename>
|
||||
class typeinfo
|
||||
{
|
||||
// Global variable for each registered type
|
||||
template<typename T>
|
||||
struct registered
|
||||
{
|
||||
static const u32 index;
|
||||
};
|
||||
|
||||
// Access global type list
|
||||
static never_inline auto& access()
|
||||
{
|
||||
static std::vector<typeinfo> list;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static never_inline u32 add_type(typeinfo info)
|
||||
{
|
||||
auto& list = access();
|
||||
|
||||
list.emplace_back(info);
|
||||
|
||||
return ::size32(list) - 1;
|
||||
}
|
||||
|
||||
public:
|
||||
const std::type_info* info;
|
||||
void(*on_init)(void*);
|
||||
void(*on_stop)(void*);
|
||||
|
||||
// Get type index
|
||||
template<typename T>
|
||||
static inline u32 get_index()
|
||||
{
|
||||
// Forbid forward declarations (It'd be better to allow them sometimes but it seems too dangerous)
|
||||
static constexpr auto size = sizeof(std::conditional_t<std::is_void<T>::value, void*, T>);
|
||||
|
||||
return registered<T>::index;
|
||||
}
|
||||
|
||||
// Read all registered types
|
||||
static inline const auto& get()
|
||||
{
|
||||
return access();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename TAG> template<typename T>
|
||||
const u32 typeinfo<TAG>::registered<T>::index = typeinfo<TAG>::add_type(
|
||||
{
|
||||
&typeid(T),
|
||||
PURE_EXPR(id_manager::on_init<T>::func(static_cast<T*>(ptr)), void* ptr),
|
||||
PURE_EXPR(id_manager::on_stop<T>::func(static_cast<T*>(ptr)), void* ptr),
|
||||
});
|
||||
}
|
||||
|
||||
// Object manager for emulated process. Multiple objects of specified arbitrary type are given unique IDs.
|
||||
class idm
|
||||
{
|
||||
// Rules for ID allocation:
|
||||
// 0) Individual ID counter may be specified for each type by defining 'using id_base = ...;'
|
||||
// 1) If no id_base specified, void is assumed.
|
||||
// 2) g_id[id_base] indicates next ID allocated in g_map.
|
||||
// 3) g_map[id_base] contains the additional copy of object pointer.
|
||||
|
||||
// Custom hasher for ID values
|
||||
struct id_hash_t final
|
||||
{
|
||||
std::size_t operator ()(u32 value) const
|
||||
|
@ -90,82 +158,157 @@ namespace idm
|
|||
}
|
||||
};
|
||||
|
||||
using map_t = std::unordered_map<u32, id_data_t, id_hash_t>;
|
||||
using map_type = std::unordered_map<u32, std::shared_ptr<void>, id_hash_t>;
|
||||
|
||||
// Can be called from the constructor called through make() or make_ptr() to get the ID of the object being created
|
||||
inline u32 get_last_id()
|
||||
// Type Index -> ID -> Object. Use global since only one process is supported atm.
|
||||
static std::vector<map_type> g_map;
|
||||
|
||||
// Next ID for each category
|
||||
static std::vector<u32> g_id;
|
||||
|
||||
static shared_mutex g_mutex;
|
||||
|
||||
static const auto& get_types()
|
||||
{
|
||||
extern thread_local u32 g_tls_last_id;
|
||||
|
||||
return g_tls_last_id;
|
||||
return id_manager::typeinfo<idm>::get();
|
||||
}
|
||||
|
||||
// Remove all objects
|
||||
void clear();
|
||||
|
||||
// Internal
|
||||
bool check(u32 in_id, id_type_index_t type);
|
||||
|
||||
// Check if an ID of specified type exists
|
||||
template<typename T>
|
||||
bool check(u32 id)
|
||||
static inline u32 get_type()
|
||||
{
|
||||
return check(id_traits<T>::in_id(id), get_id_type_index<T>());
|
||||
return id_manager::typeinfo<idm>::get_index<T>();
|
||||
}
|
||||
|
||||
// Check if an ID exists and return its type or nullptr
|
||||
const std::type_info* get_type(u32 raw_id);
|
||||
|
||||
// Internal
|
||||
template<typename T, typename Ptr>
|
||||
std::shared_ptr<T> add(Ptr&& get_ptr)
|
||||
template<typename T>
|
||||
static inline u32 get_tag()
|
||||
{
|
||||
extern shared_mutex g_mutex;
|
||||
extern idm::map_t g_map;
|
||||
extern u32 g_last_raw_id;
|
||||
extern thread_local u32 g_tls_last_id;
|
||||
return get_type<typename id_manager::id_traits<T>::tag>();
|
||||
}
|
||||
|
||||
// Prepares new ID, returns nullptr if out of resources
|
||||
static map_type::pointer allocate_id(u32 tag, u32 min, u32 max)
|
||||
{
|
||||
// Check all IDs starting from "next id"
|
||||
for (u32 i = 0; i <= max - min; i++)
|
||||
{
|
||||
// Fix current ID (wrap around)
|
||||
if (g_id[tag] < min || g_id[tag] > max) g_id[tag] = min;
|
||||
|
||||
// Get ID
|
||||
const auto r = g_map[tag].emplace(g_id[tag]++, nullptr);
|
||||
|
||||
if (r.second)
|
||||
{
|
||||
return &*r.first;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Deallocate ID, returns object
|
||||
static std::shared_ptr<void> deallocate_id(u32 tag, u32 id)
|
||||
{
|
||||
const auto found = g_map[tag].find(id);
|
||||
|
||||
if (found == g_map[tag].end()) return nullptr;
|
||||
|
||||
auto ptr = std::move(found->second);
|
||||
|
||||
g_map[tag].erase(found);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Allocate new ID and construct it from the provider()
|
||||
template<typename T, typename F, typename = std::result_of_t<F()>>
|
||||
static map_type::pointer create_id(F&& provider)
|
||||
{
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
for (u32 raw_id = g_last_raw_id; (raw_id = id_traits<T>::next_id(raw_id)); /**/)
|
||||
if (auto place = allocate_id(get_tag<T>(), id_manager::id_traits<T>::min, id_manager::id_traits<T>::max))
|
||||
{
|
||||
if (g_map.find(raw_id) != g_map.end()) continue;
|
||||
try
|
||||
{
|
||||
// Get object, write it
|
||||
place->second = provider();
|
||||
|
||||
// Update ID storage if available
|
||||
if (const u32* id = id_manager::id_storage<T>::get(static_cast<T*>(place->second.get())))
|
||||
{
|
||||
*const_cast<u32*>(id) = place->first;
|
||||
}
|
||||
|
||||
g_tls_last_id = id_traits<T>::out_id(raw_id);
|
||||
|
||||
std::shared_ptr<T> ptr = get_ptr();
|
||||
|
||||
g_map.emplace(raw_id, id_data_t(ptr));
|
||||
|
||||
if (raw_id < 0x80000000) g_last_raw_id = raw_id;
|
||||
|
||||
return ptr;
|
||||
return &*g_map[get_type<T>()].emplace(*place).first;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
deallocate_id(get_tag<T>(), place->first);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Add a new ID of specified type with specified constructor arguments (returns object or nullptr)
|
||||
template<typename T, typename... Args>
|
||||
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_ptr(Args&&... args)
|
||||
// Remove ID and return object
|
||||
static std::shared_ptr<void> delete_id(u32 type, u32 tag, u32 id)
|
||||
{
|
||||
if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
auto&& ptr = deallocate_id(tag, id);
|
||||
|
||||
g_map[type].erase(id);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
public:
|
||||
// Initialize object manager
|
||||
static void init()
|
||||
{
|
||||
g_map.resize(get_types().size(), {});
|
||||
g_id.resize(get_types().size(), 0);
|
||||
}
|
||||
|
||||
// Remove all objects
|
||||
static void clear()
|
||||
{
|
||||
// Call recorded finalization functions for all IDs
|
||||
for (std::size_t i = 0; i < g_map.size(); i++)
|
||||
{
|
||||
id_aux_initialize(ptr.get());
|
||||
return ptr;
|
||||
for (auto& id : g_map[i])
|
||||
{
|
||||
get_types()[i].on_stop(id.second.get());
|
||||
}
|
||||
|
||||
g_map[i].clear();
|
||||
g_id[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Add a new ID of specified type with specified constructor arguments (returns object or nullptr)
|
||||
template<typename T, typename Make = T, typename... Args>
|
||||
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> make_ptr(Args&&... args)
|
||||
{
|
||||
if (auto pair = create_id<T>(WRAP_EXPR(std::make_shared<Make>(std::forward<Args>(args)...))))
|
||||
{
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
return{ pair->second, static_cast<T*>(pair->second.get()) };
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Add a new ID of specified type with specified constructor arguments (returns id)
|
||||
template<typename T, typename... Args>
|
||||
std::enable_if_t<std::is_constructible<T, Args...>::value, u32> make(Args&&... args)
|
||||
template<typename T, typename Make = T, typename... Args>
|
||||
static std::enable_if_t<std::is_constructible<Make, Args...>::value, u32> make(Args&&... args)
|
||||
{
|
||||
if (auto ptr = add<T>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...))))
|
||||
if (auto pair = create_id<T>(WRAP_EXPR(std::make_shared<Make>(std::forward<Args>(args)...))))
|
||||
{
|
||||
id_aux_initialize(ptr.get());
|
||||
return get_last_id();
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
return pair->first;
|
||||
}
|
||||
|
||||
throw EXCEPTION("Out of IDs ('%s')", typeid(T).name());
|
||||
|
@ -173,93 +316,118 @@ namespace idm
|
|||
|
||||
// Add a new ID for an existing object provided (returns new id)
|
||||
template<typename T>
|
||||
u32 import(const std::shared_ptr<T>& ptr)
|
||||
static u32 import_existing(const std::shared_ptr<T>& ptr)
|
||||
{
|
||||
static const auto size = sizeof(T); // forbid forward declarations
|
||||
|
||||
if (add<T>(WRAP_EXPR(ptr)))
|
||||
if (auto pair = create_id<T>(WRAP_EXPR(ptr)))
|
||||
{
|
||||
id_aux_initialize(ptr.get());
|
||||
return get_last_id();
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
return pair->first;
|
||||
}
|
||||
|
||||
throw EXCEPTION("Out of IDs ('%s')", typeid(T).name());
|
||||
}
|
||||
|
||||
// Internal
|
||||
std::shared_ptr<void> get(u32 in_id, id_type_index_t type);
|
||||
|
||||
// Get ID of specified type
|
||||
template<typename T>
|
||||
std::shared_ptr<T> get(u32 id)
|
||||
// Add a new ID for an object returned by provider()
|
||||
template<typename T, typename F, typename = std::result_of_t<F()>>
|
||||
static std::shared_ptr<T> import(F&& provider)
|
||||
{
|
||||
return std::static_pointer_cast<T>(get(id_traits<T>::in_id(id), get_id_type_index<T>()));
|
||||
}
|
||||
|
||||
// Internal
|
||||
idm::map_t get_all(id_type_index_t type);
|
||||
|
||||
// Get all IDs of specified type T (unsorted)
|
||||
template<typename T>
|
||||
std::vector<std::shared_ptr<T>> get_all()
|
||||
{
|
||||
std::vector<std::shared_ptr<T>> result;
|
||||
|
||||
for (auto& id : get_all(get_id_type_index<T>()))
|
||||
if (auto pair = create_id<T>(std::forward<F>(provider)))
|
||||
{
|
||||
result.emplace_back(std::static_pointer_cast<T>(id.second.data));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> withdraw(u32 in_id, id_type_index_t type);
|
||||
|
||||
// Remove the ID created with type T
|
||||
template<typename T>
|
||||
bool remove(u32 id)
|
||||
{
|
||||
if (auto ptr = withdraw(id_traits<T>::in_id(id), get_id_type_index<T>()))
|
||||
{
|
||||
id_aux_finalize(static_cast<T*>(ptr.get()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the ID created with type T and return it
|
||||
template<typename T>
|
||||
std::shared_ptr<T> withdraw(u32 id)
|
||||
{
|
||||
if (auto ptr = std::static_pointer_cast<T>(withdraw(id_traits<T>::in_id(id), get_id_type_index<T>())))
|
||||
{
|
||||
id_aux_finalize(ptr.get());
|
||||
|
||||
return ptr;
|
||||
id_manager::on_init<T>::func(static_cast<T*>(pair->second.get()));
|
||||
return { pair->second, static_cast<T*>(pair->second.get()) };
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 get_count(id_type_index_t type);
|
||||
// Check whether ID exists
|
||||
template<typename T>
|
||||
static bool check(u32 id)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
return g_map[get_type<T>()].count(id) != 0;
|
||||
}
|
||||
|
||||
// Get ID
|
||||
template<typename T>
|
||||
static std::shared_ptr<T> get(u32 id)
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
const auto found = g_map[get_type<T>()].find(id);
|
||||
|
||||
if (found == g_map[get_type<T>()].end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return{ found->second, static_cast<T*>(found->second.get()) };
|
||||
}
|
||||
|
||||
// Get all IDs (unsorted)
|
||||
template<typename T>
|
||||
static std::vector<std::shared_ptr<T>> get_all()
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
std::vector<std::shared_ptr<T>> result;
|
||||
|
||||
for (auto& id : g_map[get_type<T>()])
|
||||
{
|
||||
result.emplace_back(id.second, static_cast<T*>(id.second.get()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Remove the ID
|
||||
template<typename T>
|
||||
static bool remove(u32 id)
|
||||
{
|
||||
auto&& ptr = delete_id(get_type<T>(), get_tag<T>(), id);
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return ptr.operator bool();
|
||||
}
|
||||
|
||||
// Remove the ID and return it
|
||||
template<typename T>
|
||||
static std::shared_ptr<T> withdraw(u32 id)
|
||||
{
|
||||
auto&& ptr = delete_id(get_type<T>(), get_tag<T>(), id);
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return{ ptr, static_cast<T*>(ptr.get()) };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
u32 get_count()
|
||||
static u32 get_count()
|
||||
{
|
||||
return get_count(get_id_type_index<T>());
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
return ::size32(g_map[get_type<T>()]);
|
||||
}
|
||||
|
||||
// Get sorted list of all IDs of specified type
|
||||
template<typename T>
|
||||
std::set<u32> get_set()
|
||||
static std::set<u32> get_set()
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
std::set<u32> result;
|
||||
|
||||
for (auto& id : get_all(get_id_type_index<T>()))
|
||||
for (auto& id : g_map[get_type<T>()])
|
||||
{
|
||||
result.emplace(id_traits<T>::out_id(id.first));
|
||||
result.emplace(id.first);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -267,188 +435,234 @@ namespace idm
|
|||
|
||||
// Get sorted map (ID value -> ID data) of all IDs of specified type
|
||||
template<typename T>
|
||||
std::map<u32, std::shared_ptr<T>> get_map()
|
||||
static std::map<u32, std::shared_ptr<T>> get_map()
|
||||
{
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
std::map<u32, std::shared_ptr<T>> result;
|
||||
|
||||
for (auto& id : get_all(get_id_type_index<T>()))
|
||||
for (auto& id : g_map[get_type<T>()])
|
||||
{
|
||||
result[id_traits<T>::out_id(id.first)] = std::static_pointer_cast<T>(id.second.data);
|
||||
result[id.first] = { id.second, static_cast<T*>(id.second.get()) };
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Fixed Object Manager
|
||||
// allows to manage shared objects of any specified type, but only one object per type;
|
||||
// object are deleted when the emulation is stopped
|
||||
namespace fxm
|
||||
// Object manager for emulated process. One unique object per type, or zero.
|
||||
class fxm
|
||||
{
|
||||
// Custom hasher for aligned pointer values
|
||||
struct hash_t final
|
||||
// Type Index -> Object. Use global since only one process is supported atm.
|
||||
static std::vector<std::shared_ptr<void>> g_map;
|
||||
|
||||
static shared_mutex g_mutex;
|
||||
|
||||
static inline const auto& get_types()
|
||||
{
|
||||
std::size_t operator()(id_type_index_t value) const
|
||||
{
|
||||
return reinterpret_cast<std::size_t>(value) >> 3;
|
||||
}
|
||||
};
|
||||
return id_manager::typeinfo<fxm>::get();
|
||||
}
|
||||
|
||||
using map_t = std::unordered_map<id_type_index_t, std::shared_ptr<void>, hash_t>;
|
||||
|
||||
// Remove all objects
|
||||
void clear();
|
||||
|
||||
// Internal (returns old and new pointers)
|
||||
template<typename T, bool Always, typename Ptr>
|
||||
std::pair<std::shared_ptr<T>, std::shared_ptr<T>> add(Ptr&& get_ptr)
|
||||
template<typename T>
|
||||
static inline u32 get_type()
|
||||
{
|
||||
extern shared_mutex g_mutex;
|
||||
extern fxm::map_t g_map;
|
||||
return id_manager::typeinfo<fxm>::get_index<T>();
|
||||
}
|
||||
|
||||
static std::shared_ptr<void> remove(u32 type)
|
||||
{
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
auto& item = g_map[get_id_type_index<T>()];
|
||||
return std::move(g_map[type]);
|
||||
}
|
||||
|
||||
if (Always || !item)
|
||||
public:
|
||||
// Initialize object manager
|
||||
static void init()
|
||||
{
|
||||
g_map.resize(get_types().size(), {});
|
||||
}
|
||||
|
||||
// Remove all objects
|
||||
static void clear()
|
||||
{
|
||||
// Call recorded finalization functions for all IDs
|
||||
for (std::size_t i = 0; i < g_map.size(); i++)
|
||||
{
|
||||
std::shared_ptr<T> old = std::static_pointer_cast<T>(std::move(item));
|
||||
std::shared_ptr<T> ptr = get_ptr();
|
||||
if (g_map[i])
|
||||
{
|
||||
get_types()[i].on_stop(g_map[i].get());
|
||||
}
|
||||
|
||||
// Set new object
|
||||
item = ptr;
|
||||
|
||||
return{ std::move(old), std::move(ptr) };
|
||||
}
|
||||
else
|
||||
{
|
||||
return{ std::static_pointer_cast<T>(item), nullptr };
|
||||
g_map[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Create the object (returns nullptr if it already exists)
|
||||
template<typename T, typename... Args>
|
||||
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make(Args&&... args)
|
||||
template<typename T, typename Make = T, typename... Args>
|
||||
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> make(Args&&... args)
|
||||
{
|
||||
auto pair = add<T, false>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
|
||||
|
||||
if (pair.second)
|
||||
std::shared_ptr<T> ptr;
|
||||
{
|
||||
id_aux_initialize(pair.second.get());
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
if (!g_map[get_type<T>()])
|
||||
{
|
||||
ptr = std::make_shared<Make>(std::forward<Args>(args)...);
|
||||
|
||||
g_map[get_type<T>()] = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(pair.second);
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Create the object unconditionally (old object will be removed if it exists)
|
||||
template<typename T, typename... Args>
|
||||
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> make_always(Args&&... args)
|
||||
template<typename T, typename Make = T, typename... Args>
|
||||
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> make_always(Args&&... args)
|
||||
{
|
||||
auto pair = add<T, true>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
|
||||
|
||||
if (pair.first)
|
||||
std::shared_ptr<T> ptr;
|
||||
std::shared_ptr<void> old;
|
||||
{
|
||||
id_aux_finalize(pair.first.get());
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
old = std::move(g_map[get_type<T>()]);
|
||||
ptr = std::make_shared<Make>(std::forward<Args>(args)...);
|
||||
|
||||
g_map[get_type<T>()] = ptr;
|
||||
}
|
||||
|
||||
id_aux_initialize(pair.second.get());
|
||||
return std::move(pair.second);
|
||||
if (old)
|
||||
{
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(old.get()));
|
||||
}
|
||||
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Emplace the object returned by provider() and return it if no object exists
|
||||
template<typename T, typename F>
|
||||
auto import(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
|
||||
static auto import(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
|
||||
{
|
||||
static const auto size = sizeof(T); // forbid forward declarations
|
||||
|
||||
auto pair = add<T, false>(std::forward<F>(provider));
|
||||
|
||||
if (pair.second)
|
||||
std::shared_ptr<T> ptr;
|
||||
{
|
||||
id_aux_initialize(pair.second.get());
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
if (!g_map[get_type<T>()])
|
||||
{
|
||||
ptr = provider();
|
||||
|
||||
g_map[get_type<T>()] = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(pair.second);
|
||||
if (ptr)
|
||||
{
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Emplace the object return by provider() (old object will be removed if it exists)
|
||||
template<typename T, typename F>
|
||||
auto import_always(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
|
||||
static auto import_always(F&& provider) -> decltype(static_cast<std::shared_ptr<T>>(provider()))
|
||||
{
|
||||
static const auto size = sizeof(T); // forbid forward declarations
|
||||
|
||||
auto pair = add<T, true>(std::forward<F>(provider));
|
||||
|
||||
if (pair.first)
|
||||
std::shared_ptr<T> ptr;
|
||||
std::shared_ptr<void> old;
|
||||
{
|
||||
id_aux_finalize(pair.first.get());
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
old = std::move(g_map[get_type<T>()]);
|
||||
ptr = provider();
|
||||
|
||||
g_map[get_type<T>()] = ptr;
|
||||
}
|
||||
|
||||
id_aux_initialize(pair.second.get());
|
||||
return std::move(pair.second);
|
||||
if (old)
|
||||
{
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(old.get()));
|
||||
}
|
||||
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Get the object unconditionally (create an object if it doesn't exist)
|
||||
template<typename T, typename... Args>
|
||||
std::enable_if_t<std::is_constructible<T, Args...>::value, std::shared_ptr<T>> get_always(Args&&... args)
|
||||
template<typename T, typename Make = T, typename... Args>
|
||||
static std::enable_if_t<std::is_constructible<Make, Args...>::value, std::shared_ptr<T>> get_always(Args&&... args)
|
||||
{
|
||||
auto pair = add<T, false>(WRAP_EXPR(std::make_shared<T>(std::forward<Args>(args)...)));
|
||||
|
||||
if (pair.second)
|
||||
std::shared_ptr<T> ptr;
|
||||
{
|
||||
id_aux_initialize(pair.second.get());
|
||||
return std::move(pair.second);
|
||||
std::lock_guard<shared_mutex> lock(g_mutex);
|
||||
|
||||
if (auto& value = g_map[get_type<T>()])
|
||||
{
|
||||
return{ value, static_cast<T*>(value.get()) };
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = std::make_shared<Make>(std::forward<Args>(args)...);
|
||||
|
||||
g_map[get_type<T>()] = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
return std::move(pair.first);
|
||||
id_manager::on_init<T>::func(ptr.get());
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// Internal
|
||||
bool check(id_type_index_t type);
|
||||
|
||||
// Check whether the object exists
|
||||
template<typename T>
|
||||
bool check()
|
||||
static bool check()
|
||||
{
|
||||
return check(get_id_type_index<T>());
|
||||
}
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
// Internal
|
||||
std::shared_ptr<void> get(id_type_index_t type);
|
||||
return g_map[get_type<T>()].operator bool();
|
||||
}
|
||||
|
||||
// Get the object (returns nullptr if it doesn't exist)
|
||||
template<typename T>
|
||||
std::shared_ptr<T> get()
|
||||
static std::shared_ptr<T> get()
|
||||
{
|
||||
return std::static_pointer_cast<T>(get(get_id_type_index<T>()));
|
||||
}
|
||||
reader_lock lock(g_mutex);
|
||||
|
||||
// Internal
|
||||
std::shared_ptr<void> withdraw(id_type_index_t type);
|
||||
auto& ptr = g_map[get_type<T>()];
|
||||
|
||||
return{ ptr, static_cast<T*>(ptr.get()) };
|
||||
}
|
||||
|
||||
// Delete the object
|
||||
template<typename T>
|
||||
bool remove()
|
||||
static bool remove()
|
||||
{
|
||||
if (auto ptr = withdraw(get_id_type_index<T>()))
|
||||
auto&& ptr = remove(get_type<T>());
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
id_aux_finalize(static_cast<T*>(ptr.get()));
|
||||
return true;
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return false;
|
||||
return ptr.operator bool();
|
||||
}
|
||||
|
||||
// Delete the object and return it
|
||||
template<typename T>
|
||||
std::shared_ptr<T> withdraw()
|
||||
static std::shared_ptr<T> withdraw()
|
||||
{
|
||||
if (auto ptr = std::static_pointer_cast<T>(withdraw(get_id_type_index<T>())))
|
||||
auto&& ptr = remove(get_type<T>());
|
||||
|
||||
if (ptr)
|
||||
{
|
||||
id_aux_finalize(ptr.get());
|
||||
return ptr;
|
||||
id_manager::on_stop<T>::func(static_cast<T*>(ptr.get()));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return{ ptr, static_cast<T*>(ptr.get()) };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,92 +1,62 @@
|
|||
#include "stdafx.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "events.h"
|
||||
#include "state.h"
|
||||
|
||||
#include "Utilities/Config.h"
|
||||
#include "Utilities/AutoPause.h"
|
||||
#include "Utilities/event.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "Emu/GameInfo.h"
|
||||
#include "Emu/SysCalls/ModuleManager.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/PPUCallback.h"
|
||||
#include "Emu/Cell/PPUOpcodes.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/Cell/PPUInstrTable.h"
|
||||
#include "Emu/FS/vfsFile.h"
|
||||
#include "Emu/FS/vfsLocalFile.h"
|
||||
#include "Emu/FS/vfsDeviceLocalFile.h"
|
||||
#include "Emu/Cell/lv2/sys_sync.h"
|
||||
|
||||
#include "Emu/CPU/CPUThreadManager.h"
|
||||
#include "Emu/SysCalls/Callback.h"
|
||||
#include "Emu/IdManager.h"
|
||||
#include "Emu/Io/Pad.h"
|
||||
#include "Emu/Io/Keyboard.h"
|
||||
#include "Emu/Io/Mouse.h"
|
||||
#include "Emu/RSX/GSManager.h"
|
||||
#include "Emu/Audio/AudioManager.h"
|
||||
#include "Emu/FS/VFS.h"
|
||||
#include "Emu/Event.h"
|
||||
#include "Emu/RSX/GSRender.h"
|
||||
|
||||
#include "Loader/PSF.h"
|
||||
#include "Loader/ELF64.h"
|
||||
#include "Loader/ELF32.h"
|
||||
#include "Loader/ELF.h"
|
||||
|
||||
#include "../Crypto/unself.h"
|
||||
|
||||
using namespace PPU_instr;
|
||||
cfg::bool_entry g_cfg_autostart(cfg::root.misc, "Always start after boot");
|
||||
cfg::bool_entry g_cfg_autoexit(cfg::root.misc, "Exit RPCS3 when process finishes");
|
||||
|
||||
static const std::string& BreakPointsDBName = "BreakPoints.dat";
|
||||
static const u16 bpdb_version = 0x1000;
|
||||
std::string g_cfg_defaults;
|
||||
|
||||
// Draft (not used)
|
||||
struct bpdb_header_t
|
||||
{
|
||||
le_t<u32> magic;
|
||||
le_t<u32> version;
|
||||
le_t<u32> count;
|
||||
le_t<u32> marked;
|
||||
extern cfg::string_entry g_cfg_vfs_dev_bdvd;
|
||||
extern cfg::string_entry g_cfg_vfs_app_home;
|
||||
|
||||
// POD
|
||||
bpdb_header_t() = default;
|
||||
|
||||
bpdb_header_t(u32 count, u32 marked)
|
||||
: magic(*reinterpret_cast<const u32*>("BPDB"))
|
||||
, version(0x00010000)
|
||||
, count(count)
|
||||
, marked(marked)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
extern std::atomic<u32> g_thread_count;
|
||||
extern atomic_t<u32> g_thread_count;
|
||||
|
||||
extern u64 get_system_time();
|
||||
extern void finalize_psv_modules();
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
event<void>& on_run() { static event<void> on_run; return on_run; }
|
||||
event<void>& on_stop() { static event<void> on_stop; return on_stop; }
|
||||
event<void>& on_pause() { static event<void> on_pause; return on_pause; }
|
||||
event<void>& on_resume() { static event<void> on_resume; return on_resume; }
|
||||
}
|
||||
|
||||
Emulator::Emulator()
|
||||
: m_status(Stopped)
|
||||
, m_mode(DisAsm)
|
||||
, m_rsx_callback(0)
|
||||
, m_cpu_thr_stop(0)
|
||||
, m_thread_manager(new CPUThreadManager())
|
||||
, m_pad_manager(new PadManager())
|
||||
, m_keyboard_manager(new KeyboardManager())
|
||||
, m_mouse_manager(new MouseManager())
|
||||
, m_gs_manager(new GSManager())
|
||||
, m_audio_manager(new AudioManager())
|
||||
, m_callback_manager(new CallbackManager())
|
||||
, m_event_manager(new EventManager())
|
||||
, m_module_manager(new ModuleManager())
|
||||
, m_vfs(new VFS())
|
||||
{
|
||||
m_loader.register_handler(new loader::handlers::elf32);
|
||||
m_loader.register_handler(new loader::handlers::elf64);
|
||||
}
|
||||
|
||||
void Emulator::Init()
|
||||
{
|
||||
rpcs3::config.load();
|
||||
rpcs3::oninit();
|
||||
idm::init();
|
||||
fxm::init();
|
||||
|
||||
// Reset defaults, cache them
|
||||
cfg::root.from_default();
|
||||
g_cfg_defaults = cfg::root.to_string();
|
||||
|
||||
// Reload global configuration
|
||||
cfg::root.from_string(fs::file(fs::get_config_dir() + "/config.yml", fs::read + fs::create).to_string());
|
||||
}
|
||||
|
||||
void Emulator::SetPath(const std::string& path, const std::string& elf_path)
|
||||
|
@ -95,31 +65,6 @@ void Emulator::SetPath(const std::string& path, const std::string& elf_path)
|
|||
m_elf_path = elf_path;
|
||||
}
|
||||
|
||||
void Emulator::SetTitleID(const std::string& id)
|
||||
{
|
||||
m_title_id = id;
|
||||
}
|
||||
|
||||
void Emulator::SetTitle(const std::string& title)
|
||||
{
|
||||
m_title = title;
|
||||
}
|
||||
|
||||
void Emulator::CreateConfig(const std::string& name)
|
||||
{
|
||||
const std::string& path = fs::get_config_dir() + "data/" + name;
|
||||
const std::string& ini_file = path + "/settings.ini";
|
||||
|
||||
if (!fs::is_dir("data"))
|
||||
fs::create_dir("data");
|
||||
|
||||
if (!fs::is_dir(path))
|
||||
fs::create_dir(path);
|
||||
|
||||
if (!fs::is_file(ini_file))
|
||||
rpcs3::config_t{ ini_file }.save();
|
||||
}
|
||||
|
||||
bool Emulator::BootGame(const std::string& path, bool direct)
|
||||
{
|
||||
static const char* boot_list[] =
|
||||
|
@ -158,141 +103,161 @@ bool Emulator::BootGame(const std::string& path, bool direct)
|
|||
|
||||
void Emulator::Load()
|
||||
{
|
||||
m_status = Ready;
|
||||
Stop();
|
||||
|
||||
if (!fs::is_file(m_path))
|
||||
try
|
||||
{
|
||||
m_status = Stopped;
|
||||
return;
|
||||
}
|
||||
Init();
|
||||
|
||||
const std::string& elf_dir = fs::get_parent_dir(m_path);
|
||||
|
||||
if (IsSelf(m_path))
|
||||
{
|
||||
const std::size_t elf_ext_pos = m_path.find_last_of('.');
|
||||
const std::string& elf_ext = fmt::toupper(m_path.substr(elf_ext_pos != -1 ? elf_ext_pos : m_path.size()));
|
||||
const std::string& elf_name = m_path.substr(elf_dir.size());
|
||||
|
||||
if (elf_name.compare(elf_name.find_last_of("/\\", -1, 2) + 1, 9, "EBOOT.BIN", 9) == 0)
|
||||
if (!fs::is_file(m_path))
|
||||
{
|
||||
m_path.erase(m_path.size() - 9, 1); // change EBOOT.BIN to BOOT.BIN
|
||||
LOG_ERROR(LOADER, "File not found: %s", m_path);
|
||||
return;
|
||||
}
|
||||
else if (elf_ext == ".SELF" || elf_ext == ".SPRX")
|
||||
|
||||
const std::string& elf_dir = fs::get_parent_dir(m_path);
|
||||
|
||||
if (IsSelf(m_path))
|
||||
{
|
||||
m_path.erase(m_path.size() - 4, 1); // change *.self to *.elf, *.sprx to *.prx
|
||||
const std::size_t elf_ext_pos = m_path.find_last_of('.');
|
||||
const std::string& elf_ext = fmt::to_upper(m_path.substr(elf_ext_pos != -1 ? elf_ext_pos : m_path.size()));
|
||||
const std::string& elf_name = m_path.substr(elf_dir.size());
|
||||
|
||||
if (elf_name.compare(elf_name.find_last_of("/\\", -1, 2) + 1, 9, "EBOOT.BIN", 9) == 0)
|
||||
{
|
||||
m_path.erase(m_path.size() - 9, 1); // change EBOOT.BIN to BOOT.BIN
|
||||
}
|
||||
else if (elf_ext == ".SELF" || elf_ext == ".SPRX")
|
||||
{
|
||||
m_path.erase(m_path.size() - 4, 1); // change *.self to *.elf, *.sprx to *.prx
|
||||
}
|
||||
else
|
||||
{
|
||||
m_path += ".decrypted.elf";
|
||||
}
|
||||
|
||||
if (!DecryptSelf(m_path, elf_dir + elf_name))
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to decrypt %s", elf_dir + elf_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ResetInfo();
|
||||
|
||||
LOG_NOTICE(LOADER, "Path: %s", m_path);
|
||||
|
||||
// Load custom config
|
||||
if (fs::file cfg_file{ m_path + ".yml" })
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Custom config: %s.yml", m_path);
|
||||
cfg::root.from_string(cfg_file.to_string());
|
||||
}
|
||||
|
||||
const fs::file elf_file(m_path);
|
||||
ppu_exec_loader ppu_exec;
|
||||
ppu_prx_loader ppu_prx;
|
||||
spu_exec_loader spu_exec;
|
||||
arm_exec_loader arm_exec;
|
||||
|
||||
if (!elf_file)
|
||||
{
|
||||
LOG_ERROR(LOADER, "Failed to open %s", m_path);
|
||||
return;
|
||||
}
|
||||
else if (ppu_exec.open(elf_file) == elf_error::ok)
|
||||
{
|
||||
// PS3 executable
|
||||
m_status = Ready;
|
||||
vm::ps3::init();
|
||||
|
||||
if (m_elf_path.empty())
|
||||
{
|
||||
m_elf_path = "/host_root/" + m_path;
|
||||
LOG_NOTICE(LOADER, "Elf path: %s", m_elf_path);
|
||||
}
|
||||
|
||||
// Load PARAM.SFO
|
||||
m_psf = psf::load_object(fs::file(elf_dir + "/../PARAM.SFO"));
|
||||
m_title = psf::get_string(m_psf, "TITLE", m_path);
|
||||
m_title_id = psf::get_string(m_psf, "TITLE_ID");
|
||||
LOG_NOTICE(LOADER, "Title: %s", GetTitle());
|
||||
LOG_NOTICE(LOADER, "Serial: %s", GetTitleID());
|
||||
LOG_NOTICE(LOADER, "");
|
||||
|
||||
LOG_NOTICE(LOADER, "Used configuration:\n%s\n", cfg::root.to_string());
|
||||
|
||||
// Mount /dev_bdvd/
|
||||
if (g_cfg_vfs_dev_bdvd.size() == 0 && fs::is_file(elf_dir + "/../../PS3_DISC.SFB"))
|
||||
{
|
||||
const auto dir_list = fmt::split(elf_dir, { "/", "\\" });
|
||||
|
||||
// Check latest two directories
|
||||
if (dir_list.size() >= 2 && dir_list.back() == "USRDIR" && *(dir_list.end() - 2) == "PS3_GAME")
|
||||
{
|
||||
g_cfg_vfs_dev_bdvd = elf_dir.substr(0, elf_dir.length() - 15);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_cfg_vfs_dev_bdvd = elf_dir + "/../../";
|
||||
}
|
||||
}
|
||||
|
||||
// Mount /app_home/
|
||||
if (g_cfg_vfs_app_home.size() == 0)
|
||||
{
|
||||
g_cfg_vfs_app_home = elf_dir + '/';
|
||||
}
|
||||
|
||||
vfs::dump();
|
||||
|
||||
ppu_exec.load();
|
||||
|
||||
Emu.GetCallbackManager().Init();
|
||||
fxm::import<GSRender>(PURE_EXPR(Emu.GetCallbacks().get_gs_render())); // TODO: must be created in appropriate sys_rsx syscall
|
||||
}
|
||||
else if (ppu_prx.open(elf_file) == elf_error::ok)
|
||||
{
|
||||
// PPU PRX (experimental)
|
||||
m_status = Ready;
|
||||
vm::ps3::init();
|
||||
ppu_prx.load();
|
||||
GetCallbackManager().Init();
|
||||
}
|
||||
else if (spu_exec.open(elf_file) == elf_error::ok)
|
||||
{
|
||||
// SPU executable (experimental)
|
||||
m_status = Ready;
|
||||
vm::ps3::init();
|
||||
spu_exec.load();
|
||||
}
|
||||
else if (arm_exec.open(elf_file) == elf_error::ok)
|
||||
{
|
||||
// ARMv7 executable
|
||||
m_status = Ready;
|
||||
vm::psv::init();
|
||||
arm_exec.load();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_path += ".decrypted.elf";
|
||||
}
|
||||
LOG_ERROR(LOADER, "Invalid or unsupported file format: %s", m_path);
|
||||
|
||||
if (!DecryptSelf(m_path, elf_dir + elf_name))
|
||||
{
|
||||
m_status = Stopped;
|
||||
LOG_WARNING(LOADER, "** ppu_exec_loader -> %s", bijective_find<elf_error>(ppu_exec, "???"));
|
||||
LOG_WARNING(LOADER, "** ppu_prx_loader -> %s", bijective_find<elf_error>(ppu_prx, "???"));
|
||||
LOG_WARNING(LOADER, "** spu_exec_loader -> %s", bijective_find<elf_error>(spu_exec, "???"));
|
||||
LOG_WARNING(LOADER, "** arm_exec_loader -> %s", bijective_find<elf_error>(arm_exec, "???"));
|
||||
return;
|
||||
}
|
||||
|
||||
debug::autopause::reload();
|
||||
SendDbgCommand(DID_READY_EMU);
|
||||
if (g_cfg_autostart) Run();
|
||||
}
|
||||
|
||||
ResetInfo();
|
||||
GetVFS().Init(elf_dir);
|
||||
|
||||
LOG_NOTICE(LOADER, "Loading '%s'...", m_path.c_str());
|
||||
|
||||
// /dev_bdvd/ mounting
|
||||
vfsFile f("/app_home/../dev_bdvd.path");
|
||||
if (f.IsOpened())
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// load specified /dev_bdvd/ directory and mount it
|
||||
std::string bdvd;
|
||||
bdvd.resize(f.GetSize());
|
||||
f.Read(&bdvd[0], bdvd.size());
|
||||
|
||||
Emu.GetVFS().Mount("/dev_bdvd/", bdvd, new vfsDeviceLocalFile());
|
||||
LOG_FATAL(LOADER, "%s thrown: %s", typeid(e).name(), e.what());
|
||||
Stop();
|
||||
}
|
||||
else if (fs::is_file(elf_dir + "/../../PS3_DISC.SFB")) // guess loading disc game
|
||||
{
|
||||
const auto dir_list = fmt::split(elf_dir, { "/", "\\" });
|
||||
|
||||
// check latest two directories
|
||||
if (dir_list.size() >= 2 && dir_list.back() == "USRDIR" && *(dir_list.end() - 2) == "PS3_GAME")
|
||||
{
|
||||
// mount detected /dev_bdvd/ directory
|
||||
Emu.GetVFS().Mount("/dev_bdvd/", elf_dir.substr(0, elf_dir.length() - 16), new vfsDeviceLocalFile());
|
||||
}
|
||||
}
|
||||
|
||||
LOG_NOTICE(LOADER, "");
|
||||
LOG_NOTICE(LOADER, "Mount info:");
|
||||
for (uint i = 0; i < GetVFS().m_devices.size(); ++i)
|
||||
{
|
||||
LOG_NOTICE(LOADER, "%s -> %s", GetVFS().m_devices[i]->GetPs3Path().c_str(), GetVFS().m_devices[i]->GetLocalPath().c_str());
|
||||
}
|
||||
|
||||
LOG_NOTICE(LOADER, "");
|
||||
f.Open("/app_home/../PARAM.SFO");
|
||||
const auto& psf = psf::load(f.VRead<char>());
|
||||
std::string title = psf::get_string(psf, "TITLE");
|
||||
std::string title_id = psf::get_string(psf, "TITLE_ID");
|
||||
LOG_NOTICE(LOADER, "Title: %s", title.c_str());
|
||||
LOG_NOTICE(LOADER, "Serial: %s", title_id.c_str());
|
||||
|
||||
title.length() ? SetTitle(title) : SetTitle(m_path);
|
||||
SetTitleID(title_id);
|
||||
|
||||
rpcs3::state.config = rpcs3::config;
|
||||
|
||||
// load custom config
|
||||
if (!rpcs3::config.misc.use_default_ini.value())
|
||||
{
|
||||
if (title_id.size())
|
||||
{
|
||||
title_id = title_id.substr(0, 4) + "-" + title_id.substr(4, 5);
|
||||
CreateConfig(title_id);
|
||||
rpcs3::config_t custom_config { fs::get_config_dir() + "data/" + title_id + "/settings.ini" };
|
||||
custom_config.load();
|
||||
rpcs3::state.config = custom_config;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_NOTICE(LOADER, "Used configuration: '%s'", rpcs3::state.config.path().c_str());
|
||||
LOG_NOTICE(LOADER, "");
|
||||
LOG_NOTICE(LOADER, rpcs3::state.config.to_string().c_str());
|
||||
|
||||
if (m_elf_path.empty())
|
||||
{
|
||||
GetVFS().GetDeviceLocal(m_path, m_elf_path);
|
||||
|
||||
LOG_NOTICE(LOADER, "Elf path: %s", m_elf_path);
|
||||
LOG_NOTICE(LOADER, "");
|
||||
}
|
||||
|
||||
f.Open(m_elf_path);
|
||||
|
||||
if (!f.IsOpened())
|
||||
{
|
||||
LOG_ERROR(LOADER, "Opening '%s' failed", m_path.c_str());
|
||||
m_status = Stopped;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_loader.load(f))
|
||||
{
|
||||
LOG_ERROR(LOADER, "Loading '%s' failed", m_path.c_str());
|
||||
LOG_NOTICE(LOADER, "");
|
||||
m_status = Stopped;
|
||||
vm::close();
|
||||
return;
|
||||
}
|
||||
|
||||
LoadPoints(fs::get_config_dir() + BreakPointsDBName);
|
||||
|
||||
GetGSManager().Init();
|
||||
GetCallbackManager().Init();
|
||||
GetAudioManager().Init();
|
||||
GetEventManager().Init();
|
||||
|
||||
SendDbgCommand(DID_READY_EMU);
|
||||
}
|
||||
|
||||
void Emulator::Run()
|
||||
|
@ -311,7 +276,7 @@ void Emulator::Run()
|
|||
return;
|
||||
}
|
||||
|
||||
rpcs3::onstart();
|
||||
rpcs3::on_run()();
|
||||
|
||||
SendDbgCommand(DID_START_EMU);
|
||||
|
||||
|
@ -319,7 +284,12 @@ void Emulator::Run()
|
|||
m_pause_amend_time = 0;
|
||||
m_status = Running;
|
||||
|
||||
GetCPU().Exec();
|
||||
for (auto& thread : get_all_cpu_threads())
|
||||
{
|
||||
thread->state -= cpu_state::stop;
|
||||
thread->safe_notify();
|
||||
}
|
||||
|
||||
SendDbgCommand(DID_STARTED_EMU);
|
||||
}
|
||||
|
||||
|
@ -327,15 +297,15 @@ bool Emulator::Pause()
|
|||
{
|
||||
const u64 start = get_system_time();
|
||||
|
||||
// try to set Paused status
|
||||
if (!sync_bool_compare_and_swap(&m_status, Running, Paused))
|
||||
// Try to pause
|
||||
if (!m_status.compare_and_swap_test(Running, Paused))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rpcs3::onpause();
|
||||
rpcs3::on_pause()();
|
||||
|
||||
// update pause start time
|
||||
// Update pause start time
|
||||
if (m_pause_start_time.exchange(start))
|
||||
{
|
||||
LOG_ERROR(GENERAL, "Emulator::Pause() error: concurrent access");
|
||||
|
@ -343,9 +313,9 @@ bool Emulator::Pause()
|
|||
|
||||
SendDbgCommand(DID_PAUSE_EMU);
|
||||
|
||||
for (auto& t : GetCPU().GetAllThreads())
|
||||
for (auto& thread : get_all_cpu_threads())
|
||||
{
|
||||
t->sleep(); // trigger status check
|
||||
thread->state += cpu_state::dbg_global_pause;
|
||||
}
|
||||
|
||||
SendDbgCommand(DID_PAUSED_EMU);
|
||||
|
@ -355,17 +325,17 @@ bool Emulator::Pause()
|
|||
|
||||
void Emulator::Resume()
|
||||
{
|
||||
// get pause start time
|
||||
// Get pause start time
|
||||
const u64 time = m_pause_start_time.exchange(0);
|
||||
|
||||
// try to increment summary pause time
|
||||
// Try to increment summary pause time
|
||||
if (time)
|
||||
{
|
||||
m_pause_amend_time += get_system_time() - time;
|
||||
}
|
||||
|
||||
// try to resume
|
||||
if (!sync_bool_compare_and_swap(&m_status, Paused, Running))
|
||||
// Try to resume
|
||||
if (!m_status.compare_and_swap_test(Paused, Running))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -377,41 +347,36 @@ void Emulator::Resume()
|
|||
|
||||
SendDbgCommand(DID_RESUME_EMU);
|
||||
|
||||
for (auto& t : GetCPU().GetAllThreads())
|
||||
for (auto& thread : get_all_cpu_threads())
|
||||
{
|
||||
t->awake(); // untrigger status check and signal
|
||||
thread->state -= cpu_state::dbg_global_pause;
|
||||
thread->safe_notify();
|
||||
}
|
||||
|
||||
rpcs3::onstart();
|
||||
rpcs3::on_resume()();
|
||||
|
||||
SendDbgCommand(DID_RESUMED_EMU);
|
||||
}
|
||||
|
||||
extern std::map<u32, std::string> g_armv7_dump;
|
||||
|
||||
void Emulator::Stop()
|
||||
{
|
||||
LOG_NOTICE(GENERAL, "Stopping emulator...");
|
||||
|
||||
if (sync_lock_test_and_set(&m_status, Stopped) == Stopped)
|
||||
if (m_status.exchange(Stopped) == Stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rpcs3::onstop();
|
||||
LOG_NOTICE(GENERAL, "Stopping emulator...");
|
||||
|
||||
rpcs3::on_stop()();
|
||||
SendDbgCommand(DID_STOP_EMU);
|
||||
|
||||
{
|
||||
LV2_LOCK;
|
||||
|
||||
// notify all threads
|
||||
for (auto& t : GetCPU().GetAllThreads())
|
||||
for (auto& thread : get_all_cpu_threads())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(t->mutex);
|
||||
|
||||
t->sleep(); // trigger status check
|
||||
|
||||
t->cv.notify_one(); // signal
|
||||
thread->state += cpu_state::dbg_global_stop;
|
||||
thread->safe_notify();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,86 +396,34 @@ void Emulator::Stop()
|
|||
|
||||
LOG_NOTICE(GENERAL, "Objects cleared...");
|
||||
|
||||
finalize_psv_modules();
|
||||
|
||||
for (auto& v : decltype(g_armv7_dump)(std::move(g_armv7_dump)))
|
||||
{
|
||||
LOG_NOTICE(ARMv7, "%s", v.second);
|
||||
}
|
||||
|
||||
m_rsx_callback = 0;
|
||||
m_cpu_thr_stop = 0;
|
||||
|
||||
// TODO: check finalization order
|
||||
|
||||
SavePoints(fs::get_config_dir() + BreakPointsDBName);
|
||||
m_break_points.clear();
|
||||
m_marked_points.clear();
|
||||
|
||||
GetVFS().UnMountAll();
|
||||
|
||||
GetGSManager().Close();
|
||||
GetAudioManager().Close();
|
||||
GetEventManager().Clear();
|
||||
GetCPU().Close();
|
||||
GetPadManager().Close();
|
||||
GetKeyboardManager().Close();
|
||||
GetMouseManager().Close();
|
||||
GetCallbackManager().Clear();
|
||||
GetModuleManager().Close();
|
||||
|
||||
RSXIOMem.Clear();
|
||||
vm::close();
|
||||
|
||||
SendDbgCommand(DID_STOPPED_EMU);
|
||||
}
|
||||
|
||||
void Emulator::SavePoints(const std::string& path)
|
||||
{
|
||||
const u32 break_count = size32(m_break_points);
|
||||
const u32 marked_count = size32(m_marked_points);
|
||||
|
||||
fs::file(path, fom::rewrite)
|
||||
.write(bpdb_version)
|
||||
.write(break_count)
|
||||
.write(marked_count)
|
||||
.write(m_break_points)
|
||||
.write(m_marked_points);
|
||||
}
|
||||
|
||||
bool Emulator::LoadPoints(const std::string& path)
|
||||
{
|
||||
if (fs::file f{ path })
|
||||
if (g_cfg_autoexit)
|
||||
{
|
||||
u16 version;
|
||||
u32 break_count;
|
||||
u32 marked_count;
|
||||
|
||||
if (!f.read(version) || !f.read(break_count) || !f.read(marked_count))
|
||||
{
|
||||
LOG_ERROR(LOADER, "BP file '%s' is broken (length=0x%llx)", path, f.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (version != bpdb_version)
|
||||
{
|
||||
LOG_ERROR(LOADER, "BP file '%s' has unsupported version (version=0x%x)", path, version);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_break_points.resize(break_count);
|
||||
m_marked_points.resize(marked_count);
|
||||
|
||||
if (!f.read(m_break_points) || !f.read(m_marked_points))
|
||||
{
|
||||
LOG_ERROR(LOADER, "'BP file %s' is broken (length=0x%llx, break_count=%u, marked_count=%u)", path, f.size(), break_count, marked_count);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
GetCallbacks().exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Emulator Emu;
|
||||
|
||||
DECLARE(idm::g_map);
|
||||
DECLARE(idm::g_id);
|
||||
DECLARE(idm::g_mutex);
|
||||
|
||||
DECLARE(fxm::g_map);
|
||||
DECLARE(fxm::g_mutex);
|
||||
|
||||
#ifndef _MSC_VER
|
||||
constexpr std::pair<elf_error, const char*> bijective<elf_error, const char*>::map[];
|
||||
constexpr std::pair<_log::level, const char*> bijective<_log::level, const char*>::map[];
|
||||
constexpr std::pair<rsx::shader_language, const char*> bijective<rsx::shader_language, const char*>::map[];
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "Loader/Loader.h"
|
||||
#include "VFS.h"
|
||||
#include "DbgCommand.h"
|
||||
#include "Loader/PSF.h"
|
||||
|
||||
enum class frame_type;
|
||||
|
||||
|
@ -9,11 +10,12 @@ struct EmuCallbacks
|
|||
{
|
||||
std::function<void(std::function<void()>)> call_after;
|
||||
std::function<void()> process_events;
|
||||
std::function<void(DbgCommand, class CPUThread*)> send_dbg_command;
|
||||
std::function<std::unique_ptr<class KeyboardHandlerBase>()> get_kb_handler;
|
||||
std::function<std::unique_ptr<class MouseHandlerBase>()> get_mouse_handler;
|
||||
std::function<std::unique_ptr<class PadHandlerBase>()> get_pad_handler;
|
||||
std::function<std::unique_ptr<class GSFrameBase>(frame_type)> get_gs_frame;
|
||||
std::function<void()> exit;
|
||||
std::function<void(DbgCommand, class cpu_thread*)> send_dbg_command;
|
||||
std::function<std::shared_ptr<class KeyboardHandlerBase>()> get_kb_handler;
|
||||
std::function<std::shared_ptr<class MouseHandlerBase>()> get_mouse_handler;
|
||||
std::function<std::shared_ptr<class PadHandlerBase>()> get_pad_handler;
|
||||
std::function<std::unique_ptr<class GSFrameBase>(frame_type, size2i)> get_gs_frame;
|
||||
std::function<std::shared_ptr<class GSRender>()> get_gs_render;
|
||||
std::function<std::shared_ptr<class AudioThread>()> get_audio;
|
||||
std::function<std::shared_ptr<class MsgDialogBase>()> get_msg_dialog;
|
||||
|
@ -31,17 +33,7 @@ enum Status : u32
|
|||
// Emulation Stopped exception event
|
||||
class EmulationStopped {};
|
||||
|
||||
class CPUThreadManager;
|
||||
class PadManager;
|
||||
class KeyboardManager;
|
||||
class MouseManager;
|
||||
class GSManager;
|
||||
class AudioManager;
|
||||
class CallbackManager;
|
||||
class CPUThread;
|
||||
class EventManager;
|
||||
class ModuleManager;
|
||||
struct VFS;
|
||||
|
||||
struct EmuInfo
|
||||
{
|
||||
|
@ -64,47 +56,24 @@ public:
|
|||
|
||||
class Emulator final
|
||||
{
|
||||
atomic_t<u32> m_status;
|
||||
|
||||
EmuCallbacks m_cb;
|
||||
|
||||
enum Mode
|
||||
{
|
||||
DisAsm,
|
||||
InterpreterDisAsm,
|
||||
Interpreter,
|
||||
};
|
||||
|
||||
volatile u32 m_status;
|
||||
uint m_mode;
|
||||
atomic_t<u64> m_pause_start_time; // set when paused
|
||||
atomic_t<u64> m_pause_amend_time; // increased when resumed
|
||||
|
||||
std::atomic<u64> m_pause_start_time; // set when paused
|
||||
std::atomic<u64> m_pause_amend_time; // increased when resumed
|
||||
|
||||
u32 m_rsx_callback;
|
||||
u32 m_cpu_thr_stop;
|
||||
|
||||
std::vector<u64> m_break_points;
|
||||
std::vector<u64> m_marked_points;
|
||||
|
||||
std::mutex m_core_mutex;
|
||||
|
||||
std::unique_ptr<CPUThreadManager> m_thread_manager;
|
||||
std::unique_ptr<PadManager> m_pad_manager;
|
||||
std::unique_ptr<KeyboardManager> m_keyboard_manager;
|
||||
std::unique_ptr<MouseManager> m_mouse_manager;
|
||||
std::unique_ptr<GSManager> m_gs_manager;
|
||||
std::unique_ptr<AudioManager> m_audio_manager;
|
||||
std::unique_ptr<CallbackManager> m_callback_manager;
|
||||
std::unique_ptr<EventManager> m_event_manager;
|
||||
std::unique_ptr<ModuleManager> m_module_manager;
|
||||
std::unique_ptr<VFS> m_vfs;
|
||||
|
||||
EmuInfo m_info;
|
||||
loader::loader m_loader;
|
||||
|
||||
std::string m_path;
|
||||
std::string m_elf_path;
|
||||
std::string m_title_id;
|
||||
std::string m_title;
|
||||
psf::registry m_psf;
|
||||
|
||||
public:
|
||||
Emulator();
|
||||
|
@ -119,36 +88,15 @@ public:
|
|||
return m_cb;
|
||||
}
|
||||
|
||||
void SendDbgCommand(DbgCommand cmd, class CPUThread* thread = nullptr)
|
||||
void SendDbgCommand(DbgCommand cmd, class cpu_thread* thread = nullptr)
|
||||
{
|
||||
if (m_cb.send_dbg_command) m_cb.send_dbg_command(cmd, thread);
|
||||
}
|
||||
|
||||
// Returns a future object associated with the result of the function called from the GUI thread
|
||||
template<typename F>
|
||||
std::future<void> CallAfter(F&& func) const
|
||||
// Call from the GUI thread
|
||||
void CallAfter(std::function<void()>&& func) const
|
||||
{
|
||||
// Make "shared" promise to workaround std::function limitation
|
||||
auto spr = std::make_shared<std::promise<void>>();
|
||||
|
||||
// Get future
|
||||
std::future<void> future = spr->get_future();
|
||||
|
||||
// Run asynchronously in GUI thread
|
||||
m_cb.call_after([spr = std::move(spr), task = std::forward<F>(func)]()
|
||||
{
|
||||
try
|
||||
{
|
||||
task();
|
||||
spr->set_value();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
spr->set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
return future;
|
||||
return m_cb.call_after(std::move(func));
|
||||
}
|
||||
|
||||
/** Set emulator mode to running unconditionnaly.
|
||||
|
@ -160,10 +108,7 @@ public:
|
|||
}
|
||||
|
||||
void Init();
|
||||
void SetPath(const std::string& path, const std::string& elf_path = "");
|
||||
void SetTitleID(const std::string& id);
|
||||
void SetTitle(const std::string& title);
|
||||
void CreateConfig(const std::string& name);
|
||||
void SetPath(const std::string& path, const std::string& elf_path = {});
|
||||
|
||||
const std::string& GetPath() const
|
||||
{
|
||||
|
@ -180,24 +125,20 @@ public:
|
|||
return m_title;
|
||||
}
|
||||
|
||||
const psf::registry& GetPSF() const
|
||||
{
|
||||
return m_psf;
|
||||
}
|
||||
|
||||
u64 GetPauseTime()
|
||||
{
|
||||
return m_pause_amend_time;
|
||||
}
|
||||
|
||||
std::mutex& GetCoreMutex() { return m_core_mutex; }
|
||||
CPUThreadManager& GetCPU() { return *m_thread_manager; }
|
||||
PadManager& GetPadManager() { return *m_pad_manager; }
|
||||
KeyboardManager& GetKeyboardManager() { return *m_keyboard_manager; }
|
||||
MouseManager& GetMouseManager() { return *m_mouse_manager; }
|
||||
GSManager& GetGSManager() { return *m_gs_manager; }
|
||||
AudioManager& GetAudioManager() { return *m_audio_manager; }
|
||||
CallbackManager& GetCallbackManager() { return *m_callback_manager; }
|
||||
VFS& GetVFS() { return *m_vfs; }
|
||||
std::vector<u64>& GetBreakPoints() { return m_break_points; }
|
||||
std::vector<u64>& GetMarkedPoints() { return m_marked_points; }
|
||||
EventManager& GetEventManager() { return *m_event_manager; }
|
||||
ModuleManager& GetModuleManager() { return *m_module_manager; }
|
||||
CallbackManager& GetCallbackManager()
|
||||
{
|
||||
return *m_callback_manager;
|
||||
}
|
||||
|
||||
void ResetInfo()
|
||||
{
|
||||
|
@ -219,11 +160,6 @@ public:
|
|||
m_info.m_primary_prio = prio;
|
||||
}
|
||||
|
||||
void SetRSXCallback(u32 addr)
|
||||
{
|
||||
m_rsx_callback = addr;
|
||||
}
|
||||
|
||||
void SetCPUThreadStop(u32 addr)
|
||||
{
|
||||
m_cpu_thr_stop = addr;
|
||||
|
@ -238,7 +174,6 @@ public:
|
|||
u32 GetPrimaryStackSize() { return m_info.m_primary_stacksize; }
|
||||
s32 GetPrimaryPrio() { return m_info.m_primary_prio; }
|
||||
|
||||
u32 GetRSXCallback() const { return m_rsx_callback; }
|
||||
u32 GetCPUThreadStop() const { return m_cpu_thr_stop; }
|
||||
|
||||
bool BootGame(const std::string& path, bool direct = false);
|
||||
|
@ -249,9 +184,6 @@ public:
|
|||
void Resume();
|
||||
void Stop();
|
||||
|
||||
void SavePoints(const std::string& path);
|
||||
bool LoadPoints(const std::string& path);
|
||||
|
||||
force_inline bool IsRunning() const { return m_status == Running; }
|
||||
force_inline bool IsPaused() const { return m_status == Paused; }
|
||||
force_inline bool IsStopped() const { return m_status == Stopped; }
|
||||
|
@ -260,14 +192,4 @@ public:
|
|||
|
||||
extern Emulator Emu;
|
||||
|
||||
using lv2_lock_t = std::unique_lock<std::mutex>;
|
||||
|
||||
inline bool check_lv2_lock(lv2_lock_t& lv2_lock)
|
||||
{
|
||||
return lv2_lock.owns_lock() && lv2_lock.mutex() == &Emu.GetCoreMutex();
|
||||
}
|
||||
|
||||
#define LV2_LOCK lv2_lock_t lv2_lock(Emu.GetCoreMutex())
|
||||
#define LV2_DEFER_LOCK lv2_lock_t lv2_lock
|
||||
#define CHECK_LV2_LOCK(x) if (!check_lv2_lock(x)) throw EXCEPTION("lv2_lock is invalid or not locked")
|
||||
#define CHECK_EMU_STATUS if (Emu.IsStopped()) throw EmulationStopped{}
|
||||
|
|
77
rpcs3/Emu/VFS.cpp
Normal file
77
rpcs3/Emu/VFS.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
#include "stdafx.h"
|
||||
#include "Utilities/Config.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include "VFS.h"
|
||||
|
||||
cfg::string_entry g_cfg_vfs_emulator_dir(cfg::root.vfs, "$(EmulatorDir)"); // Default (empty): taken from fs::get_executable_dir()
|
||||
cfg::string_entry g_cfg_vfs_dev_hdd0(cfg::root.vfs, "/dev_hdd0/", "$(EmulatorDir)dev_hdd0/");
|
||||
cfg::string_entry g_cfg_vfs_dev_hdd1(cfg::root.vfs, "/dev_hdd1/", "$(EmulatorDir)dev_hdd1/");
|
||||
cfg::string_entry g_cfg_vfs_dev_flash(cfg::root.vfs, "/dev_flash/", "$(EmulatorDir)dev_flash/");
|
||||
cfg::string_entry g_cfg_vfs_dev_usb000(cfg::root.vfs, "/dev_usb000/", "$(EmulatorDir)dev_usb000/");
|
||||
cfg::string_entry g_cfg_vfs_dev_bdvd(cfg::root.vfs, "/dev_bdvd/"); // Not mounted
|
||||
cfg::string_entry g_cfg_vfs_app_home(cfg::root.vfs, "/app_home/"); // Not mounted
|
||||
|
||||
cfg::bool_entry g_cfg_vfs_allow_host_root(cfg::root.vfs, "Enable /host_root/", true);
|
||||
|
||||
void vfs::dump()
|
||||
{
|
||||
LOG_NOTICE(LOADER, "Mount info:");
|
||||
LOG_NOTICE(LOADER, "/dev_hdd0/ -> %s", g_cfg_vfs_dev_hdd0.get());
|
||||
LOG_NOTICE(LOADER, "/dev_hdd1/ -> %s", g_cfg_vfs_dev_hdd1.get());
|
||||
LOG_NOTICE(LOADER, "/dev_flash/ -> %s", g_cfg_vfs_dev_flash.get());
|
||||
LOG_NOTICE(LOADER, "/dev_usb/ -> %s", g_cfg_vfs_dev_usb000.get());
|
||||
LOG_NOTICE(LOADER, "/dev_usb000/ -> %s", g_cfg_vfs_dev_usb000.get());
|
||||
if (g_cfg_vfs_dev_bdvd.size()) LOG_NOTICE(LOADER, "/dev_bdvd/ -> %s", g_cfg_vfs_dev_bdvd.get());
|
||||
if (g_cfg_vfs_app_home.size()) LOG_NOTICE(LOADER, "/app_home/ -> %s", g_cfg_vfs_app_home.get());
|
||||
if (g_cfg_vfs_allow_host_root) LOG_NOTICE(LOADER, "/host_root/ -> .");
|
||||
LOG_NOTICE(LOADER, "");
|
||||
}
|
||||
|
||||
std::string vfs::get(const std::string& vpath)
|
||||
{
|
||||
const cfg::string_entry* vdir = nullptr;
|
||||
std::size_t f_pos = vpath.find_first_not_of('/');
|
||||
std::size_t start = 0;
|
||||
|
||||
// Compare vpath with device name
|
||||
auto detect = [&](const auto& vdev) -> bool
|
||||
{
|
||||
const std::size_t size = ::size32(vdev) - 1; // Char array size
|
||||
|
||||
if (f_pos && f_pos != -1 && vpath.compare(f_pos - 1, size, vdev, size) == 0)
|
||||
{
|
||||
start = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (g_cfg_vfs_allow_host_root && detect("/host_root/"))
|
||||
return vpath.substr(start); // Accessing host FS directly
|
||||
else if (detect("/dev_hdd0/"))
|
||||
vdir = &g_cfg_vfs_dev_hdd0;
|
||||
else if (detect("/dev_hdd1/"))
|
||||
vdir = &g_cfg_vfs_dev_hdd1;
|
||||
else if (detect("/dev_flash/"))
|
||||
vdir = &g_cfg_vfs_dev_flash;
|
||||
else if (detect("/dev_usb000/"))
|
||||
vdir = &g_cfg_vfs_dev_usb000;
|
||||
else if (detect("/dev_usb/"))
|
||||
vdir = &g_cfg_vfs_dev_usb000;
|
||||
else if (detect("/dev_bdvd/"))
|
||||
vdir = &g_cfg_vfs_dev_bdvd;
|
||||
else if (detect("/app_home/"))
|
||||
vdir = &g_cfg_vfs_app_home;
|
||||
|
||||
// Return empty path if not mounted
|
||||
if (!vdir || !start)
|
||||
{
|
||||
LOG_WARNING(GENERAL, "vfs::get() failed for %s", vpath);
|
||||
return{};
|
||||
}
|
||||
|
||||
// Replace $(EmulatorDir), concatenate
|
||||
return fmt::replace_all(*vdir, "$(EmulatorDir)", g_cfg_vfs_emulator_dir.size() == 0 ? fs::get_executable_dir() : g_cfg_vfs_emulator_dir) + vpath.substr(start);
|
||||
}
|
10
rpcs3/Emu/VFS.h
Normal file
10
rpcs3/Emu/VFS.h
Normal file
|
@ -0,0 +1,10 @@
|
|||
#pragma once
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
// Print mounted directories
|
||||
void dump();
|
||||
|
||||
// Convert PS3/PSV path to fs-compatible path
|
||||
std::string get(const std::string& vpath);
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "events.h"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
event<void> oninit;
|
||||
event<void> onstart;
|
||||
event<void> onstop;
|
||||
event<void> onpause;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <Utilities/event.h>
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
extern event<void> oninit;
|
||||
extern event<void> onstart;
|
||||
extern event<void> onstop;
|
||||
extern event<void> onpause;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "state.h"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
state_t state;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "config.h"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
struct state_t
|
||||
{
|
||||
config_t config;
|
||||
|
||||
std::string path_to_elf;
|
||||
std::string virtual_path_to_elf;
|
||||
};
|
||||
|
||||
extern state_t state;
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug - LLVM|x64">
|
||||
<Configuration>Debug - LLVM</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug - MemLeak|x64">
|
||||
<Configuration>Debug - MemLeak</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release - LLVM|x64">
|
||||
<Configuration>Release - LLVM</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'">
|
||||
<ClCompile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'">
|
||||
<ClCompile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'">
|
||||
<ClCompile>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
<AdditionalIncludeDirectories>..\llvm\include;..\llvm_build\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Emu\Cell\PPULLVMRecompiler.cpp" />
|
||||
<ClCompile Include="Emu\Cell\PPULLVMRecompilerCore.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Cell\PPULLVMRecompiler.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="emucore.vcxproj">
|
||||
<Project>{c4a10229-4712-4bd2-b63e-50d93c67a038}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{304A6E8B-A311-4EC5-8045-BFA8D08175CE}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>PPULLVMRecompiler</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
<Import Project="..\rpcs3_default.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
<Import Project="..\rpcs3_memleak.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
<Import Project="..\rpcs3_llvm.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release - LLVM|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_release.props" />
|
||||
<Import Project="..\rpcs3_llvm.props" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="Emu\Cell\PPULLVMRecompiler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\PPULLVMRecompilerCore.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Emu\Cell\PPULLVMRecompiler.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup />
|
||||
</Project>
|
|
@ -1,46 +0,0 @@
|
|||
#include "stdafx.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
config_t::config_t(const std::string &path_)
|
||||
{
|
||||
path(path_);
|
||||
}
|
||||
config_t::config_t(const config_t& rhs)
|
||||
{
|
||||
assign(rhs);
|
||||
}
|
||||
|
||||
config_t& config_t::operator =(const config_t& rhs)
|
||||
{
|
||||
assign(rhs);
|
||||
path(rhs.path());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void config_t::path(const std::string &new_path)
|
||||
{
|
||||
m_path = new_path;
|
||||
}
|
||||
|
||||
std::string config_t::path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void config_t::load()
|
||||
{
|
||||
fs::file file(m_path, fom::create | fom::read);
|
||||
|
||||
if (file)
|
||||
from_string(file.to_string());
|
||||
}
|
||||
|
||||
void config_t::save() const
|
||||
{
|
||||
fs::file(m_path, fom::rewrite).write(to_string());
|
||||
}
|
||||
|
||||
config_t config{ fs::get_config_dir() + "rpcs3.new.ini" };
|
||||
}
|
1010
rpcs3/config.h
1010
rpcs3/config.h
File diff suppressed because it is too large
Load diff
175
rpcs3/rpcs3.cpp
175
rpcs3/rpcs3.cpp
|
@ -1,25 +1,24 @@
|
|||
#include "stdafx.h"
|
||||
#include "stdafx_gui.h"
|
||||
#include "rpcs3.h"
|
||||
|
||||
#include "Utilities/Config.h"
|
||||
#include "Emu/Memory/Memory.h"
|
||||
#include "Emu/System.h"
|
||||
#include "Emu/state.h"
|
||||
#include "rpcs3.h"
|
||||
|
||||
#include "Gui/ConLogFrame.h"
|
||||
#include "Emu/GameInfo.h"
|
||||
|
||||
#include "Emu/Io/Keyboard.h"
|
||||
#include "Emu/Io/Null/NullKeyboardHandler.h"
|
||||
#include "Emu/Io/Windows/WindowsKeyboardHandler.h"
|
||||
#include "BasicKeyboardHandler.h"
|
||||
|
||||
#include "Emu/Io/Mouse.h"
|
||||
#include "Emu/Io/Null/NullMouseHandler.h"
|
||||
#include "Emu/Io/Windows/WindowsMouseHandler.h"
|
||||
#include "BasicMouseHandler.h"
|
||||
|
||||
#include "Emu/Io/Pad.h"
|
||||
#include "Emu/Io/Null/NullPadHandler.h"
|
||||
#include "Emu/Io/Windows/WindowsPadHandler.h"
|
||||
#include "KeyboardPadHandler.h"
|
||||
#ifdef _MSC_VER
|
||||
#include "Emu/Io/XInput/XInputPadHandler.h"
|
||||
#include "XInputPadHandler.h"
|
||||
#endif
|
||||
|
||||
#include "Emu/RSX/Null/NullGSRender.h"
|
||||
|
@ -48,11 +47,72 @@
|
|||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
// GUI config
|
||||
YAML::Node g_gui_cfg;
|
||||
|
||||
// GUI config file
|
||||
static fs::file s_gui_cfg;
|
||||
|
||||
void save_gui_cfg()
|
||||
{
|
||||
YAML::Emitter out;
|
||||
out.SetSeqFormat(YAML::Flow);
|
||||
out << g_gui_cfg;
|
||||
|
||||
// Save to file
|
||||
s_gui_cfg.seek(0);
|
||||
s_gui_cfg.trunc(0);
|
||||
s_gui_cfg.write(out.c_str(), out.size());
|
||||
}
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_DBG_COMMAND, wxCommandEvent);
|
||||
|
||||
IMPLEMENT_APP(Rpcs3App)
|
||||
Rpcs3App* TheApp;
|
||||
|
||||
cfg::map_entry<std::function<std::shared_ptr<KeyboardHandlerBase>()>> g_cfg_kb_handler(cfg::root.io, "Keyboard",
|
||||
{
|
||||
{ "Null", PURE_EXPR(std::make_shared<NullKeyboardHandler>()) },
|
||||
{ "Basic", PURE_EXPR(std::make_shared<BasicKeyboardHandler>()) },
|
||||
});
|
||||
|
||||
cfg::map_entry<std::function<std::shared_ptr<MouseHandlerBase>()>> g_cfg_mouse_handler(cfg::root.io, "Mouse",
|
||||
{
|
||||
{ "Null", PURE_EXPR(std::make_shared<NullMouseHandler>()) },
|
||||
{ "Basic", PURE_EXPR(std::make_shared<BasicMouseHandler>()) },
|
||||
});
|
||||
|
||||
cfg::map_entry<std::function<std::shared_ptr<PadHandlerBase>()>> g_cfg_pad_handler(cfg::root.io, "Pad", "Keyboard",
|
||||
{
|
||||
{ "Null", PURE_EXPR(std::make_shared<NullPadHandler>()) },
|
||||
{ "Keyboard", PURE_EXPR(std::make_shared<KeyboardPadHandler>()) },
|
||||
#ifdef _MSC_VER
|
||||
{ "XInput", PURE_EXPR(std::make_shared<XInputPadHandler>()) },
|
||||
#endif
|
||||
});
|
||||
|
||||
cfg::map_entry<std::function<std::shared_ptr<GSRender>()>> g_cfg_gs_render(cfg::root.video, "Renderer", "OpenGL",
|
||||
{
|
||||
{ "Null", PURE_EXPR(std::make_shared<NullGSRender>()) },
|
||||
{ "OpenGL", PURE_EXPR(std::make_shared<GLGSRender>()) },
|
||||
#ifdef _MSC_VER
|
||||
{ "DX12", PURE_EXPR(std::make_shared<D3D12GSRender>()) },
|
||||
{ "Vulkan", PURE_EXPR(std::make_shared<VKGSRender>()) },
|
||||
#endif
|
||||
});
|
||||
|
||||
cfg::map_entry<std::function<std::shared_ptr<AudioThread>()>> g_cfg_audio_render(cfg::root.audio, "Renderer", "OpenAL",
|
||||
{
|
||||
{ "Null", PURE_EXPR(std::make_shared<NullAudioThread>()) },
|
||||
{ "OpenAL", PURE_EXPR(std::make_shared<OpenALThread>()) },
|
||||
#ifdef _MSC_VER
|
||||
{ "XAudio2", PURE_EXPR(std::make_shared<XAudio2Thread>()) },
|
||||
#endif
|
||||
});
|
||||
|
||||
extern cfg::bool_entry g_cfg_autostart;
|
||||
extern cfg::bool_entry g_cfg_autoexit;
|
||||
|
||||
bool Rpcs3App::OnInit()
|
||||
{
|
||||
static const wxCmdLineEntryDesc desc[]
|
||||
|
@ -72,6 +132,9 @@ bool Rpcs3App::OnInit()
|
|||
this->Exit();
|
||||
}
|
||||
|
||||
s_gui_cfg.open(fs::get_config_dir() + "/config_gui.yml", fs::read + fs::write + fs::create);
|
||||
g_gui_cfg = YAML::Load(s_gui_cfg.to_string());
|
||||
|
||||
EmuCallbacks callbacks;
|
||||
|
||||
callbacks.call_after = [](std::function<void()> func)
|
||||
|
@ -85,83 +148,38 @@ bool Rpcs3App::OnInit()
|
|||
wxGetApp().ProcessPendingEvents();
|
||||
};
|
||||
|
||||
callbacks.send_dbg_command = [](DbgCommand id, CPUThread* t)
|
||||
callbacks.exit = [this]()
|
||||
{
|
||||
wxGetApp().Exit();
|
||||
};
|
||||
|
||||
callbacks.send_dbg_command = [](DbgCommand id, cpu_thread* t)
|
||||
{
|
||||
wxGetApp().SendDbgCommand(id, t);
|
||||
};
|
||||
|
||||
callbacks.get_kb_handler = []() -> std::unique_ptr<KeyboardHandlerBase>
|
||||
{
|
||||
switch (auto mode = rpcs3::config.io.keyboard_handler_mode.value())
|
||||
{
|
||||
case io_handler_mode::null: return std::make_unique<NullKeyboardHandler>();
|
||||
case io_handler_mode::windows: return std::make_unique<WindowsKeyboardHandler>();
|
||||
default: throw EXCEPTION("Invalid Keyboard Handler Mode %d", +(u32)mode);
|
||||
}
|
||||
};
|
||||
callbacks.get_kb_handler = PURE_EXPR(g_cfg_kb_handler.get()());
|
||||
|
||||
callbacks.get_mouse_handler = []() -> std::unique_ptr<MouseHandlerBase>
|
||||
{
|
||||
switch (auto mode = rpcs3::config.io.mouse_handler_mode.value())
|
||||
{
|
||||
case io_handler_mode::null: return std::make_unique<NullMouseHandler>();
|
||||
case io_handler_mode::windows: return std::make_unique<WindowsMouseHandler>();
|
||||
default: throw EXCEPTION("Invalid Mouse Handler Mode %d", +(u32)mode);
|
||||
}
|
||||
};
|
||||
callbacks.get_mouse_handler = PURE_EXPR(g_cfg_mouse_handler.get()());
|
||||
|
||||
callbacks.get_pad_handler = []() -> std::unique_ptr<PadHandlerBase>
|
||||
{
|
||||
switch (auto mode = rpcs3::config.io.pad_handler_mode.value())
|
||||
{
|
||||
case io_handler_mode::null: return std::make_unique<NullPadHandler>();
|
||||
case io_handler_mode::windows: return std::make_unique<WindowsPadHandler>();
|
||||
#ifdef _MSC_VER
|
||||
case io_handler_mode::xinput: return std::make_unique<XInputPadHandler>();
|
||||
#endif
|
||||
default: throw EXCEPTION("Invalid Pad Handler Mode %d", (int)mode);
|
||||
}
|
||||
};
|
||||
callbacks.get_pad_handler = PURE_EXPR(g_cfg_pad_handler.get()());
|
||||
|
||||
callbacks.get_gs_frame = [](frame_type type) -> std::unique_ptr<GSFrameBase>
|
||||
callbacks.get_gs_frame = [](frame_type type, size2i size) -> std::unique_ptr<GSFrameBase>
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case frame_type::OpenGL: return std::make_unique<GLGSFrame>();
|
||||
case frame_type::DX12: return std::make_unique<GSFrame>("DirectX 12");
|
||||
case frame_type::Null: return std::make_unique<GSFrame>("Null");
|
||||
case frame_type::Vulkan: return std::make_unique<GSFrame>("Vulkan");
|
||||
case frame_type::OpenGL: return std::make_unique<GLGSFrame>(size);
|
||||
case frame_type::DX12: return std::make_unique<GSFrame>("DirectX 12", size);
|
||||
case frame_type::Null: return std::make_unique<GSFrame>("Null", size);
|
||||
case frame_type::Vulkan: return std::make_unique<GSFrame>("Vulkan", size);
|
||||
}
|
||||
|
||||
throw EXCEPTION("Invalid Frame Type");
|
||||
throw EXCEPTION("Invalid Frame Type (0x%x)", type);
|
||||
};
|
||||
|
||||
callbacks.get_gs_render = []() -> std::shared_ptr<GSRender>
|
||||
{
|
||||
switch (auto mode = rpcs3::state.config.rsx.renderer.value())
|
||||
{
|
||||
case rsx_renderer_type::Null: return std::make_shared<NullGSRender>();
|
||||
case rsx_renderer_type::OpenGL: return std::make_shared<GLGSRender>();
|
||||
#ifdef _MSC_VER
|
||||
case rsx_renderer_type::DX12: return std::make_shared<D3D12GSRender>();
|
||||
case rsx_renderer_type::Vulkan: return std::make_shared<VKGSRender>();
|
||||
#endif
|
||||
default: throw EXCEPTION("Invalid GS Renderer %d", (int)mode);
|
||||
}
|
||||
};
|
||||
callbacks.get_gs_render = PURE_EXPR(g_cfg_gs_render.get()());
|
||||
|
||||
callbacks.get_audio = []() -> std::shared_ptr<AudioThread>
|
||||
{
|
||||
switch (rpcs3::state.config.audio.out.value())
|
||||
{
|
||||
default:
|
||||
case audio_output_type::Null: return std::make_shared<NullAudioThread>();
|
||||
case audio_output_type::OpenAL: return std::make_shared<OpenALThread>();
|
||||
#ifdef _MSC_VER
|
||||
case audio_output_type::XAudio2: return std::make_shared<XAudio2Thread>();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
callbacks.get_audio = PURE_EXPR(g_cfg_audio_render.get()());
|
||||
|
||||
callbacks.get_msg_dialog = []() -> std::shared_ptr<MsgDialogBase>
|
||||
{
|
||||
|
@ -199,13 +217,15 @@ void Rpcs3App::OnArguments(const wxCmdLineParser& parser)
|
|||
|
||||
if (parser.FoundSwitch("t"))
|
||||
{
|
||||
HLEExitOnStop = rpcs3::config.misc.exit_on_stop.value();
|
||||
rpcs3::config.misc.exit_on_stop = true;
|
||||
if (parser.GetParamCount() != 1)
|
||||
{
|
||||
wxLogDebug(wxT("A (S)ELF file needs to be given in test mode, exiting."));
|
||||
wxLogDebug("A (S)ELF file needs to be given in test mode, exiting.");
|
||||
this->Exit();
|
||||
}
|
||||
|
||||
// TODO: clean implementation
|
||||
g_cfg_autostart = true;
|
||||
g_cfg_autoexit = true;
|
||||
}
|
||||
|
||||
if (parser.GetParamCount() > 0)
|
||||
|
@ -218,16 +238,11 @@ void Rpcs3App::OnArguments(const wxCmdLineParser& parser)
|
|||
|
||||
void Rpcs3App::Exit()
|
||||
{
|
||||
if (parser.FoundSwitch("t"))
|
||||
{
|
||||
rpcs3::config.misc.exit_on_stop = HLEExitOnStop;
|
||||
}
|
||||
|
||||
Emu.Stop();
|
||||
wxApp::Exit();
|
||||
}
|
||||
|
||||
void Rpcs3App::SendDbgCommand(DbgCommand id, CPUThread* thr)
|
||||
void Rpcs3App::SendDbgCommand(DbgCommand id, cpu_thread* thr)
|
||||
{
|
||||
wxCommandEvent event(wxEVT_DBG_COMMAND, id);
|
||||
event.SetClientData(thr);
|
||||
|
|
|
@ -1,21 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "Gui/MainFrame.h"
|
||||
#include "Emu/DbgCommand.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include <wx/app.h>
|
||||
#include <wx/cmdline.h>
|
||||
|
||||
class CPUThread;
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_DBG_COMMAND, wxCommandEvent);
|
||||
|
||||
|
||||
class Rpcs3App : public wxApp
|
||||
{
|
||||
private:
|
||||
wxCmdLineParser parser;
|
||||
// Used to restore the configuration state after a test run
|
||||
bool HLEExitOnStop;
|
||||
|
||||
public:
|
||||
MainFrame* m_MainFrame;
|
||||
|
||||
|
@ -25,12 +21,9 @@ public:
|
|||
|
||||
Rpcs3App();
|
||||
|
||||
void SendDbgCommand(DbgCommand id, CPUThread* thr=nullptr);
|
||||
void SendDbgCommand(DbgCommand id, class cpu_thread* thr = nullptr);
|
||||
};
|
||||
|
||||
DECLARE_APP(Rpcs3App)
|
||||
|
||||
//extern CPUThread& GetCPU(const u8 core);
|
||||
|
||||
extern Rpcs3App* TheApp;
|
||||
static const u64 PS3_CLK = 3200000000;
|
||||
|
|
Loading…
Add table
Reference in a new issue