mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-04-23 13:00:44 +00:00
HW: Rewrite MagCard features of SI_DeviceAMBaseboard in new MagneticCardReader class.
A huge thank you goes to GXTX and https://github.com/GXTX/YACardEmu which this code is based on.
This commit is contained in:
parent
d893565b78
commit
f657b32521
2
Externals/licenses.md
vendored
2
Externals/licenses.md
vendored
@ -76,6 +76,8 @@ Dolphin includes or links code of the following third-party software projects:
|
||||
[MIT](https://github.com/microsoft/wil/blob/master/LICENSE)
|
||||
- [xxHash](https://github.com/Cyan4973/xxHash):
|
||||
[BSD 2-Clause](https://github.com/Cyan4973/xxHash/blob/master/LICENSE)
|
||||
- [YACardEmu](https://github.com/GXTX/YACardEmu)
|
||||
[GPLv2+](https://github.com/GXTX/YACardEmu/blob/master/license.txt)
|
||||
- [zlib-ng](https://github.com/zlib-ng/zlib-ng):
|
||||
[zlib license](https://github.com/zlib-ng/zlib-ng/blob/develop/LICENSE.md)
|
||||
- [Zstandard](https://facebook.github.io/zstd/):
|
||||
|
||||
@ -254,6 +254,12 @@ add_library(core
|
||||
HW/HSP/HSP_DeviceNull.h
|
||||
HW/HW.cpp
|
||||
HW/HW.h
|
||||
HW/MagCard/C1231BR.cpp
|
||||
HW/MagCard/C1231BR.h
|
||||
HW/MagCard/C1231LR.cpp
|
||||
HW/MagCard/C1231LR.h
|
||||
HW/MagCard/MagneticCardReader.cpp
|
||||
HW/MagCard/MagneticCardReader.h
|
||||
HW/Memmap.cpp
|
||||
HW/Memmap.h
|
||||
HW/MemoryInterface.cpp
|
||||
|
||||
82
Source/Core/Core/HW/MagCard/C1231BR.cpp
Normal file
82
Source/Core/Core/HW/MagCard/C1231BR.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on: https://github.com/GXTX/YACardEmu
|
||||
// Copyright (C) 2020-2023 wutno (https://github.com/GXTX)
|
||||
// Copyright (C) 2022-2023 tugpoat (https://github.com/tugpoat)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/MagCard/C1231BR.h"
|
||||
|
||||
namespace MagCard
|
||||
{
|
||||
|
||||
void C1231BR::Command_10_Initialize()
|
||||
{
|
||||
// TODO: Not hardware tested.
|
||||
if (!m_current_packet.empty())
|
||||
{
|
||||
if (InitMode(m_current_packet[0]) == InitMode::EjectAfter)
|
||||
{
|
||||
m_is_shutter_open = true;
|
||||
MoveCard(R::ENTRY_SLOT);
|
||||
}
|
||||
}
|
||||
|
||||
// m_status.SoftReset(); // TODO: Needed ?
|
||||
FinishCommand();
|
||||
}
|
||||
|
||||
void C1231BR::Command_D0_ControlShutter()
|
||||
{
|
||||
enum class Action : u8
|
||||
{
|
||||
Close = 0x30,
|
||||
Open = 0x31,
|
||||
};
|
||||
|
||||
if (m_current_packet.empty())
|
||||
{
|
||||
SetPError(P::SYSTEM_ERR);
|
||||
FinishCommand();
|
||||
return;
|
||||
}
|
||||
|
||||
m_is_shutter_open = Action(m_current_packet[0]) == Action::Open;
|
||||
|
||||
INFO_LOG_FMT(SERIALINTERFACE_CARD, "Shutter: {}", m_is_shutter_open ? "Open" : "Close");
|
||||
|
||||
FinishCommand();
|
||||
}
|
||||
|
||||
u8 C1231BR::GetPositionValue()
|
||||
{
|
||||
// True "R" (Card Position) values.
|
||||
// FYI: I see a 0xd0 command transition from: 11000 -> 01110 -> 00111
|
||||
// Are these 5 bits individual sensors placed within the machine ?
|
||||
static constexpr std::array<u8, std::size_t(R::MAX_POSITIONS)> POSITION_VALUES{
|
||||
0b00000, // NO_CARD
|
||||
0b11000, // READ_WRITE_HEAD
|
||||
0b00111, // THERMAL_HEAD
|
||||
0b11100, // DISPENSER_THERMAL
|
||||
0b00001, // ENTRY_SLOT
|
||||
};
|
||||
|
||||
return (m_is_shutter_open ? 0x80u : 0x40u) |
|
||||
(m_card_settings->report_dispenser_empty ? 0x00u : 0x20u) |
|
||||
POSITION_VALUES[u8(m_status.r)];
|
||||
}
|
||||
|
||||
void C1231BR::DoState(PointerWrap& p)
|
||||
{
|
||||
MagneticCardReader::DoState(p);
|
||||
|
||||
p.Do(m_is_shutter_open);
|
||||
}
|
||||
|
||||
bool C1231BR::IsReadyForCard()
|
||||
{
|
||||
return !IsCardPresent() && m_is_shutter_open;
|
||||
}
|
||||
|
||||
} // namespace MagCard
|
||||
37
Source/Core/Core/HW/MagCard/C1231BR.h
Normal file
37
Source/Core/Core/HW/MagCard/C1231BR.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on: https://github.com/GXTX/YACardEmu
|
||||
// Copyright (C) 2020-2023 wutno (https://github.com/GXTX)
|
||||
// Copyright (C) 2022-2023 tugpoat (https://github.com/tugpoat)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/MagCard/MagneticCardReader.h"
|
||||
|
||||
namespace MagCard
|
||||
{
|
||||
|
||||
// CRP-1231BR-10
|
||||
// Used by F-Zero AX
|
||||
class C1231BR final : public MagneticCardReader
|
||||
{
|
||||
public:
|
||||
using MagneticCardReader::MagneticCardReader;
|
||||
|
||||
protected:
|
||||
// Specific to this model.
|
||||
bool m_is_shutter_open = true;
|
||||
|
||||
bool IsReadyForCard() override;
|
||||
|
||||
u8 GetPositionValue() override;
|
||||
|
||||
void Command_10_Initialize() override;
|
||||
void Command_D0_ControlShutter() override;
|
||||
|
||||
void DoState(PointerWrap& p) override;
|
||||
};
|
||||
|
||||
} // namespace MagCard
|
||||
27
Source/Core/Core/HW/MagCard/C1231LR.cpp
Normal file
27
Source/Core/Core/HW/MagCard/C1231LR.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on: https://github.com/GXTX/YACardEmu
|
||||
// Copyright (C) 2020-2023 wutno (https://github.com/GXTX)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Core/HW/MagCard/C1231LR.h"
|
||||
|
||||
namespace MagCard
|
||||
{
|
||||
|
||||
u8 C1231LR::GetPositionValue()
|
||||
{
|
||||
// True "R" (Card Position) values.
|
||||
static constexpr std::array<u8, std::size_t(R::MAX_POSITIONS)> POSITION_VALUES{
|
||||
0x30, // NO_CARD
|
||||
0x31, // READ_WRITE_HEAD
|
||||
0x32, // THERMAL_HEAD
|
||||
0x33, // DISPENSER_THERMAL
|
||||
0x34, // ENTRY_SLOT
|
||||
};
|
||||
|
||||
return POSITION_VALUES[u8(m_status.r)];
|
||||
}
|
||||
|
||||
} // namespace MagCard
|
||||
26
Source/Core/Core/HW/MagCard/C1231LR.h
Normal file
26
Source/Core/Core/HW/MagCard/C1231LR.h
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on: https://github.com/GXTX/YACardEmu
|
||||
// Copyright (C) 2020-2023 wutno (https://github.com/GXTX)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/MagCard/MagneticCardReader.h"
|
||||
|
||||
namespace MagCard
|
||||
{
|
||||
|
||||
// CRP-1231LR-10NAB
|
||||
// Used by Mario Kart Arcade GP + GP2
|
||||
class C1231LR final : public MagneticCardReader
|
||||
{
|
||||
public:
|
||||
using MagneticCardReader::MagneticCardReader;
|
||||
|
||||
protected:
|
||||
u8 GetPositionValue() override;
|
||||
};
|
||||
|
||||
} // namespace MagCard
|
||||
1002
Source/Core/Core/HW/MagCard/MagneticCardReader.cpp
Normal file
1002
Source/Core/Core/HW/MagCard/MagneticCardReader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
207
Source/Core/Core/HW/MagCard/MagneticCardReader.h
Normal file
207
Source/Core/Core/HW/MagCard/MagneticCardReader.h
Normal file
@ -0,0 +1,207 @@
|
||||
// Copyright 2026 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Based on: https://github.com/GXTX/YACardEmu
|
||||
// Copyright (C) 2020-2023 wutno (https://github.com/GXTX)
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
namespace MagCard
|
||||
{
|
||||
|
||||
class MagneticCardReader
|
||||
{
|
||||
public:
|
||||
static constexpr std::size_t TRACK_SIZE = 69; // A nice amount of data.
|
||||
static constexpr std::size_t NUM_TRACKS = 3;
|
||||
|
||||
// This data is not part of the card reader state.
|
||||
struct Settings
|
||||
{
|
||||
std::string card_name;
|
||||
std::string card_path;
|
||||
|
||||
bool insert_card_when_possible = true;
|
||||
bool report_dispenser_empty = false;
|
||||
};
|
||||
|
||||
explicit MagneticCardReader(Settings* settings);
|
||||
virtual ~MagneticCardReader();
|
||||
|
||||
MagneticCardReader(const MagneticCardReader&) = delete;
|
||||
MagneticCardReader& operator=(const MagneticCardReader&) = delete;
|
||||
MagneticCardReader(MagneticCardReader&&) = delete;
|
||||
MagneticCardReader& operator=(MagneticCardReader&&) = delete;
|
||||
|
||||
// TODO: This std:vector buffer interface is a bit funky..
|
||||
|
||||
// read = MagCard <-- Baseboard.
|
||||
// write = Magcard --> Baseboard.
|
||||
void Process(std::vector<u8>* read, std::vector<u8>* write);
|
||||
|
||||
virtual void DoState(PointerWrap& p);
|
||||
|
||||
protected:
|
||||
// Status bytes:
|
||||
enum class R : u8
|
||||
{
|
||||
// Note: Derived classes define the real values in GetPositionValue().
|
||||
NO_CARD,
|
||||
READ_WRITE_HEAD,
|
||||
THERMAL_HEAD,
|
||||
DISPENSER_THERMAL,
|
||||
ENTRY_SLOT,
|
||||
|
||||
MAX_POSITIONS,
|
||||
};
|
||||
enum class P : u8
|
||||
{
|
||||
NO_ERR = 0x30,
|
||||
READ_ERR = 0x31,
|
||||
WRITE_ERR = 0x32,
|
||||
CARD_JAM = 0x33,
|
||||
MOTOR_ERR = 0x34, // transport system motor error
|
||||
PRINT_ERR = 0x35,
|
||||
ILLEGAL_ERR = 0x38, // generic error
|
||||
BATTERY_ERR = 0x40, // low battery voltage
|
||||
SYSTEM_ERR = 0x41, // reader/writer system err
|
||||
TRACK_1_READ_ERR = 0x51,
|
||||
TRACK_2_READ_ERR = 0x52,
|
||||
TRACK_3_READ_ERR = 0x53,
|
||||
TRACK_1_AND_2_READ_ERR = 0x54,
|
||||
TRACK_1_AND_3_READ_ERR = 0x55,
|
||||
TRACK_2_AND_3_READ_ERR = 0x56,
|
||||
};
|
||||
enum class S : u8
|
||||
{
|
||||
NO_JOB = 0x30,
|
||||
ILLEGAL_COMMAND = 0x32,
|
||||
RUNNING_COMMAND = 0x33,
|
||||
WAITING_FOR_CARD = 0x34,
|
||||
DISPENSER_EMPTY = 0x35,
|
||||
NO_DISPENSER = 0x36,
|
||||
CARD_FULL = 0x37,
|
||||
};
|
||||
|
||||
struct Status
|
||||
{
|
||||
R r = R::NO_CARD;
|
||||
P p = P::NO_ERR;
|
||||
S s = S::NO_JOB;
|
||||
|
||||
void SoftReset()
|
||||
{
|
||||
p = P::NO_ERR;
|
||||
s = S::NO_JOB;
|
||||
}
|
||||
};
|
||||
|
||||
enum class InitMode : u8
|
||||
{
|
||||
Standard = 0x30,
|
||||
EjectAfter = 0x31,
|
||||
ResetSpecifications = 0x32,
|
||||
};
|
||||
|
||||
bool ReceivePacket(std::span<const u8> packet);
|
||||
void BuildPacket(std::vector<u8>& write_buffer);
|
||||
|
||||
void StepStateMachine(DT elapsed_time);
|
||||
void StepStatePerson(DT elapsed_time);
|
||||
|
||||
// This is what actually maps the "R" position value to a number.
|
||||
virtual u8 GetPositionValue() = 0;
|
||||
|
||||
void SetPError(P error_code);
|
||||
void SetSError(S error_code);
|
||||
|
||||
void ClearCardInMemory();
|
||||
|
||||
// Read/Write with host filesystem.
|
||||
void ReadCardFile();
|
||||
void WriteCardFile();
|
||||
|
||||
bool IsRunningCommand() const;
|
||||
void FinishCommand();
|
||||
|
||||
u8 GetCurrentCommand() const;
|
||||
|
||||
// Is a card anywhere the machine can sense it ?
|
||||
bool IsCardPresent() const;
|
||||
|
||||
// Is a card beyond the "ENTRY_SLOT" position ?
|
||||
bool IsCardLoaded() const;
|
||||
|
||||
virtual bool IsReadyForCard();
|
||||
|
||||
// Move the card through the machine.
|
||||
void MoveCard(R new_position);
|
||||
|
||||
// Unloads the card and moves it to the "ENTRY_SLOT" position.
|
||||
void EjectCard();
|
||||
|
||||
// Feeds a new card into the machine from an internal hopper.
|
||||
void DispenseCard();
|
||||
|
||||
// Commands
|
||||
virtual void Command_10_Initialize();
|
||||
void Command_20_ReadStatus();
|
||||
void Command_33_ReadData(); // Multi-track read.
|
||||
void Command_35_GetData(); // Read the entire card.
|
||||
void Command_40_Cancel();
|
||||
void Command_53_WriteData(); // Multi-track write.
|
||||
void Command_78_PrintSettings();
|
||||
void Command_7A_RegisterFont();
|
||||
void Command_7B_PrintImage();
|
||||
void Command_7C_PrintLine();
|
||||
void Command_7D_Erase(); // Erase the printed image.
|
||||
void Command_7E_PrintBarcode();
|
||||
void Command_80_EjectCard();
|
||||
void Command_A0_Clean();
|
||||
void Command_B0_DispenseCard();
|
||||
void Command_C0_ControlLED();
|
||||
void Command_C1_SetPrintRetry();
|
||||
virtual void Command_D0_ControlShutter();
|
||||
void Command_F0_GetVersion();
|
||||
|
||||
Settings* const m_card_settings;
|
||||
|
||||
// R P S values that are sent in every ENQUIRY response.
|
||||
Status m_status{};
|
||||
|
||||
// A byte sent in every ENQUIRY response.
|
||||
// TODO: What's the default value on just powered up hardware ?
|
||||
u8 m_current_command = 0;
|
||||
|
||||
// The data included in every ENQUIRY response.
|
||||
std::vector<u8> m_command_payload;
|
||||
|
||||
// The card track data in memory.
|
||||
using TrackData = std::optional<std::array<u8, TRACK_SIZE>>;
|
||||
std::array<TrackData, NUM_TRACKS> m_card_data;
|
||||
|
||||
// The parameter data of the command being processed.
|
||||
std::vector<u8> m_current_packet;
|
||||
|
||||
// The progress state of the current command.
|
||||
// 0 == not running a command.
|
||||
// TODO: Change this to emulated milliseconds or something.
|
||||
s32 m_current_step = 0;
|
||||
|
||||
// Used to delay re-insertion of cards after ejection.
|
||||
// This avoids having to expose card "Take"/"Insert" actions.
|
||||
DT m_time_since_machine_moved_card{};
|
||||
DT m_time_since_person_moved_card{};
|
||||
};
|
||||
|
||||
} // namespace MagCard
|
||||
@ -9,10 +9,8 @@
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "Common/Buffer.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/FileUtil.h"
|
||||
#include "Common/IOFile.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
#include "Common/MsgHandler.h"
|
||||
#include "Common/Swap.h"
|
||||
@ -28,6 +26,8 @@
|
||||
#include "Core/HW/EXI/EXI.h"
|
||||
#include "Core/HW/GCPad.h"
|
||||
#include "Core/HW/MMIO.h"
|
||||
#include "Core/HW/MagCard/C1231BR.h"
|
||||
#include "Core/HW/MagCard/C1231LR.h"
|
||||
#include "Core/HW/Memmap.h"
|
||||
#include "Core/HW/ProcessorInterface.h"
|
||||
#include "Core/HW/SI/SI.h"
|
||||
@ -160,6 +160,26 @@ CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices dev
|
||||
// Use count
|
||||
m_ic_card_data[0x28] = 0xFF;
|
||||
m_ic_card_data[0x29] = 0xFF;
|
||||
|
||||
// Magnetic Card Reader
|
||||
m_mag_card_settings.card_path = File::GetUserPath(D_TRIUSER_IDX);
|
||||
m_mag_card_settings.card_name = fmt::format("tricard_{}.bin", SConfig::GetInstance().GetGameID());
|
||||
|
||||
// TODO: Do any other games use the Magnetic Card Reader ?
|
||||
switch (AMMediaboard::GetGameType())
|
||||
{
|
||||
case FZeroAX:
|
||||
m_mag_card_reader = std::make_unique<MagCard::C1231BR>(&m_mag_card_settings);
|
||||
break;
|
||||
|
||||
case MarioKartGP:
|
||||
case MarioKartGP2:
|
||||
m_mag_card_reader = std::make_unique<MagCard::C1231LR>(&m_mag_card_settings);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 SI_XFER_LENGTH_MASK = 0x7f;
|
||||
@ -1147,506 +1167,43 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
|
||||
case GCAMCommand::SerialB:
|
||||
{
|
||||
DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)");
|
||||
|
||||
if (!validate_data_in_out(1, 0, "SerialB"))
|
||||
break;
|
||||
const u32 length = *data_in++;
|
||||
if (!validate_data_in_out(length, 0, "SerialB"))
|
||||
const u32 in_length = *data_in++;
|
||||
|
||||
static constexpr u32 max_packet_size = 0x2f;
|
||||
|
||||
// Also accounting for the 2-byte header.
|
||||
if (!validate_data_in_out(in_length, max_packet_size + 2, "SerialB"))
|
||||
break;
|
||||
if (length)
|
||||
|
||||
if (m_mag_card_reader)
|
||||
{
|
||||
// Send Card Reply
|
||||
if (length == 1 && data_in[0] == 0x05)
|
||||
{
|
||||
if (m_card_read_length)
|
||||
{
|
||||
if (!validate_data_in_out(0, 1, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = gcam_command;
|
||||
u32 read_length = m_card_read_length - m_card_read;
|
||||
// Append the data to our buffer.
|
||||
const auto prev_size = m_mag_card_in_buffer.size();
|
||||
m_mag_card_in_buffer.resize(prev_size + in_length);
|
||||
std::ranges::copy(std::span{data_in, in_length},
|
||||
m_mag_card_in_buffer.data() + prev_size);
|
||||
|
||||
if (AMMediaboard::GetGameType() == FZeroAX)
|
||||
{
|
||||
read_length = std::min<u32>(read_length, 0x2F);
|
||||
}
|
||||
|
||||
if (!validate_data_in_out(0, 1, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = read_length; // 0x2F (max size per packet)
|
||||
|
||||
if (!validate_data_in_out(0, read_length, "SerialB"))
|
||||
break;
|
||||
if (u64{m_card_read} + read_length > sizeof(m_card_read_packet))
|
||||
{
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_AMBB,
|
||||
"GC-AM: Command SerialB, m_card_read_packet overflow:\n"
|
||||
" - m_card_read_packet = {}\n"
|
||||
" - m_card_read = {}\n"
|
||||
" - read_length = {}",
|
||||
fmt::ptr(m_card_read_packet), m_card_read, read_length);
|
||||
data_in = data_in_end;
|
||||
break;
|
||||
}
|
||||
memcpy(data_out.data() + data_offset, m_card_read_packet + m_card_read,
|
||||
read_length);
|
||||
|
||||
data_offset += read_length;
|
||||
m_card_read += read_length;
|
||||
|
||||
if (m_card_read >= m_card_read_length)
|
||||
m_card_read_length = 0;
|
||||
|
||||
data_in += length;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!validate_data_in_out(0, 5, "SerialB"))
|
||||
break;
|
||||
|
||||
data_out[data_offset++] = gcam_command;
|
||||
const u32 command_length_offset = data_offset;
|
||||
data_out[data_offset++] = 0x00; // len
|
||||
|
||||
data_out[data_offset++] = 0x02;
|
||||
const u32 checksum_start = data_offset;
|
||||
|
||||
data_out[data_offset++] = 0x00; // 0x00 len
|
||||
|
||||
data_out[data_offset++] = m_card_command; // 0x01 command
|
||||
|
||||
switch (CARDCommand(m_card_command))
|
||||
{
|
||||
case CARDCommand::Init:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x00; // 0x02
|
||||
data_out[data_offset++] = 0x30; // 0x03
|
||||
break;
|
||||
case CARDCommand::GetState:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x20 | m_card_bit; // 0x02
|
||||
|
||||
// bit 0: Please take your card
|
||||
// bit 1: Endless waiting causes UNK_E to be called
|
||||
data_out[data_offset++] = 0x00; // 0x03
|
||||
break;
|
||||
case CARDCommand::Read:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x02; // 0x02
|
||||
data_out[data_offset++] = 0x53; // 0x03
|
||||
break;
|
||||
case CARDCommand::IsPresent:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x22; // 0x02
|
||||
data_out[data_offset++] = 0x30; // 0x03
|
||||
break;
|
||||
case CARDCommand::Write:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x02; // 0x02
|
||||
data_out[data_offset++] = 0x00; // 0x03
|
||||
break;
|
||||
case CARDCommand::SetPrintParam:
|
||||
case CARDCommand::RegisterFont:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x00; // 0x02
|
||||
data_out[data_offset++] = 0x00; // 0x03
|
||||
break;
|
||||
case CARDCommand::WriteInfo:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x02; // 0x02
|
||||
data_out[data_offset++] = 0x00; // 0x03
|
||||
break;
|
||||
case CARDCommand::Erase:
|
||||
// TODO: CARDCommand::Erase is not handled.
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_AMBB, "CARDCommand::Erase is not handled.");
|
||||
break;
|
||||
case CARDCommand::Eject:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
if (AMMediaboard::GetGameType() == FZeroAX)
|
||||
{
|
||||
data_out[data_offset++] = 0x01; // 0x02
|
||||
}
|
||||
else
|
||||
{
|
||||
data_out[data_offset++] = 0x31; // 0x02
|
||||
}
|
||||
data_out[data_offset++] = 0x30; // 0x03
|
||||
break;
|
||||
case CARDCommand::Clean:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x02; // 0x02
|
||||
data_out[data_offset++] = 0x00; // 0x03
|
||||
break;
|
||||
case CARDCommand::Load:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x02; // 0x02
|
||||
data_out[data_offset++] = 0x30; // 0x03
|
||||
break;
|
||||
case CARDCommand::SetShutter:
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x00; // 0x02
|
||||
data_out[data_offset++] = 0x00; // 0x03
|
||||
break;
|
||||
}
|
||||
|
||||
if (!validate_data_in_out(0, 3, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x30; // 0x04
|
||||
data_out[data_offset++] = 0x00; // 0x05
|
||||
|
||||
data_out[data_offset++] = 0x03; // 0x06
|
||||
|
||||
data_out[checksum_start] = data_offset - checksum_start; // 0x00 len
|
||||
|
||||
if (!validate_data_in_out(0, 1, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset] = 0; // 0x07
|
||||
for (u32 i = 0; i < data_out[checksum_start]; ++i)
|
||||
data_out[data_offset] ^= data_out[checksum_start + i];
|
||||
|
||||
if (!validate_data_in_out(0, 1, "SerialB"))
|
||||
break;
|
||||
data_offset++;
|
||||
|
||||
data_out[command_length_offset] = data_out[checksum_start] + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!validate_data_in_out(length, 0, "SerialB"))
|
||||
break;
|
||||
if (u64{m_card_offset} + length > std::size(m_card_buffer))
|
||||
{
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_AMBB,
|
||||
"GC-AM: Command SerialB, m_card_buffer overflow:\n"
|
||||
" - m_card_buffer = {}\n"
|
||||
" - m_card_offset = {}\n"
|
||||
" - length = {}",
|
||||
fmt::ptr(m_card_buffer), m_card_offset, length);
|
||||
data_in = data_in_end;
|
||||
break;
|
||||
}
|
||||
for (u32 i = 0; i < length; ++i)
|
||||
m_card_buffer[m_card_offset + i] = data_in[i];
|
||||
|
||||
m_card_offset += length;
|
||||
|
||||
// Check if we got a complete command
|
||||
if (m_card_buffer[0] == 0x02)
|
||||
{
|
||||
if (m_card_offset < 2)
|
||||
{
|
||||
ERROR_LOG_FMT(
|
||||
SERIALINTERFACE_AMBB,
|
||||
"GC-AM: Command SerialB, m_card_buffer overflow (m_card_offset < 2):\n"
|
||||
" - m_card_buffer = {}\n"
|
||||
" - m_card_offset = {}\n"
|
||||
" - length = {}",
|
||||
fmt::ptr(m_card_buffer), m_card_offset, length);
|
||||
data_in = data_in_end;
|
||||
break;
|
||||
}
|
||||
if (const u32 offset = m_card_offset - 2;
|
||||
m_card_buffer[1] == offset && m_card_buffer[offset] == 0x03)
|
||||
{
|
||||
m_card_command = m_card_buffer[2];
|
||||
|
||||
switch (CARDCommand(m_card_command))
|
||||
{
|
||||
case CARDCommand::Init:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init");
|
||||
|
||||
m_card_write_length = 0;
|
||||
m_card_bit = 0;
|
||||
m_card_memory_size = 0;
|
||||
m_card_state_call_count = 0;
|
||||
break;
|
||||
case CARDCommand::GetState:
|
||||
{
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})",
|
||||
m_card_bit);
|
||||
|
||||
if (m_card_memory_size == 0)
|
||||
{
|
||||
const std::string card_filename(
|
||||
fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),
|
||||
SConfig::GetInstance().GetGameID()));
|
||||
|
||||
if (File::Exists(card_filename))
|
||||
{
|
||||
File::IOFile card(card_filename, "rb+");
|
||||
m_card_memory_size = static_cast<u32>(card.GetSize());
|
||||
if (m_card_memory_size > sizeof(m_card_memory))
|
||||
{
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_CARD,
|
||||
"GC-AM: Command CARD GetState overflow:\n"
|
||||
" - file name = {}\n"
|
||||
" - file size = {}\n"
|
||||
" - card size = {}",
|
||||
card_filename, m_card_memory_size, sizeof(m_card_memory));
|
||||
data_in = data_in_end;
|
||||
break;
|
||||
}
|
||||
card.ReadBytes(m_card_memory, m_card_memory_size);
|
||||
card.Close();
|
||||
|
||||
m_card_is_inserted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size)
|
||||
{
|
||||
m_card_state_call_count++;
|
||||
if (m_card_state_call_count > 10)
|
||||
{
|
||||
if (m_card_bit & 2)
|
||||
m_card_bit &= ~2u;
|
||||
else
|
||||
m_card_bit |= 2;
|
||||
|
||||
m_card_state_call_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_card_clean == 1)
|
||||
{
|
||||
m_card_clean = 2;
|
||||
}
|
||||
else if (m_card_clean == 2)
|
||||
{
|
||||
const std::string card_filename(
|
||||
fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),
|
||||
SConfig::GetInstance().GetGameID()));
|
||||
|
||||
if (File::Exists(card_filename))
|
||||
{
|
||||
m_card_memory_size = static_cast<u32>(File::GetSize(card_filename));
|
||||
if (m_card_memory_size)
|
||||
{
|
||||
if (AMMediaboard::GetGameType() == FZeroAX)
|
||||
{
|
||||
m_card_bit = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_card_bit = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_card_clean = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CARDCommand::IsPresent:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent");
|
||||
break;
|
||||
case CARDCommand::RegisterFont:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont");
|
||||
break;
|
||||
case CARDCommand::Load:
|
||||
{
|
||||
const u8 mode = m_card_buffer[6];
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})", mode);
|
||||
break;
|
||||
}
|
||||
case CARDCommand::Clean:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean");
|
||||
m_card_clean = 1;
|
||||
break;
|
||||
case CARDCommand::Read:
|
||||
{
|
||||
const u8 mode = m_card_buffer[6];
|
||||
const u8 bitmode = m_card_buffer[7];
|
||||
const u8 track = m_card_buffer[8];
|
||||
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD,
|
||||
"GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode, bitmode,
|
||||
track);
|
||||
|
||||
// Prepare read packet
|
||||
memset(m_card_read_packet, 0, 0xDB);
|
||||
|
||||
const std::string card_filename(
|
||||
fmt::format("{}tricard_{}.bin", File::GetUserPath(D_TRIUSER_IDX),
|
||||
SConfig::GetInstance().GetGameID()));
|
||||
|
||||
if (File::Exists(card_filename))
|
||||
{
|
||||
File::IOFile card(card_filename, "rb+");
|
||||
if (m_card_memory_size == 0)
|
||||
{
|
||||
m_card_memory_size = static_cast<u32>(card.GetSize());
|
||||
}
|
||||
|
||||
if (m_card_memory_size > sizeof(m_card_memory))
|
||||
{
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_CARD,
|
||||
"GC-AM: Command CARD Read overflow:\n"
|
||||
" - file name = {}\n"
|
||||
" - file size = {}\n"
|
||||
" - card size = {}",
|
||||
card_filename, m_card_memory_size, sizeof(m_card_memory));
|
||||
data_in = data_in_end;
|
||||
break;
|
||||
}
|
||||
card.ReadBytes(m_card_memory, m_card_memory_size);
|
||||
card.Close();
|
||||
|
||||
m_card_is_inserted = true;
|
||||
}
|
||||
else if (m_card_memory_size > sizeof(m_card_memory))
|
||||
{
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_CARD,
|
||||
"GC-AM: Command CARD Read overflow:\n"
|
||||
" - requested size = {}\n"
|
||||
" - card size = {}",
|
||||
m_card_memory_size, sizeof(m_card_memory));
|
||||
data_in = data_in_end;
|
||||
break;
|
||||
}
|
||||
|
||||
m_card_read_packet[0] = 0x02; // SUB CMD
|
||||
m_card_read_packet[1] = 0x00; // SUB CMDLen
|
||||
|
||||
m_card_read_packet[2] = 0x33; // CARD CMD
|
||||
|
||||
if (m_card_is_inserted) // CARD Status
|
||||
{
|
||||
m_card_read_packet[3] = 0x31;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_card_read_packet[3] = 0x30;
|
||||
}
|
||||
|
||||
m_card_read_packet[4] = 0x30;
|
||||
m_card_read_packet[5] = 0x30;
|
||||
|
||||
u32 packet_offset = 6;
|
||||
// Data reply
|
||||
static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 6);
|
||||
memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size);
|
||||
packet_offset += m_card_memory_size;
|
||||
|
||||
static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 7);
|
||||
m_card_read_packet[packet_offset++] = 0x03;
|
||||
|
||||
m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen
|
||||
|
||||
static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 8);
|
||||
for (u32 i = 0; i < packet_offset - 1; ++i)
|
||||
m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i];
|
||||
|
||||
static_assert(sizeof(m_card_read_packet) >= sizeof(m_card_memory) + 9);
|
||||
packet_offset++;
|
||||
|
||||
m_card_read_length = packet_offset;
|
||||
m_card_read = 0;
|
||||
break;
|
||||
}
|
||||
case CARDCommand::Write:
|
||||
{
|
||||
const u8 mode = m_card_buffer[6];
|
||||
const u8 bitmode = m_card_buffer[7];
|
||||
const u8 track = m_card_buffer[8];
|
||||
|
||||
m_card_memory_size = m_card_buffer[1] - 9;
|
||||
if (m_card_memory_size > sizeof(m_card_memory))
|
||||
{
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_CARD,
|
||||
"GC-AM: Command CARD Write overflow:\n"
|
||||
" - write size = {}\n"
|
||||
" - card size = {}",
|
||||
m_card_memory_size, sizeof(m_card_memory));
|
||||
data_in = data_in_end;
|
||||
break;
|
||||
}
|
||||
|
||||
static_assert(sizeof(m_card_buffer) >= sizeof(m_card_memory) + 9);
|
||||
memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size);
|
||||
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD,
|
||||
"GC-AM: Command CARD Write: {:02X} {:02X} {:02X} {}", mode,
|
||||
bitmode, track, m_card_memory_size);
|
||||
|
||||
const std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" +
|
||||
SConfig::GetInstance().GetGameID() + ".bin");
|
||||
|
||||
File::IOFile card(card_filename, "wb+");
|
||||
card.WriteBytes(m_card_memory, m_card_memory_size);
|
||||
card.Close();
|
||||
|
||||
m_card_bit = 2;
|
||||
|
||||
m_card_state_call_count = 0;
|
||||
break;
|
||||
}
|
||||
case CARDCommand::SetPrintParam:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetPrintParam");
|
||||
break;
|
||||
case CARDCommand::WriteInfo:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD WriteInfo");
|
||||
break;
|
||||
case CARDCommand::Erase:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Erase");
|
||||
break;
|
||||
case CARDCommand::Eject:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Eject");
|
||||
if (AMMediaboard::GetGameType() != FZeroAX)
|
||||
{
|
||||
m_card_bit = 0;
|
||||
}
|
||||
break;
|
||||
case CARDCommand::SetShutter:
|
||||
NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetShutter");
|
||||
if (AMMediaboard::GetGameType() != FZeroAX)
|
||||
{
|
||||
m_card_bit = 0;
|
||||
}
|
||||
// Close
|
||||
if (m_card_buffer[6] == 0x30)
|
||||
{
|
||||
m_card_shutter = false;
|
||||
}
|
||||
// Open
|
||||
else if (m_card_buffer[6] == 0x31)
|
||||
{
|
||||
m_card_shutter = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:Unhandled command!");
|
||||
ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:[{:08X}]", m_card_command);
|
||||
// hexdump( m_card_buffer, m_card_offset );
|
||||
break;
|
||||
}
|
||||
m_card_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validate_data_in_out(0, 3, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = 0x32;
|
||||
data_out[data_offset++] = 0x01; // len
|
||||
data_out[data_offset++] = 0x06; // OK
|
||||
}
|
||||
// Send and receive data with the magnetic card reader.
|
||||
m_mag_card_reader->Process(&m_mag_card_in_buffer, &m_mag_card_out_buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!validate_data_in_out(0, 2, "SerialB"))
|
||||
break;
|
||||
data_out[data_offset++] = gcam_command;
|
||||
data_out[data_offset++] = 0x00; // len
|
||||
}
|
||||
data_in += length;
|
||||
|
||||
data_in += in_length;
|
||||
const auto out_length = std::min(u32(m_mag_card_out_buffer.size()), max_packet_size);
|
||||
|
||||
// Write the 2-byte header.
|
||||
data_out[data_offset++] = gcam_command;
|
||||
data_out[data_offset++] = u8(out_length);
|
||||
|
||||
// Write the data.
|
||||
std::copy_n(m_mag_card_out_buffer.data(), out_length, data_out.data() + data_offset);
|
||||
data_offset += out_length;
|
||||
|
||||
// Remove the data from our buffer.
|
||||
m_mag_card_out_buffer.erase(m_mag_card_out_buffer.begin(),
|
||||
m_mag_card_out_buffer.begin() + s32(out_length));
|
||||
break;
|
||||
}
|
||||
case GCAMCommand::JVSIOA:
|
||||
@ -2762,24 +2319,14 @@ void CSIDevice_AMBaseboard::DoState(PointerWrap& p)
|
||||
p.Do(m_ic_write_offset);
|
||||
p.Do(m_ic_write_size);
|
||||
|
||||
p.Do(m_card_memory);
|
||||
p.Do(m_card_read_packet);
|
||||
p.Do(m_card_buffer);
|
||||
// Magnetic Card Reader
|
||||
if (m_mag_card_reader)
|
||||
{
|
||||
m_mag_card_reader->DoState(p);
|
||||
|
||||
// Setup CARD
|
||||
p.Do(m_card_memory_size);
|
||||
p.Do(m_card_is_inserted);
|
||||
|
||||
p.Do(m_card_command);
|
||||
p.Do(m_card_clean);
|
||||
p.Do(m_card_write_length);
|
||||
p.Do(m_card_wrote);
|
||||
p.Do(m_card_read_length);
|
||||
p.Do(m_card_read);
|
||||
p.Do(m_card_bit);
|
||||
p.Do(m_card_shutter);
|
||||
p.Do(m_card_state_call_count);
|
||||
p.Do(m_card_offset);
|
||||
p.Do(m_mag_card_in_buffer);
|
||||
p.Do(m_mag_card_out_buffer);
|
||||
}
|
||||
|
||||
// Serial
|
||||
p.Do(m_wheel_init);
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Core/HW/GCPad.h"
|
||||
#include "Core/HW/MagCard/MagneticCardReader.h"
|
||||
#include "Core/HW/SI/SI_Device.h"
|
||||
|
||||
#include "InputCommon/GCPadStatus.h"
|
||||
|
||||
namespace Movie
|
||||
@ -274,24 +276,13 @@ private:
|
||||
u32 m_ic_write_offset = 0;
|
||||
u32 m_ic_write_size = 0;
|
||||
|
||||
u8 m_card_memory[0xD0] = {};
|
||||
u8 m_card_read_packet[0xDB] = {};
|
||||
u8 m_card_buffer[0x100] = {};
|
||||
// Magnetic Card Reader
|
||||
MagCard::MagneticCardReader::Settings m_mag_card_settings;
|
||||
|
||||
// Setup CARD
|
||||
u32 m_card_memory_size = 0;
|
||||
bool m_card_is_inserted = false;
|
||||
std::vector<u8> m_mag_card_in_buffer;
|
||||
std::vector<u8> m_mag_card_out_buffer;
|
||||
|
||||
u32 m_card_command = 0;
|
||||
u32 m_card_clean = 0;
|
||||
u32 m_card_write_length = 0;
|
||||
u32 m_card_wrote = 0;
|
||||
u32 m_card_read_length = 0;
|
||||
u32 m_card_read = 0;
|
||||
u32 m_card_bit = 0;
|
||||
bool m_card_shutter = true; // Open
|
||||
u32 m_card_state_call_count = 0;
|
||||
u8 m_card_offset = 0;
|
||||
std::unique_ptr<MagCard::MagneticCardReader> m_mag_card_reader;
|
||||
|
||||
// Serial
|
||||
u32 m_wheel_init = 0;
|
||||
|
||||
@ -317,6 +317,9 @@
|
||||
<ClInclude Include="Core\HW\HSP\HSP_DeviceARAMExpansion.h" />
|
||||
<ClInclude Include="Core\HW\HSP\HSP_DeviceNull.h" />
|
||||
<ClInclude Include="Core\HW\HW.h" />
|
||||
<ClInclude Include="Core\HW\MagCard\C1231BR.h" />
|
||||
<ClInclude Include="Core\HW\MagCard\C1231LR.h" />
|
||||
<ClInclude Include="Core\HW\MagCard\MagneticCardReader.h" />
|
||||
<ClInclude Include="Core\HW\Memmap.h" />
|
||||
<ClInclude Include="Core\HW\MemoryInterface.h" />
|
||||
<ClInclude Include="Core\HW\MMIO.h" />
|
||||
@ -1015,6 +1018,9 @@
|
||||
<ClCompile Include="Core\HW\HSP\HSP_DeviceARAMExpansion.cpp" />
|
||||
<ClCompile Include="Core\HW\HSP\HSP_DeviceNull.cpp" />
|
||||
<ClCompile Include="Core\HW\HW.cpp" />
|
||||
<ClCompile Include="Core\HW\MagCard\C1231BR.cpp" />
|
||||
<ClCompile Include="Core\HW\MagCard\C1231LR.cpp" />
|
||||
<ClCompile Include="Core\HW\MagCard\MagneticCardReader.cpp" />
|
||||
<ClCompile Include="Core\HW\Memmap.cpp" />
|
||||
<ClCompile Include="Core\HW\MemoryInterface.cpp" />
|
||||
<ClCompile Include="Core\HW\MMIO.cpp" />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user