mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-12-06 07:12:28 +01:00
sys_usbd: Emulate Kamen Rider Summonride Ride Gate (#17605)
This PR aims to implement another USB peripheral device, used in Kamen Rider Summonride. Code is very similar to Skylanders (for the loading/creating/removing of figures) and Disney Infinity for the actual USB commands and responses (minus any of the built in random number generation). I haven't been able to generate a full list of the Ride Chips (see [here](https://kamenrider.fandom.com/wiki/Kamen_Rider_Summonride#Characters) for a list of Characters and Chips) besides the ones I have myself from the Starter Set, so I am hoping that interested parties are able to play around and discover what other chips there may be, or if there are any issues when generating their own Characters and Chips. Implements https://github.com/RPCS3/rpcs3/issues/17122
This commit is contained in:
parent
ba2518f862
commit
81f5be30aa
|
|
@ -402,6 +402,7 @@ target_sources(rpcs3_emu PRIVATE
|
|||
Io/GunCon3.cpp
|
||||
Io/Infinity.cpp
|
||||
Io/interception.cpp
|
||||
Io/KamenRider.cpp
|
||||
Io/KeyboardHandler.cpp
|
||||
Io/midi_config_types.cpp
|
||||
Io/mouse_config.cpp
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include "Emu/Io/Skylander.h"
|
||||
#include "Emu/Io/Infinity.h"
|
||||
#include "Emu/Io/Dimensions.h"
|
||||
#include "Emu/Io/KamenRider.h"
|
||||
#include "Emu/Io/GHLtar.h"
|
||||
#include "Emu/Io/ghltar_config.h"
|
||||
#include "Emu/Io/guncon3_config.h"
|
||||
|
|
@ -175,7 +176,7 @@ private:
|
|||
{0x1430, 0x0150, 0x0150, "Skylanders Portal", &usb_device_skylander::get_num_emu_devices, &usb_device_skylander::make_instance},
|
||||
{0x0E6F, 0x0129, 0x0129, "Disney Infinity Base", &usb_device_infinity::get_num_emu_devices, &usb_device_infinity::make_instance},
|
||||
{0x0E6F, 0x0241, 0x0241, "Lego Dimensions Portal", &usb_device_dimensions::get_num_emu_devices, &usb_device_dimensions::make_instance},
|
||||
{0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal", nullptr, nullptr},
|
||||
{0x0E6F, 0x200A, 0x200A, "Kamen Rider Summonride Portal", &usb_device_kamen_rider::get_num_emu_devices, &usb_device_kamen_rider::make_instance},
|
||||
|
||||
// Cameras
|
||||
// {0x1415, 0x0020, 0x2000, "Sony Playstation Eye", nullptr, nullptr}, // TODO: verifiy
|
||||
|
|
|
|||
291
rpcs3/Emu/Io/KamenRider.cpp
Normal file
291
rpcs3/Emu/Io/KamenRider.cpp
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
#include "stdafx.h"
|
||||
#include "KamenRider.h"
|
||||
|
||||
LOG_CHANNEL(kamen_rider_log, "kamen_rider");
|
||||
|
||||
rider_gate g_ridergate;
|
||||
|
||||
void kamen_rider_figure::save()
|
||||
{
|
||||
if (!kamen_file)
|
||||
{
|
||||
kamen_rider_log.error("Tried to save kamen rider figure to file but no kamen rider figure is active!");
|
||||
return;
|
||||
}
|
||||
kamen_file.seek(0, fs::seek_set);
|
||||
kamen_file.write(data.data(), 0x14 * 0x10);
|
||||
}
|
||||
|
||||
u8 rider_gate::generate_checksum(const std::array<u8, 64>& data, u32 num_of_bytes) const
|
||||
{
|
||||
ensure(num_of_bytes <= data.size());
|
||||
int checksum = 0;
|
||||
for (u32 i = 0; i < num_of_bytes; i++)
|
||||
{
|
||||
checksum += data[i];
|
||||
}
|
||||
return (checksum & 0xFF);
|
||||
}
|
||||
|
||||
kamen_rider_figure& rider_gate::get_figure_by_uid(const std::array<u8, 7> uid)
|
||||
{
|
||||
for (kamen_rider_figure& figure : figures)
|
||||
{
|
||||
if (figure.uid == uid)
|
||||
{
|
||||
return figure;
|
||||
}
|
||||
}
|
||||
return figures[7];
|
||||
}
|
||||
|
||||
void rider_gate::get_blank_response(u8 command, u8 sequence, std::array<u8, 64>& reply_buf)
|
||||
{
|
||||
reply_buf = {0x55, 0x02, command, sequence};
|
||||
reply_buf[4] = generate_checksum(reply_buf, 4);
|
||||
}
|
||||
|
||||
void rider_gate::wake_rider_gate(std::array<u8, 64>& reply_buf, u8 command, u8 sequence)
|
||||
{
|
||||
std::lock_guard lock(kamen_mutex);
|
||||
|
||||
m_is_awake = true;
|
||||
reply_buf = {0x55, 0x1a, command, sequence, 0x00, 0x07, 0x00, 0x03, 0x02,
|
||||
0x09, 0x20, 0x03, 0xf5, 0x00, 0x19, 0x42, 0x52, 0xb7,
|
||||
0xb9, 0xa1, 0xae, 0x2b, 0x88, 0x42, 0x05, 0xfe, 0xe0, 0x1c, 0xac};
|
||||
}
|
||||
|
||||
void rider_gate::get_list_tags(std::array<u8, 64>& reply_buf, u8 command, u8 sequence)
|
||||
{
|
||||
std::lock_guard lock(kamen_mutex);
|
||||
|
||||
reply_buf = {0x55, 0x02, command, sequence};
|
||||
u8 index = 4;
|
||||
for (const kamen_rider_figure& figure : figures)
|
||||
{
|
||||
if (figure.present)
|
||||
{
|
||||
reply_buf[index] = 0x09;
|
||||
memcpy(&reply_buf[index + 1], figure.data.data(), 7);
|
||||
index += 8;
|
||||
reply_buf[1] += 8;
|
||||
}
|
||||
}
|
||||
reply_buf[index] = generate_checksum(reply_buf, index);
|
||||
}
|
||||
|
||||
void rider_gate::query_block(std::array<u8, 64>& reply_buf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block)
|
||||
{
|
||||
std::lock_guard lock(kamen_mutex);
|
||||
|
||||
reply_buf = {0x55, 0x13, command, sequence, 0x00};
|
||||
|
||||
const std::array<u8, 7> uid_array = {uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]};
|
||||
|
||||
const kamen_rider_figure& figure = get_figure_by_uid(uid_array);
|
||||
if (figure.present)
|
||||
{
|
||||
if (sector < 5 && block < 4)
|
||||
{
|
||||
memcpy(&reply_buf[5], &figure.data[(sector * 4 * 16) + (block * 16)], 16);
|
||||
}
|
||||
}
|
||||
reply_buf[21] = generate_checksum(reply_buf, 21);
|
||||
}
|
||||
|
||||
void rider_gate::write_block(std::array<u8, 64>& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf)
|
||||
{
|
||||
std::lock_guard lock(kamen_mutex);
|
||||
|
||||
const std::array<u8, 7> uid_array = {uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]};
|
||||
|
||||
kamen_rider_figure& figure = get_figure_by_uid(uid_array);
|
||||
if (figure.present)
|
||||
{
|
||||
if (sector < 5 && block < 4)
|
||||
{
|
||||
memcpy(&figure.data[(sector * 4 * 16) + (block * 16)], to_write_buf, 16);
|
||||
}
|
||||
}
|
||||
|
||||
get_blank_response(command, sequence, replyBuf);
|
||||
}
|
||||
|
||||
std::optional<std::array<u8, 64>> rider_gate::pop_added_removed_response()
|
||||
{
|
||||
std::lock_guard lock(kamen_mutex);
|
||||
|
||||
if (m_figure_added_removed_responses.empty())
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::array<u8, 64> response = m_figure_added_removed_responses.front();
|
||||
m_figure_added_removed_responses.pop();
|
||||
return response;
|
||||
}
|
||||
|
||||
bool rider_gate::remove_figure(u8 index)
|
||||
{
|
||||
std::lock_guard lock(kamen_mutex);
|
||||
|
||||
auto& figure = figures[index];
|
||||
|
||||
if (figure.present)
|
||||
{
|
||||
figure.present = false;
|
||||
figure.save();
|
||||
figure.kamen_file.close();
|
||||
if (m_is_awake)
|
||||
{
|
||||
std::array<u8, 64> figure_removed_response = {0x56, 0x09, 0x09, 0x00};
|
||||
memcpy(&figure_removed_response[4], figure.uid.data(), figure.uid.size());
|
||||
figure_removed_response[11] = generate_checksum(figure_removed_response, 11);
|
||||
m_figure_added_removed_responses.push(std::move(figure_removed_response));
|
||||
}
|
||||
figure.uid = {};
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
u8 rider_gate::load_figure(const std::array<u8, 0x14 * 0x10>& buf, fs::file in_file)
|
||||
{
|
||||
std::lock_guard lock(kamen_mutex);
|
||||
|
||||
u8 found_slot = 0xFF;
|
||||
|
||||
// mimics spot retaining on the portal
|
||||
for (auto i = 0; i < 7; i++)
|
||||
{
|
||||
if (!figures[i].present)
|
||||
{
|
||||
if (i < found_slot)
|
||||
{
|
||||
found_slot = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_slot != 0xFF)
|
||||
{
|
||||
auto& figure = figures[found_slot];
|
||||
memcpy(figure.data.data(), buf.data(), buf.size());
|
||||
figure.kamen_file = std::move(in_file);
|
||||
figure.uid = {buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]};
|
||||
figure.present = true;
|
||||
|
||||
if (m_is_awake)
|
||||
{
|
||||
std::array<u8, 64> figure_added_response = {0x56, 0x09, 0x09, 0x01};
|
||||
memcpy(&figure_added_response[4], figure.uid.data(), figure.uid.size());
|
||||
figure_added_response[11] = generate_checksum(figure_added_response, 11);
|
||||
m_figure_added_removed_responses.push(std::move(figure_added_response));
|
||||
}
|
||||
}
|
||||
return found_slot;
|
||||
}
|
||||
|
||||
usb_device_kamen_rider::usb_device_kamen_rider(const std::array<u8, 7>& location)
|
||||
: usb_device_emulated(location)
|
||||
{
|
||||
device = UsbDescriptorNode(USB_DESCRIPTOR_DEVICE, UsbDeviceDescriptor{0x200, 0x0, 0x0, 0x0, 0x40, 0x0E6F, 0x200A, 0x100, 0x1, 0x2, 0x3, 0x1});
|
||||
auto& config0 = device.add_node(UsbDescriptorNode(USB_DESCRIPTOR_CONFIG, UsbDeviceConfiguration{0x29, 0x1, 0x1, 0x0, 0x80, 0xFA}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_INTERFACE, UsbDeviceInterface{0x0, 0x0, 0x2, 0x3, 0x0, 0x0, 0x0}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x81, 0x3, 0x40, 0x1}));
|
||||
config0.add_node(UsbDescriptorNode(USB_DESCRIPTOR_ENDPOINT, UsbDeviceEndpoint{0x1, 0x3, 0x40, 0x1}));
|
||||
}
|
||||
|
||||
usb_device_kamen_rider::~usb_device_kamen_rider()
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<usb_device> usb_device_kamen_rider::make_instance(u32, const std::array<u8, 7>& location)
|
||||
{
|
||||
return std::make_shared<usb_device_kamen_rider>(location);
|
||||
}
|
||||
|
||||
u16 usb_device_kamen_rider::get_num_emu_devices()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void usb_device_kamen_rider::control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer)
|
||||
{
|
||||
usb_device_emulated::control_transfer(bmRequestType, bRequest, wValue, wIndex, wLength, buf_size, buf, transfer);
|
||||
}
|
||||
|
||||
void usb_device_kamen_rider::interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer)
|
||||
{
|
||||
ensure(buf_size == 0x40);
|
||||
|
||||
transfer->fake = true;
|
||||
transfer->expected_count = buf_size;
|
||||
transfer->expected_result = HC_CC_NOERR;
|
||||
|
||||
if (endpoint == 0x81)
|
||||
{
|
||||
// Respond after FF command
|
||||
transfer->expected_time = get_timestamp() + 1000;
|
||||
std::optional<std::array<u8, 64>> response = g_ridergate.pop_added_removed_response();
|
||||
if (response)
|
||||
{
|
||||
memcpy(buf, response.value().data(), 0x40);
|
||||
}
|
||||
else if (!m_queries.empty())
|
||||
{
|
||||
memcpy(buf, m_queries.front().data(), 0x20);
|
||||
m_queries.pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
transfer->expected_count = 0;
|
||||
transfer->expected_result = EHCI_CC_HALTED;
|
||||
}
|
||||
}
|
||||
else if (endpoint == 0x01)
|
||||
{
|
||||
const u8 command = buf[2];
|
||||
const u8 sequence = buf[3];
|
||||
|
||||
std::array<u8, 64> q_result{};
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case 0xB0: // Wake
|
||||
{
|
||||
g_ridergate.wake_rider_gate(q_result, command, sequence);
|
||||
break;
|
||||
}
|
||||
case 0xC0:
|
||||
case 0xC3: // Color Commands
|
||||
{
|
||||
g_ridergate.get_blank_response(command, sequence, q_result);
|
||||
break;
|
||||
}
|
||||
case 0xD0: // Tag List
|
||||
{
|
||||
// Return list of figure UIDs, separated by an 09
|
||||
g_ridergate.get_list_tags(q_result, command, sequence);
|
||||
break;
|
||||
}
|
||||
case 0xD2: // Read
|
||||
{
|
||||
// Read 16 bytes from figure with UID buf[4] - buf[10]
|
||||
g_ridergate.query_block(q_result, command, sequence, &buf[4], buf[11], buf[12]);
|
||||
break;
|
||||
}
|
||||
case 0xD3:
|
||||
{
|
||||
// Write 16 bytes to figure with UID buf[4] - buf[10]
|
||||
g_ridergate.write_block(q_result, command, sequence, &buf[4], buf[11], buf[12], &buf[13]);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
kamen_rider_log.error("Unhandled Query Type: 0x%02X", command);
|
||||
break;
|
||||
}
|
||||
m_queries.push(std::move(q_result));
|
||||
}
|
||||
}
|
||||
60
rpcs3/Emu/Io/KamenRider.h
Normal file
60
rpcs3/Emu/Io/KamenRider.h
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include "Emu/Io/usb_device.h"
|
||||
#include "Utilities/mutex.h"
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
|
||||
struct kamen_rider_figure
|
||||
{
|
||||
fs::file kamen_file;
|
||||
std::array<u8, 0x14 * 0x10> data{};
|
||||
std::array<u8, 7> uid{};
|
||||
bool present = false;
|
||||
void save();
|
||||
};
|
||||
|
||||
class rider_gate
|
||||
{
|
||||
public:
|
||||
void get_blank_response(u8 command, u8 sequence, std::array<u8, 64>& reply_buf);
|
||||
void wake_rider_gate(std::array<u8, 64>& replyBuf, u8 command, u8 sequence);
|
||||
void get_list_tags(std::array<u8, 64>& replyBuf, u8 command, u8 sequence);
|
||||
void query_block(std::array<u8, 64>& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block);
|
||||
void write_block(std::array<u8, 64>& replyBuf, u8 command, u8 sequence, const u8* uid, u8 sector, u8 block, const u8* to_write_buf);
|
||||
std::optional<std::array<u8, 64>> pop_added_removed_response();
|
||||
|
||||
bool remove_figure(u8 position);
|
||||
u8 load_figure(const std::array<u8, 0x14 * 0x10>& buf, fs::file in_file);
|
||||
|
||||
protected:
|
||||
shared_mutex kamen_mutex;
|
||||
std::array<kamen_rider_figure, 8> figures{};
|
||||
|
||||
private:
|
||||
u8 generate_checksum(const std::array<u8, 64>& data, u32 num_of_bytes) const;
|
||||
kamen_rider_figure& get_figure_by_uid(const std::array<u8, 7> uid);
|
||||
|
||||
std::queue<std::array<u8, 64>> m_figure_added_removed_responses;
|
||||
|
||||
bool m_is_awake = false;
|
||||
};
|
||||
|
||||
extern rider_gate g_ridergate;
|
||||
|
||||
class usb_device_kamen_rider : public usb_device_emulated
|
||||
{
|
||||
public:
|
||||
usb_device_kamen_rider(const std::array<u8, 7>& location);
|
||||
~usb_device_kamen_rider();
|
||||
|
||||
static std::shared_ptr<usb_device> make_instance(u32 controller_index, const std::array<u8, 7>& location);
|
||||
static u16 get_num_emu_devices();
|
||||
|
||||
void control_transfer(u8 bmRequestType, u8 bRequest, u16 wValue, u16 wIndex, u16 wLength, u32 buf_size, u8* buf, UsbTransfer* transfer) override;
|
||||
void interrupt_transfer(u32 buf_size, u8* buf, u32 endpoint, UsbTransfer* transfer) override;
|
||||
|
||||
protected:
|
||||
std::queue<std::array<u8, 64>> m_queries;
|
||||
};
|
||||
|
|
@ -450,6 +450,7 @@
|
|||
<ClCompile Include="Emu\Io\Dimensions.cpp" />
|
||||
<ClCompile Include="Emu\Io\Infinity.cpp" />
|
||||
<ClCompile Include="Emu\Io\Skylander.cpp" />
|
||||
<ClCompile Include="Emu\Io\KamenRider.cpp" />
|
||||
<ClCompile Include="Emu\Io\usb_device.cpp" />
|
||||
<ClCompile Include="Emu\Io\usb_vfs.cpp" />
|
||||
<ClCompile Include="Emu\RSX\Capture\rsx_capture.cpp" />
|
||||
|
|
@ -809,6 +810,7 @@
|
|||
<ClInclude Include="Emu\Io\Dimensions.h" />
|
||||
<ClInclude Include="Emu\Io\Infinity.h" />
|
||||
<ClInclude Include="Emu\Io\Skylander.h" />
|
||||
<ClInclude Include="Emu\Io\KamenRider.h" />
|
||||
<ClInclude Include="Emu\Io\usb_device.h" />
|
||||
<ClInclude Include="Emu\Io\usb_vfs.h" />
|
||||
<ClInclude Include="Emu\IPC.h" />
|
||||
|
|
|
|||
|
|
@ -879,6 +879,9 @@
|
|||
<ClCompile Include="Emu\Io\Skylander.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Io\KamenRider.cpp">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Emu\Cell\lv2\sys_overlay.cpp">
|
||||
<Filter>Emu\Cell\lv2</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -2079,6 +2082,9 @@
|
|||
<ClInclude Include="Emu\Io\Skylander.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Io\KamenRider.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Crypto\md5.h">
|
||||
<Filter>Crypto</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -328,6 +328,9 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Debug\moc_ipc_settings_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_kamen_rider_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_kernel_explorer.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
|
@ -616,6 +619,9 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Release\moc_ipc_settings_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_kamen_rider_dialog.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_kernel_explorer.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
|
|
@ -819,6 +825,7 @@
|
|||
<ClCompile Include="rpcs3qt\infinity_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\input_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\ipc_settings_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\kamen_rider_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\localized.cpp" />
|
||||
<ClCompile Include="rpcs3qt\localized_emu.cpp" />
|
||||
<ClCompile Include="rpcs3qt\log_viewer.cpp" />
|
||||
|
|
@ -1665,6 +1672,16 @@
|
|||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\kamen_rider_dialog.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\debug" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\update_manager.h">
|
||||
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
|
|
|
|||
|
|
@ -107,6 +107,9 @@
|
|||
<Filter Include="Gui\infinity">
|
||||
<UniqueIdentifier>{f5fcca0d-918b-46ba-bb91-2f2f9d9ddbba}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Gui\kamen_rider">
|
||||
<UniqueIdentifier>{8b4d2dff-2b4e-4794-9859-4379ef0e75c0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Gui\skylanders">
|
||||
<UniqueIdentifier>{c25f8f80-cc74-4760-8488-a291b3026b1d}</UniqueIdentifier>
|
||||
</Filter>
|
||||
|
|
@ -639,6 +642,9 @@
|
|||
<ClCompile Include="rpcs3qt\infinity_dialog.cpp">
|
||||
<Filter>Gui\infinity</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\kamen_rider_dialog.cpp">
|
||||
<Filter>Gui\infinity</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\skylander_dialog.cpp">
|
||||
<Filter>Gui\skylanders</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -753,6 +759,12 @@
|
|||
<ClCompile Include="QTGeneratedFiles\Release\moc_infinity_dialog.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_kamen_rider_dialog.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_kamen_rider_dialog.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="QTGeneratedFiles\Debug\moc_skylander_dialog.cpp">
|
||||
<Filter>Generated Files\Debug</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -1621,6 +1633,9 @@
|
|||
<CustomBuild Include="rpcs3qt\infinity_dialog.h">
|
||||
<Filter>Gui\infinity</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\kamen_rider_dialog.h">
|
||||
<Filter>Gui\kamen_rider</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="rpcs3qt\skylander_dialog.h">
|
||||
<Filter>Gui\skylanders</Filter>
|
||||
</CustomBuild>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ add_library(rpcs3_ui STATIC
|
|||
input_dialog.cpp
|
||||
instruction_editor_dialog.cpp
|
||||
ipc_settings_dialog.cpp
|
||||
kamen_rider_dialog.cpp
|
||||
kernel_explorer.cpp
|
||||
localized.cpp
|
||||
localized_emu.cpp
|
||||
|
|
|
|||
422
rpcs3/rpcs3qt/kamen_rider_dialog.cpp
Normal file
422
rpcs3/rpcs3qt/kamen_rider_dialog.cpp
Normal file
|
|
@ -0,0 +1,422 @@
|
|||
#include "stdafx.h"
|
||||
#include "Utilities/File.h"
|
||||
#include "kamen_rider_dialog.h"
|
||||
#include "Emu/Io/KamenRider.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QGroupBox>
|
||||
#include <QFileDialog>
|
||||
#include <QVBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
#include <QStringList>
|
||||
#include <QCompleter>
|
||||
|
||||
kamen_rider_dialog* kamen_rider_dialog::inst = nullptr;
|
||||
std::array<std::optional<std::tuple<u8, u8, u8>>, UI_FIG_NUM> kamen_rider_dialog::figure_slots = {};
|
||||
QString last_kamen_rider_path;
|
||||
|
||||
static const std::map<const std::pair<const u8, const u8>, const std::string> list_kamen_riders = {
|
||||
{{0x10, 0x10}, "Kamen Rider Drive Wind"},
|
||||
{{0x10, 0x20}, "Kamen Rider Drive Water"},
|
||||
{{0x10, 0x30}, "Kamen Rider Drive Fire"},
|
||||
{{0x10, 0x40}, "Kamen Rider Drive Light"},
|
||||
{{0x10, 0x50}, "Kamen Rider Drive Dark"},
|
||||
{{0x11, 0x10}, "Kamen Rider Gaim Wind"},
|
||||
{{0x11, 0x20}, "Kamen Rider Gaim Water"},
|
||||
{{0x12, 0x20}, "Kamen Rider Wizard Water"},
|
||||
{{0x12, 0x30}, "Kamen Rider Wizard Fire"},
|
||||
{{0x13, 0x40}, "Kamen Rider Fourze Light"},
|
||||
{{0x14, 0x20}, "Kamen Rider 000 Water"},
|
||||
{{0x15, 0x10}, "Kamen Rider Double Wind"},
|
||||
{{0x16, 0x50}, "Kamen Rider Decade Dark"},
|
||||
{{0x17, 0x50}, "Kamen Rider Kiva Dark"},
|
||||
{{0x18, 0x40}, "Kamen Rider Den-O Light"},
|
||||
{{0x19, 0x30}, "Kamen Rider Kabuto Fire"},
|
||||
{{0x1A, 0x30}, "Kamen Rider Hibiki Fire"},
|
||||
{{0x1B, 0x50}, "Kamen Rider Blade Dark"},
|
||||
{{0x1C, 0x50}, "Kamen Rider Faiz Dark"},
|
||||
{{0x1D, 0x10}, "Kamen Rider Ryuki Wind"},
|
||||
{{0x1E, 0x20}, "Kamen Rider Agito Water"},
|
||||
{{0x1F, 0x40}, "Kamen Rider Kuuga Light"},
|
||||
{{0x20, 0x00}, "Type Wild"},
|
||||
{{0x21, 0x00}, "Kamen Rider Zangetsu"},
|
||||
{{0x22, 0x00}, "All Dragon"},
|
||||
{{0x31, 0x00}, "Kachidoki Arms"},
|
||||
};
|
||||
|
||||
static u32 kamen_rider_crc32(const std::array<u8, 16>& buffer)
|
||||
{
|
||||
static constexpr std::array<u32, 256> CRC32_TABLE{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535,
|
||||
0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd,
|
||||
0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d,
|
||||
0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4,
|
||||
0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac,
|
||||
0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab,
|
||||
0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f,
|
||||
0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb,
|
||||
0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea,
|
||||
0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce,
|
||||
0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a,
|
||||
0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409,
|
||||
0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739,
|
||||
0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268,
|
||||
0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0,
|
||||
0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8,
|
||||
0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef,
|
||||
0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703,
|
||||
0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7,
|
||||
0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae,
|
||||
0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6,
|
||||
0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d,
|
||||
0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5,
|
||||
0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605,
|
||||
0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};
|
||||
|
||||
// Kamen Rider figures calculate their CRC32 based on 12 bytes in the block of 16
|
||||
u32 ret = 0;
|
||||
for (u32 i = 0; i < 12; ++i)
|
||||
{
|
||||
const u8 index = u8(ret & 0xFF) ^ buffer[i];
|
||||
ret = ((ret >> 8) ^ CRC32_TABLE[index]);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
kamen_rider_creator_dialog::kamen_rider_creator_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("Kamen Rider Creator"));
|
||||
setObjectName("kamen_rider_creator");
|
||||
setMinimumSize(QSize(500, 150));
|
||||
|
||||
QVBoxLayout* vbox_panel = new QVBoxLayout();
|
||||
|
||||
QComboBox* combo_figlist = new QComboBox();
|
||||
QStringList filterlist;
|
||||
for (const auto& [entry, figure_name] : list_kamen_riders)
|
||||
{
|
||||
const uint qvar = (entry.first << 8) | entry.second;
|
||||
QString name = QString::fromStdString(figure_name);
|
||||
combo_figlist->addItem(name, QVariant(qvar));
|
||||
filterlist << std::move(name);
|
||||
}
|
||||
combo_figlist->addItem(tr("--Unknown--"), QVariant(0xFFFF));
|
||||
combo_figlist->setEditable(true);
|
||||
combo_figlist->setInsertPolicy(QComboBox::NoInsert);
|
||||
combo_figlist->model()->sort(0, Qt::AscendingOrder);
|
||||
|
||||
QCompleter* co_compl = new QCompleter(filterlist, this);
|
||||
co_compl->setCaseSensitivity(Qt::CaseInsensitive);
|
||||
co_compl->setCompletionMode(QCompleter::PopupCompletion);
|
||||
co_compl->setFilterMode(Qt::MatchContains);
|
||||
combo_figlist->setCompleter(co_compl);
|
||||
|
||||
vbox_panel->addWidget(combo_figlist);
|
||||
|
||||
QFrame* line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
vbox_panel->addWidget(line);
|
||||
|
||||
QHBoxLayout* hbox_idvar = new QHBoxLayout();
|
||||
QLabel* label_id = new QLabel(tr("ID:"));
|
||||
QLabel* label_type = new QLabel(tr("Type:"));
|
||||
QLineEdit* edit_id = new QLineEdit("0");
|
||||
QLineEdit* edit_type = new QLineEdit("0");
|
||||
QRegularExpressionValidator* rxv = new QRegularExpressionValidator(QRegularExpression("\\d*"), this);
|
||||
edit_id->setValidator(rxv);
|
||||
edit_type->setValidator(rxv);
|
||||
hbox_idvar->addWidget(label_id);
|
||||
hbox_idvar->addWidget(edit_id);
|
||||
hbox_idvar->addWidget(label_type);
|
||||
hbox_idvar->addWidget(edit_type);
|
||||
vbox_panel->addLayout(hbox_idvar);
|
||||
|
||||
QHBoxLayout* hbox_buttons = new QHBoxLayout();
|
||||
QPushButton* btn_create = new QPushButton(tr("Create"), this);
|
||||
QPushButton* btn_cancel = new QPushButton(tr("Cancel"), this);
|
||||
hbox_buttons->addStretch();
|
||||
hbox_buttons->addWidget(btn_create);
|
||||
hbox_buttons->addWidget(btn_cancel);
|
||||
vbox_panel->addLayout(hbox_buttons);
|
||||
|
||||
setLayout(vbox_panel);
|
||||
|
||||
connect(combo_figlist, QOverload<int>::of(&QComboBox::currentIndexChanged), [=](int index)
|
||||
{
|
||||
const u16 fig_info = combo_figlist->itemData(index).toUInt();
|
||||
if (fig_info != 0xFFFF)
|
||||
{
|
||||
const u8 fig_id = fig_info >> 8;
|
||||
const u8 fig_type = fig_info & 0xFF;
|
||||
|
||||
edit_id->setText(QString::number(fig_id));
|
||||
edit_type->setText(QString::number(fig_type));
|
||||
}
|
||||
});
|
||||
|
||||
connect(btn_create, &QAbstractButton::clicked, this, [=, this]()
|
||||
{
|
||||
bool ok_id = false, ok_var = false;
|
||||
const u8 fig_id = edit_id->text().toUShort(&ok_id);
|
||||
if (!ok_id)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Error converting value"), tr("ID entered is invalid!"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
const u8 fig_type = edit_type->text().toUShort(&ok_var);
|
||||
if (!ok_var)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Error converting value"), tr("Variant entered is invalid!"), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
QString predef_name = last_kamen_rider_path;
|
||||
const auto found_fig = list_kamen_riders.find(std::make_pair(fig_id, fig_type));
|
||||
if (found_fig != list_kamen_riders.cend())
|
||||
{
|
||||
predef_name += QString::fromStdString(found_fig->second + ".bin");
|
||||
}
|
||||
else
|
||||
{
|
||||
predef_name += QString("Unknown(%1 %2).bin").arg(fig_id).arg(fig_type);
|
||||
}
|
||||
|
||||
file_path = QFileDialog::getSaveFileName(this, tr("Create Kamen Rider File"), predef_name, tr("Kamen Rider Object (*.bin);;All Files (*)"));
|
||||
if (file_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fs::file fig_file(file_path.toStdString(), fs::read + fs::write + fs::create);
|
||||
if (!fig_file)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to create kamen rider file!"), tr("Failed to create kamen rider file:\n%1").arg(file_path), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<u8, 0x14 * 0x10> buf{};
|
||||
|
||||
buf[0] = 0x04;
|
||||
buf[6] = 0x80;
|
||||
|
||||
std::random_device rd;
|
||||
std::mt19937 mt(rd());
|
||||
std::uniform_int_distribution<int> dist(0, 255);
|
||||
|
||||
buf[1] = dist(mt);
|
||||
buf[2] = dist(mt);
|
||||
buf[3] = dist(mt);
|
||||
buf[4] = dist(mt);
|
||||
buf[5] = dist(mt);
|
||||
|
||||
buf[7] = 0x89;
|
||||
buf[8] = 0x44;
|
||||
buf[10] = 0xc2;
|
||||
std::array<u8, 16> figure_data = {u8(dist(mt)), 0x03, 0x00, 0x00, 0x01, 0x0e, 0x0a, 0x0a, 0x10, fig_type, 0x01, fig_id};
|
||||
write_to_ptr<le_t<u32>>(figure_data.data(), 0xC, kamen_rider_crc32(figure_data));
|
||||
memcpy(&buf[16], figure_data.data(), figure_data.size());
|
||||
fig_file.write(buf.data(), buf.size());
|
||||
fig_file.close();
|
||||
|
||||
last_kamen_rider_path = QFileInfo(file_path).absolutePath() + "/";
|
||||
accept();
|
||||
});
|
||||
|
||||
connect(btn_cancel, &QAbstractButton::clicked, this, &QDialog::reject);
|
||||
|
||||
connect(co_compl, QOverload<const QString&>::of(&QCompleter::activated), [=](const QString& text)
|
||||
{
|
||||
combo_figlist->setCurrentText(text);
|
||||
combo_figlist->setCurrentIndex(combo_figlist->findText(text));
|
||||
});
|
||||
}
|
||||
|
||||
QString kamen_rider_creator_dialog::get_file_path() const
|
||||
{
|
||||
return file_path;
|
||||
}
|
||||
|
||||
kamen_rider_dialog::kamen_rider_dialog(QWidget* parent)
|
||||
: QDialog(parent)
|
||||
{
|
||||
setWindowTitle(tr("kamen_riders Manager"));
|
||||
setObjectName("kamen_riders_manager");
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setMinimumSize(QSize(700, 200));
|
||||
|
||||
QVBoxLayout* vbox_panel = new QVBoxLayout();
|
||||
|
||||
auto add_line = [](QVBoxLayout* vbox)
|
||||
{
|
||||
QFrame* line = new QFrame();
|
||||
line->setFrameShape(QFrame::HLine);
|
||||
line->setFrameShadow(QFrame::Sunken);
|
||||
vbox->addWidget(line);
|
||||
};
|
||||
|
||||
QGroupBox* group_kamen_riders = new QGroupBox(tr("Active Portal Kamen Riders:"));
|
||||
QVBoxLayout* vbox_group = new QVBoxLayout();
|
||||
|
||||
for (auto i = 0; i < UI_FIG_NUM; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
{
|
||||
add_line(vbox_group);
|
||||
}
|
||||
|
||||
QHBoxLayout* hbox_kamen_rider = new QHBoxLayout();
|
||||
QLabel* label_figname = new QLabel(QString(tr("Kamen Rider %1")).arg(i + 1));
|
||||
edit_kamen_riders[i] = new QLineEdit();
|
||||
edit_kamen_riders[i]->setEnabled(false);
|
||||
|
||||
QPushButton* clear_btn = new QPushButton(tr("Clear"));
|
||||
QPushButton* create_btn = new QPushButton(tr("Create"));
|
||||
QPushButton* load_btn = new QPushButton(tr("Load"));
|
||||
|
||||
connect(clear_btn, &QAbstractButton::clicked, this, [this, i]()
|
||||
{
|
||||
clear_kamen_rider(i);
|
||||
});
|
||||
connect(create_btn, &QAbstractButton::clicked, this, [this, i]()
|
||||
{
|
||||
create_kamen_rider(i);
|
||||
});
|
||||
connect(load_btn, &QAbstractButton::clicked, this, [this, i]()
|
||||
{
|
||||
load_kamen_rider(i);
|
||||
});
|
||||
|
||||
hbox_kamen_rider->addWidget(label_figname);
|
||||
hbox_kamen_rider->addWidget(edit_kamen_riders[i]);
|
||||
hbox_kamen_rider->addWidget(clear_btn);
|
||||
hbox_kamen_rider->addWidget(create_btn);
|
||||
hbox_kamen_rider->addWidget(load_btn);
|
||||
|
||||
vbox_group->addLayout(hbox_kamen_rider);
|
||||
}
|
||||
|
||||
group_kamen_riders->setLayout(vbox_group);
|
||||
vbox_panel->addWidget(group_kamen_riders);
|
||||
setLayout(vbox_panel);
|
||||
|
||||
update_edits();
|
||||
}
|
||||
|
||||
kamen_rider_dialog::~kamen_rider_dialog()
|
||||
{
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
kamen_rider_dialog* kamen_rider_dialog::get_dlg(QWidget* parent)
|
||||
{
|
||||
if (inst == nullptr)
|
||||
inst = new kamen_rider_dialog(parent);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void kamen_rider_dialog::clear_kamen_rider(u8 slot)
|
||||
{
|
||||
if (const auto& slot_infos = ::at32(figure_slots, slot))
|
||||
{
|
||||
const auto& [cur_slot, id, var] = slot_infos.value();
|
||||
g_ridergate.remove_figure(cur_slot);
|
||||
figure_slots[slot] = {};
|
||||
update_edits();
|
||||
}
|
||||
}
|
||||
|
||||
void kamen_rider_dialog::create_kamen_rider(u8 slot)
|
||||
{
|
||||
kamen_rider_creator_dialog create_dlg(this);
|
||||
if (create_dlg.exec() == Accepted)
|
||||
{
|
||||
load_kamen_rider_path(slot, create_dlg.get_file_path());
|
||||
}
|
||||
}
|
||||
|
||||
void kamen_rider_dialog::load_kamen_rider(u8 slot)
|
||||
{
|
||||
const QString file_path = QFileDialog::getOpenFileName(this, tr("Select Kamen Rider File"), last_kamen_rider_path, tr("Kamen Rider (*.bin);;All Files (*)"));
|
||||
if (file_path.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
last_kamen_rider_path = QFileInfo(file_path).absolutePath() + "/";
|
||||
|
||||
load_kamen_rider_path(slot, file_path);
|
||||
}
|
||||
|
||||
void kamen_rider_dialog::load_kamen_rider_path(u8 slot, const QString& path)
|
||||
{
|
||||
fs::file fig_file(path.toStdString(), fs::read + fs::write + fs::lock);
|
||||
if (!fig_file)
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to open the kamen rider file!"), tr("Failed to open the kamen rider file(%1)!\nFile may already be in use on the portal.").arg(path), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
std::array<u8, 0x14 * 0x10> data;
|
||||
if (fig_file.read(data.data(), data.size()) != data.size())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Failed to read the kamen rider file!"), tr("Failed to read the kamen rider file(%1)!\nFile was too small.").arg(path), QMessageBox::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
clear_kamen_rider(slot);
|
||||
|
||||
u8 fig_id = data[0x19];
|
||||
u8 fig_type = data[0x1B];
|
||||
|
||||
u8 portal_slot = g_ridergate.load_figure(data, std::move(fig_file));
|
||||
figure_slots[slot] = std::tuple(portal_slot, fig_id, fig_type);
|
||||
|
||||
update_edits();
|
||||
}
|
||||
|
||||
void kamen_rider_dialog::update_edits()
|
||||
{
|
||||
for (auto i = 0; i < UI_FIG_NUM; i++)
|
||||
{
|
||||
QString display_string;
|
||||
if (const auto& sd = figure_slots[i])
|
||||
{
|
||||
const auto& [portal_slot, fig_id, fig_type] = sd.value();
|
||||
const auto found_fig = list_kamen_riders.find(std::make_pair(fig_id, fig_type));
|
||||
if (found_fig != list_kamen_riders.cend())
|
||||
{
|
||||
display_string = QString::fromStdString(found_fig->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
display_string = QString(tr("Unknown (Id:%1 Var:%2)")).arg(fig_id).arg(fig_type);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
display_string = tr("None");
|
||||
}
|
||||
|
||||
edit_kamen_riders[i]->setText(display_string);
|
||||
}
|
||||
}
|
||||
49
rpcs3/rpcs3qt/kamen_rider_dialog.h
Normal file
49
rpcs3/rpcs3qt/kamen_rider_dialog.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <QDialog>
|
||||
#include <QLineEdit>
|
||||
|
||||
constexpr auto UI_FIG_NUM = 8;
|
||||
|
||||
class kamen_rider_creator_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit kamen_rider_creator_dialog(QWidget* parent);
|
||||
QString get_file_path() const;
|
||||
|
||||
protected:
|
||||
QString file_path;
|
||||
};
|
||||
|
||||
class kamen_rider_dialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit kamen_rider_dialog(QWidget* parent);
|
||||
~kamen_rider_dialog();
|
||||
static kamen_rider_dialog* get_dlg(QWidget* parent);
|
||||
|
||||
kamen_rider_dialog(kamen_rider_dialog const&) = delete;
|
||||
void operator=(kamen_rider_dialog const&) = delete;
|
||||
|
||||
protected:
|
||||
void clear_kamen_rider(u8 slot);
|
||||
void create_kamen_rider(u8 slot);
|
||||
void load_kamen_rider(u8 slot);
|
||||
void load_kamen_rider_path(u8 slot, const QString& path);
|
||||
|
||||
void update_edits();
|
||||
|
||||
protected:
|
||||
std::array<QLineEdit*, UI_FIG_NUM> edit_kamen_riders{};
|
||||
static std::array<std::optional<std::tuple<u8, u8, u8>>, UI_FIG_NUM> figure_slots;
|
||||
|
||||
private:
|
||||
static kamen_rider_dialog* inst;
|
||||
};
|
||||
|
|
@ -23,6 +23,7 @@
|
|||
#include "skylander_dialog.h"
|
||||
#include "infinity_dialog.h"
|
||||
#include "dimensions_dialog.h"
|
||||
#include "kamen_rider_dialog.h"
|
||||
#include "cheat_manager.h"
|
||||
#include "patch_manager_dialog.h"
|
||||
#include "patch_creator_dialog.h"
|
||||
|
|
@ -2999,6 +3000,12 @@ void main_window::CreateConnects()
|
|||
dim_dlg->show();
|
||||
});
|
||||
|
||||
connect(ui->actionManage_KamenRider_RideGate, &QAction::triggered, this, [this]
|
||||
{
|
||||
kamen_rider_dialog* kam_dlg = kamen_rider_dialog::get_dlg(this);
|
||||
kam_dlg->show();
|
||||
});
|
||||
|
||||
connect(ui->actionManage_Cheats, &QAction::triggered, this, [this]
|
||||
{
|
||||
cheat_manager_dialog* cheat_manager = cheat_manager_dialog::get_dlg(this);
|
||||
|
|
|
|||
|
|
@ -302,6 +302,7 @@
|
|||
<addaction name="actionManage_Skylanders_Portal"/>
|
||||
<addaction name="actionManage_Infinity_Base"/>
|
||||
<addaction name="actionManage_Dimensions_ToyPad"/>
|
||||
<addaction name="actionManage_KamenRider_RideGate"/>
|
||||
<addaction name="actionManage_Cheats"/>
|
||||
<addaction name="actionManage_Game_Patches"/>
|
||||
<addaction name="actionManage_Screenshots"/>
|
||||
|
|
@ -1173,6 +1174,11 @@
|
|||
<string>Dimensions Toypad</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionManage_KamenRider_RideGate">
|
||||
<property name="text">
|
||||
<string>Kamen Rider Ride Gate</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionManage_Cheats">
|
||||
<property name="text">
|
||||
<string>Cheats</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue