Skip to content

Adding Opera (3DO) as emulation core in Bizhawk #4264

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 82 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
c82506a
Adding base
SergioMartin86 Mar 8, 2025
fc8fa94
Progress with opera and adding 3do firmwares
SergioMartin86 Mar 9, 2025
b90d6f6
Adding missing callbacks
SergioMartin86 Mar 9, 2025
bc213ed
Adding missing callbacks
SergioMartin86 Mar 9, 2025
63a5ac6
3DO core starting
SergioMartin86 Mar 9, 2025
d3be088
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 9, 2025
e7aeebb
Now passing inputs
SergioMartin86 Mar 10, 2025
98f2191
Passing gamepad inputs
SergioMartin86 Mar 10, 2025
57af521
Fixing input names
SergioMartin86 Mar 10, 2025
face41a
Now supporting mouse
SergioMartin86 Mar 11, 2025
400f4b9
Adding mouse support
SergioMartin86 Mar 11, 2025
935a4d1
Added flightstick
SergioMartin86 Mar 11, 2025
4a507cb
Added flight stick
SergioMartin86 Mar 11, 2025
d85bf7e
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 11, 2025
5994501
Adding last inputs and fixing audio
SergioMartin86 Mar 12, 2025
2cef3f1
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 12, 2025
b09d17e
Adding controllers and fixing audio
SergioMartin86 Mar 12, 2025
6866911
Adding orbatak
SergioMartin86 Mar 12, 2025
cce05d7
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 12, 2025
4c97d6d
Fixing mnemonics, added orbatak
SergioMartin86 Mar 12, 2025
707f890
Adding font roms
SergioMartin86 Mar 12, 2025
8f6ffe5
Adding font roms
SergioMartin86 Mar 12, 2025
95b7a93
Adding region
SergioMartin86 Mar 12, 2025
3adcba0
Adding region
SergioMartin86 Mar 12, 2025
6445eea
Fixing mouse issue
SergioMartin86 Mar 13, 2025
3fb18a6
Fixed initialization bug in fresh installs
SergioMartin86 Mar 13, 2025
634fcd7
Setting mouse to relative inputs
SergioMartin86 Mar 13, 2025
14d77aa
Using mouse as relative
SergioMartin86 Mar 13, 2025
6375152
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 13, 2025
339b3fa
Bypassing bios checks to enable bizhawk to pass whatever bios it wants
SergioMartin86 Mar 13, 2025
62922cb
Adding default inputs for 3DO
SergioMartin86 Mar 13, 2025
d141155
Adding detection of nvram changes
SergioMartin86 Mar 14, 2025
59c1f7c
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 14, 2025
3937334
Adding lag frame and nvram saving
SergioMartin86 Mar 14, 2025
24f496b
Adding cd use detection
SergioMartin86 Mar 14, 2025
4505b66
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 14, 2025
6523c94
Adding cdrom light
SergioMartin86 Mar 14, 2025
1201035
Using cd callbacks
SergioMartin86 Mar 15, 2025
26f9501
Using cd callbacks
SergioMartin86 Mar 15, 2025
95dec71
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 15, 2025
db9d212
Bypassing image name check
SergioMartin86 Mar 15, 2025
0e5030c
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 15, 2025
7a4be1d
Adding multidisc support
SergioMartin86 Mar 15, 2025
58dc832
merging upstream
SergioMartin86 Mar 15, 2025
f74d7ec
Removing 3do-iso special extension
SergioMartin86 Mar 15, 2025
4b68111
Fixing build
SergioMartin86 Mar 15, 2025
02e8313
Attempting to add disc swapping
SergioMartin86 Mar 15, 2025
158f91e
Trying to add multidisc support
SergioMartin86 Mar 15, 2025
ad3636a
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 15, 2025
4fcb12b
Uncommenting cd functions
SergioMartin86 Mar 15, 2025
90f0673
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 15, 2025
5f3ed39
revert unrelated changes
Morilli Mar 16, 2025
833663d
misc unmanaged integration
Morilli Mar 16, 2025
5c31c3a
Update waterbox-cores.yml
Morilli Mar 16, 2025
f1222c2
Added reset button, removed eject/insert
SergioMartin86 Mar 16, 2025
543e1e5
Added reset button, removed eject/insert
SergioMartin86 Mar 16, 2025
fe75a5c
Fix line endings in `Bk2MnemonicLookup.cs` changes
YoshiRulz Mar 16, 2025
e0f6f05
Fix indentation
YoshiRulz Mar 16, 2025
f93584c
Fix spaces in `Opera.cs`
YoshiRulz Mar 16, 2025
633c2c3
Alphabetise
YoshiRulz Mar 16, 2025
fba0d59
Make `Opera`'s `ISaveRam` implementations `override`
YoshiRulz Feb 23, 2025
39f566d
Misc. code style changes
YoshiRulz Mar 16, 2025
710d0ba
Fix casting array index to `uint` for comparisons
YoshiRulz Mar 16, 2025
fecbc9e
Adding default framerate and removing message duration parameter
SergioMartin86 Mar 18, 2025
b7260d1
Refactoring input parsing and fixing some errors
SergioMartin86 Mar 18, 2025
0dcdcaf
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 18, 2025
7fa5f53
Fixing indentation
SergioMartin86 Mar 18, 2025
2669fda
Exposing non volatile ram
SergioMartin86 Mar 18, 2025
b503e08
Adding automatic sram management
SergioMartin86 Mar 18, 2025
aa88205
Now letting bk manage saveram automatically
SergioMartin86 Mar 18, 2025
ea69752
Fixing indentation and removing unncessary flag
SergioMartin86 Mar 18, 2025
3abad35
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 18, 2025
683ae9b
Removing dead code
SergioMartin86 Mar 18, 2025
6e24c6b
Removing dead code
SergioMartin86 Mar 18, 2025
0de3211
Removing unnecessary mnemonic fallbacks
SergioMartin86 Mar 18, 2025
06a3684
Keep fixing indentation errors
SergioMartin86 Mar 18, 2025
d286bd0
Removing struct for memory areas
SergioMartin86 Mar 18, 2025
496ca8c
Adding proper detection of input reading
SergioMartin86 Mar 19, 2025
ed08e48
Changing namespace to not have an underscore
SergioMartin86 Mar 19, 2025
701931a
Merge branch 'opera' of github.com:SergioMartin86/BizHawk into opera
SergioMartin86 Mar 19, 2025
f3ca025
fix whitespace in PlatformFrameRates
Morilli Mar 22, 2025
adc640c
make waterbox function non-virtual
Morilli Mar 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/waterbox-cores.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
git submodule update --init uae/libretro-uae;
git submodule update --init stella/core;
git submodule update --init dsda/core;
git submodule update --init opera/opera-libretro;
- name: Install clang 18
run: wget https://apt.llvm.org/llvm.sh;
chmod u+x llvm.sh;
Expand Down Expand Up @@ -68,6 +69,7 @@ jobs:
Assets/dll/libsnes.wbx.zst
Assets/dll/melonDS.wbx.zst
Assets/dll/ngp.wbx.zst
Assets/dll/opera.wbx.zst
Assets/dll/pcfx.wbx.zst
Assets/dll/picodrive.wbx.zst
Assets/dll/shock.wbx.zst
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,6 @@
path = waterbox/dsda/core
url = https://github.com/TASEmulators/dsda-doom.git
branch = wbx
[submodule "waterbox/opera/opera-libretro"]
path = waterbox/opera/opera-libretro
url = https://github.com/TASEmulators/opera-libretro.git
42 changes: 42 additions & 0 deletions Assets/defctrl.json
Original file line number Diff line number Diff line change
Expand Up @@ -1108,6 +1108,48 @@
"Mult": 1.0,
"Deadzone": 0.0
}
},
"3DO Controller": {
"P1 Mouse X": {
"Value": "RMouse X",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Mouse Y": {
"Value": "RMouse Y",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Flight Stick Horizontal Axis": {
"Value": "RMouse X",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Flight Stick Vertical Axis": {
"Value": "RMouse Y",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Light Gun Screen X": {
"Value": "RMouse X",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Light Gun Screen Y": {
"Value": "RMouse Y",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Trackball X": {
"Value": "RMouse X",
"Mult": 1.0,
"Deadzone": 0.0
},
"P1 Trackball Y": {
"Value": "RMouse Y",
"Mult": 1.0,
"Deadzone": 0.0
}
}
},
"AllTrollersFeedbacks": {
Expand Down
Binary file added Assets/dll/opera.wbx.zst
Binary file not shown.
1 change: 1 addition & 0 deletions src/BizHawk.Client.Common/movie/PlatformFrameRates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public static class PlatformFrameRates

private static readonly Dictionary<string, double> Rates = new Dictionary<string, double>
{
["3DO"] = 60.0, // The emulator (Opera-Libretro) reports exact 60.0 for NTSC https://github.com/libretro/opera-libretro/blob/67a29e60a4d194b675c9272b21b61eaa022f3ba3/libopera/opera_region.c#L10
["3DS"] = 268111856.0 / 4481136.0, // 59.8312249394
// while the number of scanlines per frame is software controlled and variable, we
// enforce exactly 262 (NTSC) 312 (PAL) per reference time frame
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Client.EmuHawk/config/FirmwareConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public partial class FirmwareConfig : Form, IDialogParent
// Redundant with SystemLookup? Not so fast. That data drives things. This is one step abstracted. Don't be such a smart guy. Keep this redundant list up to date.
private static readonly Dictionary<string, string> SystemGroupNames = new Dictionary<string, string>
{
["3DO"] = "3DO / 3DO Arcade / 3DO M2",
["Amiga"] = "Amiga",
["NES"] = "NES",
["SNES"] = "SNES",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public MultiDiskBundler()
InitializeComponent();
Icon = ToolIcon;
SystemDropDown.Items.AddRange([
VSystemID.Raw._3DO,
VSystemID.Raw.Amiga,
VSystemID.Raw.AmstradCPC,
VSystemID.Raw.AppleII,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,25 @@ public static string LookupAxis(string button, string systemId)

private static readonly Dictionary<string, Dictionary<string, char>> SystemOverrides = new Dictionary<string, Dictionary<string, char>>
{
[VSystemID.Raw._3DO] = new()
{
["LT"] = 'l',
["RT"] = 'r',
["P"] = 'P',
["Trigger"] = 'T',
["Reload"] = 'R',
["Is Off-Screen"] = 'O',
["Aux A"] = 'A',
["Start P1"] = '1',
["Start P2"] = '2',
["Coin P1"] = 'C',
["Coin P2"] = 'c',
["Service"] = 'S',
["Fourth Button"] = '4',
["Next Disc"] = '>',
["Prev Disc"] = '<',
},

[VSystemID.Raw.NES] = new()
{
["FDS Eject"] = 'E',
Expand Down Expand Up @@ -863,6 +882,16 @@ public static string LookupAxis(string button, string systemId)

private static readonly Dictionary<string, Dictionary<string, string>> AxisSystemOverrides = new Dictionary<string, Dictionary<string, string>>
{
[VSystemID.Raw._3DO] = new()
{
["Flight Stick Horizontal Axis"] = "fsX",
["Flight Stick Vertical Axis"] = "fsY",
["Flight Stick Altitude Axis"] = "fsZ",
["Light Gun Screen X"] = "lgX",
["Light Gun Screen Y"] = "lgY",
["Trackball X"] = "tX",
["Trackball Y"] = "tY",
},
[VSystemID.Raw.A78] = new()
{
["VPos"] = "X",
Expand Down
54 changes: 54 additions & 0 deletions src/BizHawk.Emulation.Common/Database/FirmwareDatabase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,60 @@ void AddPatchAndMaybeReverse(FirmwarePatchOption fpo)
FirmwareAndOption("1E5B0B2441A4979B6966D942B20CC76C413B8C5E", 2048, "32X", "M", "32X_M_BIOS.BIN", "32x SH2 MASTER BIOS");
FirmwareAndOption("4103668C1BBD66C5E24558E73D4F3F92061A109A", 1024, "32X", "S", "32X_S_BIOS.BIN", "32x SH2 SLAVE BIOS");

// 3DO
// Information obtained from https://3dodev.com/software/roms

Firmware("3DO", "Panasonic_FZ1_U", "Panasonic FZ-1 (U)");
Option("3DO", "Panasonic_FZ1_U", File("34BF189111295F74D7B7DFC1F304D98B8D36325A", 1048576, "panafz1.bin", "Panasonic FZ-1 (U)"), FirmwareOptionStatus.Ideal);
Option("3DO", "Panasonic_FZ1_U", File("DE3C55490733E6C69724D87E149B52ED955638ED", 1048576, "panafz1_dev_0.9.bin", "Panasonic FZ-1 (U) v0.9 dev build"));
Option("3DO", "Panasonic_FZ1_U", File("4CB4EE36E0F5BC0995D34992B4F241C420D49B2E", 1048576, "panafz1_dev.bin", "Panasonic FZ-1 (U) dev build"));

Firmware("3DO", "Panasonic_FZ1_E", "Panasonic FZ-1 (E)");
Option("3DO", "Panasonic_FZ1_E", File("1D0DB81E171EBC1D07CEFC8CE8AB082306186E56", 1048576, "panafz1e.bin", "Panasonic FZ-1 (E)"), FirmwareOptionStatus.Ideal);
Option("3DO", "Panasonic_FZ1_E", File("4696951E492E5526772A860EA2C0F35411A80927", 1048576, "panafz1e-unencrypted.bin", "Panasonic FZ-1 (E) (unencrypted)"));

Firmware("3DO", "Panasonic_FZ1_J", "Panasonic FZ-1 (J)");
Option("3DO", "Panasonic_FZ1_J", File("EC7EC62D60EC0459A14ED56EBC66761EF3C80EFC", 1048576, "panafz1j.bin", "Panasonic FZ-1 (J)"), FirmwareOptionStatus.Ideal);
Option("3DO", "Panasonic_FZ1_J", File("A417587AE3B0B8EF00C830920C21AF8BEE88E419", 1048576, "panafz1j - norsa.bin", "Panasonic FZ-1(J)(RSA check disabled)"));

Firmware("3DO", "Panasonic_FZ10_U", "Panasonic FZ-10 (U)");
Option("3DO", "Panasonic_FZ10_U", File("3C912300775D1AD730DC35757E279C274C0ACAAD", 1048576, "panafz10.bin", "Panasonic FZ-10 (U)"), FirmwareOptionStatus.Ideal);
Option("3DO", "Panasonic_FZ10_U", File("F05E642322C03694F06A809C0B90FC27AC73C002", 1048576, "panafz10-norsa.bin", "Panasonic FZ-10 (U) (RSA check disabled)"));

Firmware("3DO", "Panasonic_FZ10_E", "Panasonic FZ-10 (E)");
Option("3DO", "Panasonic_FZ10_E", File("A900371F0CDCDC03F79557F11D406FD71251A5FD", 1048576, "panafz10e-anvil.bin", "Panasonic FZ-10 (E) [ANVIL]"), FirmwareOptionStatus.Ideal);
Option("3DO", "Panasonic_FZ10_E", File("2765C7B4557CC838B32567D2428D088980295159", 1048576, "panafz10e-anvil-norsa.bin", "Panasonic FZ-10 (E) [ANVIL] (RSA check disabled)"));

Firmware("3DO", "Panasonic_FZ10_J", "Panasonic FZ-10 (J)");
Option("3DO", "Panasonic_FZ10_J", File("FE7F9C9C6A98910013BF13F2CF798DE9FEA52ACD", 1048576, "panafz10j.bin", "Panasonic FZ-10 (J)"), FirmwareOptionStatus.Ideal);

Firmware("3DO", "Goldstar_GDO101P", "Goldstar GDO-101P");
Option("3DO", "Goldstar_GDO101P", File("C4A2E5336F77FB5F743DE1EEA2CDA43675EE2DE7", 1048576, "goldstar.bin", "Goldstar GDO-101P"), FirmwareOptionStatus.Ideal);

Firmware("3DO", "Goldstar_FC1", "Goldstar FC-1");
Option("3DO", "Goldstar_FC1", File("8EF7503C948314D242DA47B7FDC272F68DAC2AEE", 1048576, "goldstar_fc1_enc.bin", "Goldstar FC-1 (encrypted)"), FirmwareOptionStatus.Ideal);

Firmware("3DO", "Sanyo_IMP21J_Try", "Sanyo IMP-21J Try");
Option("3DO", "Sanyo_IMP21J_Try", File("B01C53DA256DDE43FFEC4AD3FC3ADFA8D635E943", 1048576, "sanyotry.bin", "Sanyo IMP-21J Try"), FirmwareOptionStatus.Ideal);

Firmware("3DO", "Sanyo_HC21", "Sanyo HC-21");
Option("3DO", "Sanyo_HC21", File("C389AF32BCADF0D86826927DC3D20B7072F90069", 1048576, "sanyo_hc21_b3_unenc.bin", "Sanyo HC-21 B3 (unencrypted)"), FirmwareOptionStatus.Ideal);
Option("3DO", "Sanyo_HC21", File("29C40515DC1174FF13975BAA59EB532083E4A3D3", 1048576, "sanyo_hc21_alpha.bin", "Sanyo HC-21 (alpha 3/21/94)"));

Firmware("3DO", "Shootout_At_Old_Tucson", "(3DO Arcade) Shootout At Old Tucson");
Option("3DO", "Shootout_At_Old_Tucson", File("520D3D1B5897800AF47F92EFD2444A26B7A7DEAD", 524288, "3do_arcade_saot.bin", "Shootout At Old Tucson"), FirmwareOptionStatus.Ideal);

Firmware("3DO", "3DO_NTSC_1fc2", "3DO-NTSC-1.0fc2");
Option("3DO", "3DO_NTSC_1fc2", File("BD325C869E1DDE8A3872FC21565E0646A3D5B525", 1048576, "3do_devkit_1.0fc2.bin", "3DO-NTSC-1.0fc2 encrypted development kit"), FirmwareOptionStatus.Ideal);

Firmware("3DO", "Kanji_ROM_Panasonic_FZ1", "Kanji ROM for Panasonic FZ-1");
Option("3DO", "Kanji_ROM_Panasonic_FZ1", File("ACD39A8FEE1B9D2950D5AB447846C11FB31AF63E", 933636, "panafz1-kanji.bin", "Kanji ROM for Panasonic FZ-1 (J)"), FirmwareOptionStatus.Ideal);
Option("3DO", "Kanji_ROM_Panasonic_FZ1", File("884515605EE243577AB20767EF8C1A7368E4E407", 1048576, "panafz1j-kanji.bin", "Kanji ROM for Panasonic FZ-1 (J) / Panasonic FZ-10 (J)"));

Firmware("3DO", "Kanji_ROM_Panasonic_FZ10", "Kanji ROM for Panasonic FZ-10");
Option("3DO", "Kanji_ROM_Panasonic_FZ10", File("2E857B957803D0331FD229328DF01F3FFAB69EEE", 1048576, "panafz10ja-anvil-kanji.bin", "Kanji ROM for: Panasonic FZ-10 (J) [ANVIL]"), FirmwareOptionStatus.Ideal);
Option("3DO", "Kanji_ROM_Panasonic_FZ10", File("884515605EE243577AB20767EF8C1A7368E4E407", 1048576, "panafz1j-kanji.bin", "Kanji ROM for Panasonic FZ-1 (J) / Panasonic FZ-10 (J)"));

// 3DS
// bleh, undefined hash AND size...
FirmwareAndOption(SHA1Checksum.Dummy, 0, "3DS", "aes_keys", "aes_keys.txt", "AES Keys");
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Emulation.Common/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public static class EmulatorExtensions
/// <remarks>need to think about e.g. Genesis / Mega Drive using one sysID but having a different display name depending on the BIOS region --yoshi</remarks>
public static readonly IReadOnlyDictionary<string, string> SystemIDDisplayNames = new Dictionary<string, string>
{
[VSystemID.Raw._3DO] = "3DO",
[VSystemID.Raw.Amiga] = "Amiga",
[VSystemID.Raw.A26] = "Atari 2600",
[VSystemID.Raw.A78] = "Atari 7800",
Expand Down
1 change: 1 addition & 0 deletions src/BizHawk.Emulation.Common/VSystemID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static class VSystemID
{
public static class Raw
{
public const string _3DO = "3DO";
public const string A26 = "A26";
public const string A78 = "A78";
public const string Amiga = "Amiga";
Expand Down
154 changes: 154 additions & 0 deletions src/BizHawk.Emulation.Cores/Consoles/3DO/LibOpera.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
using System.Runtime.InteropServices;
using BizHawk.BizInvoke;
using BizHawk.Emulation.Cores.Waterbox;

namespace BizHawk.Emulation.Consoles.ThreeDO
{
public abstract class LibOpera : LibWaterboxCore
{
// NTSC Specifications
public const int NTSC_WIDTH = 320;
public const int NTSC_HEIGHT = 240;
public const int NTSC_VIDEO_NUMERATOR = 60;
public const int NTSC_VIDEO_DENOMINATOR = 1;

// PAL1 Specifications
public const int PAL1_WIDTH = 320;
public const int PAL1_HEIGHT = 288;
public const int PAL1_VIDEO_NUMERATOR = 50;
public const int PAL1_VIDEO_DENOMINATOR = 1;

// PAL2 Specifications
public const int PAL2_WIDTH = 384;
public const int PAL2_HEIGHT = 288;
public const int PAL2_VIDEO_NUMERATOR = 50;
public const int PAL2_VIDEO_DENOMINATOR = 1;

[UnmanagedFunctionPointer(CC)]
public delegate void CDReadCallback(int lba, IntPtr dst);

[UnmanagedFunctionPointer(CC)]
public delegate int CDSectorCountCallback();

[BizImport(CC)]
public abstract void SetCdCallbacks(CDReadCallback cdrc, CDSectorCountCallback cdscc);

[BizImport(CC, Compatibility = true)]
public abstract bool Init(string gameFile, string biosFile, string fontFile, int port1Type, int port2Type, int videoStandard);

[BizImport(CC, Compatibility = true)]
public abstract bool sram_changed();

[BizImport(CC, Compatibility = true)]
public abstract int get_sram_size();

[BizImport(CC, Compatibility = true)]
public abstract void get_sram(IntPtr sramBuffer);

[BizImport(CC, Compatibility = true)]
public abstract void set_sram(IntPtr sramBuffer);

[StructLayout(LayoutKind.Sequential)]
public struct GamepadInputs
{
public int up;
public int down;
public int left;
public int right;
public int start;
public int select;
public int buttonA;
public int buttonB;
public int buttonX;
public int buttonY;
public int buttonL;
public int buttonR;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason these are all ints instead of a type more closely resembling their data range, like bool?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try using bool but I keep getting false at the core side, even when I send trues from the bk side. I looked high and low for a discrepancy in the apis, but everything looks correct.

Copy link
Member

@CasualPokePlayer CasualPokePlayer Mar 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When using bool, was the core actually using bool too, or just ints? The struct sizes need to match up.

Also the Compatibility = true would also probably cause issues regardless if you did that, as that would go under "normal" marshalling rules (i.e. the struct is converted into one with 4 byte bools instead of just directly pinning the original struct). You generally shouldn't use Compatibility = true unless you need "normal" marshalling as that slower.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried again, making sure again that the structs coincide. You can find them here:

[BK Side]
https://github.com/SergioMartin86/BizHawk/blob/b4836176a8df5d2eba2d77598412036767840d8d/src/BizHawk.Emulation.Cores/Consoles/3DO/LibOpera.cs#L58

[WBX side] https://github.com/SergioMartin86/BizHawk/blob/b4836176a8df5d2eba2d77598412036767840d8d/waterbox/opera/bizhawk.hpp#L13

The values are simply not passed around:
image

Perhaps you will see the discrepancy -- I just don't see it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably got something to do with the bullshit default bool marshalling rules that nobody wants, although BizInvoke should usually get rid of that...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah but there is no compatibility in the usage of this struct (just the frame advance call as far as I can see)

Copy link
Member

@CasualPokePlayer CasualPokePlayer Mar 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, in that case it's getting placed into a class rather than a struct, which has some strange behavior in this regard, since this isn't strictly blittable behavior overall. For saving space and just using bools, byte would be used C# side (or maybe rather bit flags if ypu really want to conserve space, which might or might not be wanted since bigger frame info means more GC pressure)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a uint64_t xdd; at the start of struct gamePad_t makes it work, so apparently there's 8 extra padding bytes in the c# class somehow. There more I've read into marshalling and blitting the more confused I get as to how things even work in other cores. It's possible everything that is working is just working by pure chance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be weird mismatch of C# vs C padding rules? In the perspective of marshalling, a lot of usual knowledge doesn't strictly apply per se, since the class instance just gets pinned down and the pointer to such is passed on, so it matters how C# is internally ordering the data.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{8A9D1BEF-7766-42E2-A739-069482027C11}

Looks like the bool isReset is getting tagged onto the base FrameInfo before the structs.

Using https://github.com/SergeyTeplyakov/ObjectLayoutInspector this is the actual layout:

Full managed memory layout
Type layout for 'FrameInfo'
Size: 240 bytes. Paddings: 25 bytes (%10 of empty space)
|===============================================================|
| Object Header (8 bytes)                                       |
|---------------------------------------------------------------|
| Method Table Ptr (8 bytes)                                    |
|===============================================================|
|   0-7: IntPtr VideoBuffer (8 bytes)                           |
|---------------------------------------------------------------|
|  8-15: IntPtr SoundBuffer (8 bytes)                           |
|---------------------------------------------------------------|
| 16-23: Int64 Cycles (8 bytes)                                 |
|---------------------------------------------------------------|
| 24-27: Int32 Width (4 bytes)                                  |
|---------------------------------------------------------------|
| 28-31: Int32 Height (4 bytes)                                 |
|---------------------------------------------------------------|
| 32-35: Int32 Samples (4 bytes)                                |
|---------------------------------------------------------------|
| 36-39: Int32 Lagged (4 bytes)                                 |
|---------------------------------------------------------------|
|    40: Boolean isReset (1 byte)                               |
|---------------------------------------------------------------|
| 41-47: padding (7 bytes)                                      |
|---------------------------------------------------------------|
| 48-139: GameInput port1 (92 bytes)                            |
| |===========================================================| |
| |  0-11: GamepadInputs gamepad (12 bytes)                   | |
| | |=================================|                       | |
| | |     0: Boolean up (1 byte)      |                       | |
| | |---------------------------------|                       | |
| | |     1: Boolean down (1 byte)    |                       | |
| | |---------------------------------|                       | |
| | |     2: Boolean left (1 byte)    |                       | |
| | |---------------------------------|                       | |
| | |     3: Boolean right (1 byte)   |                       | |
| | |---------------------------------|                       | |
| | |     4: Boolean start (1 byte)   |                       | |
| | |---------------------------------|                       | |
| | |     5: Boolean select (1 byte)  |                       | |
| | |---------------------------------|                       | |
| | |     6: Boolean buttonA (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     7: Boolean buttonB (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     8: Boolean buttonX (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     9: Boolean buttonY (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |    10: Boolean buttonL (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |    11: Boolean buttonR (1 byte) |                       | |
| | |=================================|                       | |
| |-----------------------------------------------------------| |
| | 12-23: MouseInputs mouse (12 bytes)                       | |
| | |======================================|                  | |
| | |   0-3: Int32 dX (4 bytes)            |                  | |
| | |--------------------------------------|                  | |
| | |   4-7: Int32 dY (4 bytes)            |                  | |
| | |--------------------------------------|                  | |
| | |     8: Boolean leftButton (1 byte)   |                  | |
| | |--------------------------------------|                  | |
| | |     9: Boolean middleButton (1 byte) |                  | |
| | |--------------------------------------|                  | |
| | |    10: Boolean rightButton (1 byte)  |                  | |
| | |--------------------------------------|                  | |
| | |    11: Boolean fourthButton (1 byte) |                  | |
| | |======================================|                  | |
| |-----------------------------------------------------------| |
| | 24-47: FlightStickInputs flightStick (24 bytes)           | |
| | |=======================================|                 | |
| | |     0: Boolean up (1 byte)            |                 | |
| | |---------------------------------------|                 | |
| | |     1: Boolean down (1 byte)          |                 | |
| | |---------------------------------------|                 | |
| | |     2: Boolean left (1 byte)          |                 | |
| | |---------------------------------------|                 | |
| | |     3: Boolean right (1 byte)         |                 | |
| | |---------------------------------------|                 | |
| | |     4: Boolean fire (1 byte)          |                 | |
| | |---------------------------------------|                 | |
| | |     5: Boolean buttonA (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     6: Boolean buttonB (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     7: Boolean buttonC (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     8: Boolean buttonX (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     9: Boolean buttonP (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |    10: Boolean leftTrigger (1 byte)   |                 | |
| | |---------------------------------------|                 | |
| | |    11: Boolean rightTrigger (1 byte)  |                 | |
| | |---------------------------------------|                 | |
| | | 12-15: Int32 horizontalAxis (4 bytes) |                 | |
| | |---------------------------------------|                 | |
| | | 16-19: Int32 verticalAxis (4 bytes)   |                 | |
| | |---------------------------------------|                 | |
| | | 20-23: Int32 altitudeAxis (4 bytes)   |                 | |
| | |=======================================|                 | |
| |-----------------------------------------------------------| |
| | 48-59: LightGunInputs lightGun (12 bytes)                 | |
| | |=====================================|                   | |
| | |     0: Boolean trigger (1 byte)     |                   | |
| | |-------------------------------------|                   | |
| | |     1: Boolean select (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     2: Boolean reload (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     3: Boolean isOffScreen (1 byte) |                   | |
| | |-------------------------------------|                   | |
| | |   4-7: Int32 screenX (4 bytes)      |                   | |
| | |-------------------------------------|                   | |
| | |  8-11: Int32 screenY (4 bytes)      |                   | |
| | |=====================================|                   | |
| |-----------------------------------------------------------| |
| | 60-75: ArcadeLightGunInputs arcadeLightGun (16 bytes)     | |
| | |=====================================|                   | |
| | |     0: Boolean trigger (1 byte)     |                   | |
| | |-------------------------------------|                   | |
| | |     1: Boolean select (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     2: Boolean start (1 byte)       |                   | |
| | |-------------------------------------|                   | |
| | |     3: Boolean reload (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     4: Boolean auxA (1 byte)        |                   | |
| | |-------------------------------------|                   | |
| | |     5: Boolean isOffScreen (1 byte) |                   | |
| | |-------------------------------------|                   | |
| | |   6-7: padding (2 bytes)            |                   | |
| | |-------------------------------------|                   | |
| | |  8-11: Int32 screenX (4 bytes)      |                   | |
| | |-------------------------------------|                   | |
| | | 12-15: Int32 screenY (4 bytes)      |                   | |
| | |=====================================|                   | |
| |-----------------------------------------------------------| |
| | 76-91: OrbatakTrackballInputs orbatakTrackball (16 bytes) | |
| | |=================================|                       | |
| | |   0-3: Int32 dX (4 bytes)       |                       | |
| | |---------------------------------|                       | |
| | |   4-7: Int32 dY (4 bytes)       |                       | |
| | |---------------------------------|                       | |
| | |     8: Boolean startP1 (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     9: Boolean startP2 (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |    10: Boolean coinP1 (1 byte)  |                       | |
| | |---------------------------------|                       | |
| | |    11: Boolean coinP2 (1 byte)  |                       | |
| | |---------------------------------|                       | |
| | |    12: Boolean service (1 byte) |                       | |
| | |---------------------------------|                       | |
| | | 13-15: padding (3 bytes)        |                       | |
| | |=================================|                       | |
| |===========================================================| |
|---------------------------------------------------------------|
| 140-143: padding (4 bytes)                                    |
|---------------------------------------------------------------|
| 144-235: GameInput port2 (92 bytes)                           |
| |===========================================================| |
| |  0-11: GamepadInputs gamepad (12 bytes)                   | |
| | |=================================|                       | |
| | |     0: Boolean up (1 byte)      |                       | |
| | |---------------------------------|                       | |
| | |     1: Boolean down (1 byte)    |                       | |
| | |---------------------------------|                       | |
| | |     2: Boolean left (1 byte)    |                       | |
| | |---------------------------------|                       | |
| | |     3: Boolean right (1 byte)   |                       | |
| | |---------------------------------|                       | |
| | |     4: Boolean start (1 byte)   |                       | |
| | |---------------------------------|                       | |
| | |     5: Boolean select (1 byte)  |                       | |
| | |---------------------------------|                       | |
| | |     6: Boolean buttonA (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     7: Boolean buttonB (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     8: Boolean buttonX (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     9: Boolean buttonY (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |    10: Boolean buttonL (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |    11: Boolean buttonR (1 byte) |                       | |
| | |=================================|                       | |
| |-----------------------------------------------------------| |
| | 12-23: MouseInputs mouse (12 bytes)                       | |
| | |======================================|                  | |
| | |   0-3: Int32 dX (4 bytes)            |                  | |
| | |--------------------------------------|                  | |
| | |   4-7: Int32 dY (4 bytes)            |                  | |
| | |--------------------------------------|                  | |
| | |     8: Boolean leftButton (1 byte)   |                  | |
| | |--------------------------------------|                  | |
| | |     9: Boolean middleButton (1 byte) |                  | |
| | |--------------------------------------|                  | |
| | |    10: Boolean rightButton (1 byte)  |                  | |
| | |--------------------------------------|                  | |
| | |    11: Boolean fourthButton (1 byte) |                  | |
| | |======================================|                  | |
| |-----------------------------------------------------------| |
| | 24-47: FlightStickInputs flightStick (24 bytes)           | |
| | |=======================================|                 | |
| | |     0: Boolean up (1 byte)            |                 | |
| | |---------------------------------------|                 | |
| | |     1: Boolean down (1 byte)          |                 | |
| | |---------------------------------------|                 | |
| | |     2: Boolean left (1 byte)          |                 | |
| | |---------------------------------------|                 | |
| | |     3: Boolean right (1 byte)         |                 | |
| | |---------------------------------------|                 | |
| | |     4: Boolean fire (1 byte)          |                 | |
| | |---------------------------------------|                 | |
| | |     5: Boolean buttonA (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     6: Boolean buttonB (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     7: Boolean buttonC (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     8: Boolean buttonX (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |     9: Boolean buttonP (1 byte)       |                 | |
| | |---------------------------------------|                 | |
| | |    10: Boolean leftTrigger (1 byte)   |                 | |
| | |---------------------------------------|                 | |
| | |    11: Boolean rightTrigger (1 byte)  |                 | |
| | |---------------------------------------|                 | |
| | | 12-15: Int32 horizontalAxis (4 bytes) |                 | |
| | |---------------------------------------|                 | |
| | | 16-19: Int32 verticalAxis (4 bytes)   |                 | |
| | |---------------------------------------|                 | |
| | | 20-23: Int32 altitudeAxis (4 bytes)   |                 | |
| | |=======================================|                 | |
| |-----------------------------------------------------------| |
| | 48-59: LightGunInputs lightGun (12 bytes)                 | |
| | |=====================================|                   | |
| | |     0: Boolean trigger (1 byte)     |                   | |
| | |-------------------------------------|                   | |
| | |     1: Boolean select (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     2: Boolean reload (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     3: Boolean isOffScreen (1 byte) |                   | |
| | |-------------------------------------|                   | |
| | |   4-7: Int32 screenX (4 bytes)      |                   | |
| | |-------------------------------------|                   | |
| | |  8-11: Int32 screenY (4 bytes)      |                   | |
| | |=====================================|                   | |
| |-----------------------------------------------------------| |
| | 60-75: ArcadeLightGunInputs arcadeLightGun (16 bytes)     | |
| | |=====================================|                   | |
| | |     0: Boolean trigger (1 byte)     |                   | |
| | |-------------------------------------|                   | |
| | |     1: Boolean select (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     2: Boolean start (1 byte)       |                   | |
| | |-------------------------------------|                   | |
| | |     3: Boolean reload (1 byte)      |                   | |
| | |-------------------------------------|                   | |
| | |     4: Boolean auxA (1 byte)        |                   | |
| | |-------------------------------------|                   | |
| | |     5: Boolean isOffScreen (1 byte) |                   | |
| | |-------------------------------------|                   | |
| | |   6-7: padding (2 bytes)            |                   | |
| | |-------------------------------------|                   | |
| | |  8-11: Int32 screenX (4 bytes)      |                   | |
| | |-------------------------------------|                   | |
| | | 12-15: Int32 screenY (4 bytes)      |                   | |
| | |=====================================|                   | |
| |-----------------------------------------------------------| |
| | 76-91: OrbatakTrackballInputs orbatakTrackball (16 bytes) | |
| | |=================================|                       | |
| | |   0-3: Int32 dX (4 bytes)       |                       | |
| | |---------------------------------|                       | |
| | |   4-7: Int32 dY (4 bytes)       |                       | |
| | |---------------------------------|                       | |
| | |     8: Boolean startP1 (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |     9: Boolean startP2 (1 byte) |                       | |
| | |---------------------------------|                       | |
| | |    10: Boolean coinP1 (1 byte)  |                       | |
| | |---------------------------------|                       | |
| | |    11: Boolean coinP2 (1 byte)  |                       | |
| | |---------------------------------|                       | |
| | |    12: Boolean service (1 byte) |                       | |
| | |---------------------------------|                       | |
| | | 13-15: padding (3 bytes)        |                       | |
| | |=================================|                       | |
| |===========================================================| |
|---------------------------------------------------------------|
| 236-239: padding (4 bytes)                                    |
|===============================================================|

Having any of the struct's fields be bool makes it no longer align for reasons I cannot tell you.


[StructLayout(LayoutKind.Sequential)]
public struct MouseInputs
{
public int dX;
public int dY;
public int leftButton;
public int middleButton;
public int rightButton;
public int fourthButton;
}

[StructLayout(LayoutKind.Sequential)]
public struct FlightStickInputs
{
public int up;
public int down;
public int left;
public int right;
public int fire;
public int buttonA;
public int buttonB;
public int buttonC;
public int buttonX;
public int buttonP;
public int leftTrigger;
public int rightTrigger;
public int horizontalAxis;
public int verticalAxis;
public int altitudeAxis;
}

[StructLayout(LayoutKind.Sequential)]
public struct LightGunInputs
{
public int trigger;
public int select;
public int reload;
public int isOffScreen;
public int screenX;
public int screenY;
}

[StructLayout(LayoutKind.Sequential)]
public struct ArcadeLightGunInputs
{
public int trigger;
public int select;
public int start;
public int reload;
public int auxA;
public int isOffScreen;
public int screenX;
public int screenY;
}

[StructLayout(LayoutKind.Sequential)]
public struct OrbatakTrackballInputs
{
public int dX;
public int dY;
public int startP1;
public int startP2;
public int coinP1;
public int coinP2;
public int service;
}

[StructLayout(LayoutKind.Sequential)]
public struct GameInput
{
public GamepadInputs gamepad;
public MouseInputs mouse;
public FlightStickInputs flightStick;
public LightGunInputs lightGun;
public ArcadeLightGunInputs arcadeLightGun;
public OrbatakTrackballInputs orbatakTrackball;
}

[StructLayout(LayoutKind.Sequential)]
public new class FrameInfo : LibWaterboxCore.FrameInfo
{
public GameInput port1;
public GameInput port2;
public int isReset = 0;
}
}
}
Loading