diff --git a/HintMachine.SNI/Default.cs b/HintMachine.SNI/Default.cs new file mode 100644 index 0000000..b9ba69d --- /dev/null +++ b/HintMachine.SNI/Default.cs @@ -0,0 +1,7 @@ +namespace HintMachine.SNIConnection +{ + public class Default + { + + } +} \ No newline at end of file diff --git a/HintMachine.SNI/HintMachine.SNIConnection.csproj b/HintMachine.SNI/HintMachine.SNIConnection.csproj new file mode 100644 index 0000000..2efb9ae --- /dev/null +++ b/HintMachine.SNI/HintMachine.SNIConnection.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + enable + enable + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + diff --git a/HintMachine.SNI/Protos/sni.proto b/HintMachine.SNI/Protos/sni.proto new file mode 100644 index 0000000..e29de96 --- /dev/null +++ b/HintMachine.SNI/Protos/sni.proto @@ -0,0 +1,425 @@ +syntax = "proto3"; + +option go_package = "github.com/alttpo/sni/protos/sni"; +option csharp_namespace = "SNI"; +option java_package = "com.github.alttpo.sni"; + +////////////////////////////////////////////////////////////////////////////////////////////////// +// services +////////////////////////////////////////////////////////////////////////////////////////////////// + +service Devices { + // detect and list devices currently connected to the system: + rpc ListDevices(DevicesRequest) returns (DevicesResponse) {} +} + +service DeviceControl { + // only available if DeviceCapability ResetSystem is present + rpc ResetSystem(ResetSystemRequest) returns (ResetSystemResponse) {} + + // only available if DeviceCapability ResetToMenu is present + rpc ResetToMenu(ResetToMenuRequest) returns (ResetToMenuResponse) {} + + // only available if DeviceCapability PauseUnpauseEmulation is present + rpc PauseUnpauseEmulation(PauseEmulationRequest) returns (PauseEmulationResponse) {} + + // only available if DeviceCapability PauseToggleEmulation is present + rpc PauseToggleEmulation(PauseToggleEmulationRequest) returns (PauseToggleEmulationResponse) {} +} + +service DeviceMemory { + // detect the current memory mapping for the given device by reading 00:FFB0 header: + rpc MappingDetect(DetectMemoryMappingRequest) returns (DetectMemoryMappingResponse) {} + + // read a single memory segment with a given size from the given device: + rpc SingleRead(SingleReadMemoryRequest) returns (SingleReadMemoryResponse) {} + // write a single memory segment with given data to the given device: + rpc SingleWrite(SingleWriteMemoryRequest) returns (SingleWriteMemoryResponse) {} + + // read multiple memory segments with given sizes from the given device: + rpc MultiRead(MultiReadMemoryRequest) returns (MultiReadMemoryResponse) {} + // write multiple memory segments with given data to the given device: + rpc MultiWrite(MultiWriteMemoryRequest) returns (MultiWriteMemoryResponse) {} + + // stream read multiple memory segments with given sizes from the given device: + rpc StreamRead(stream MultiReadMemoryRequest) returns (stream MultiReadMemoryResponse) {} + // stream write multiple memory segments with given data to the given device: + rpc StreamWrite(stream MultiWriteMemoryRequest) returns (stream MultiWriteMemoryResponse) {} +} + +service DeviceFilesystem { + rpc ReadDirectory(ReadDirectoryRequest) returns (ReadDirectoryResponse) {} + rpc MakeDirectory(MakeDirectoryRequest) returns (MakeDirectoryResponse) {} + rpc RemoveFile(RemoveFileRequest) returns (RemoveFileResponse) {} + rpc RenameFile(RenameFileRequest) returns (RenameFileResponse) {} + rpc PutFile(PutFileRequest) returns (PutFileResponse) {} + rpc GetFile(GetFileRequest) returns (GetFileResponse) {} + rpc BootFile(BootFileRequest) returns (BootFileResponse) {} +} + +service DeviceInfo { + rpc FetchFields(FieldsRequest) returns (FieldsResponse) {} +} + +service DeviceNWA { + rpc NWACommand(NWACommandRequest) returns (NWACommandResponse) {} +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// enums +////////////////////////////////////////////////////////////////////////////////////////////////// + +// address space used to interpret an address in: +enum AddressSpace { + // The default is the FX Pak Pro / SD2SNES's address space: + // $00_0000..$DF_FFFF = ROM contents, linearly mapped + // $E0_0000..$EF_FFFF = SRAM contents, linearly mapped + // $F5_0000..$F6_FFFF = WRAM contents, linearly mapped + // $F7_0000..$F7_FFFF = VRAM contents, linearly mapped + // $F8_0000..$F8_FFFF = APU contents, linearly mapped + // $F9_0000..$F9_01FF = CGRAM contents, linearly mapped + // $F9_0200..$F9_041F = OAM contents, linearly mapped + // $F9_0420..$F9_04FF = MISC contents, linearly mapped + // $F9_0500..$F9_06FF = PPUREG, linearly mapped + // $F9_0700..$F9_08FF = CPUREG, linearly mapped + // translated device address depends on device being talked to and its current MemoryMapping mode + FxPakPro = 0; // SNES + // The SNES's main A-bus; address depends on device's current MemoryMapping mode, e.g. LoROM, HiROM, ExHiROM, etc. + SnesABus = 1; + // Do not do any address translation; simply pass the raw address to the device as-is: + Raw = 2; +} + +// memory mapping mode of a SNES cart: +enum MemoryMapping { + Unknown = 0; + HiROM = 1; + LoROM = 2; + ExHiROM = 3; // (48-64Mbit) + SA1 = 4; + // TODO: BSX = 5; +} + +// capabilities of a device +enum DeviceCapability { + None = 0; + ReadMemory = 1; + WriteMemory = 2; + ExecuteASM = 3; + ResetSystem = 4; + PauseUnpauseEmulation = 5; + PauseToggleEmulation = 6; + ResetToMenu = 7; + FetchFields = 8; + + ReadDirectory = 10; + MakeDirectory = 11; + RemoveFile = 12; + RenameFile = 13; + PutFile = 14; + GetFile = 15; + BootFile = 16; + + NWACommand = 20; +} + +// fields to query from DeviceInfo.FetchFields +enum Field { + DeviceName = 0; + DeviceVersion = 1; + DeviceStatus = 2; + + CoreName = 20; + CoreVersion = 21; + CorePlatform = 22; + + RomFileName = 40; + RomHashType = 41; + RomHashValue = 42; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// devices messages +////////////////////////////////////////////////////////////////////////////////////////////////// + +message DevicesRequest { + // optional list of device kind filters + repeated string kinds = 1; + // TODO: repeated DeviceCapability capabilities; +} +message DevicesResponse { + message Device { + // URI that describes exactly how to connect to the device, e.g.: + // RetroArch: "ra://127.0.0.1:55355" + // FX Pak Pro: "fxpakpro://./dev/cu.usbmodemDEMO000000001" (MacOS) + // "fxpakpro://./COM4" (Windows) + // "fxpakpro://./dev/ttyACM0" (Linux) + // uri is used as the unique identifier of the device for clients to refer to + string uri = 1; + // friendly display name of the device + string displayName = 2; + // device kind, e.g. "fxpakpro", "retroarch", "lua" + string kind = 3; + // all device capabilities: + repeated DeviceCapability capabilities = 4; + // default address space for the device: + AddressSpace defaultAddressSpace = 5; + + // [DEPRECATED] console system supported, e.g. "snes", "n64" + // since devices can support multiple systems, it's better to fetch platform from DeviceInfo.FetchFields method + string system = 6 [deprecated = true]; + } + + repeated Device devices = 1; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// control messages +////////////////////////////////////////////////////////////////////////////////////////////////// + +message ResetSystemRequest { + string uri = 1; +} +message ResetSystemResponse { + string uri = 1; +} + +message ResetToMenuRequest { + string uri = 1; +} +message ResetToMenuResponse { + string uri = 1; +} + +message PauseEmulationRequest { + string uri = 1; + // true to pause emulation, false to unpause + bool paused = 2; +} +message PauseEmulationResponse { + string uri = 1; + bool paused = 2; +} + +message PauseToggleEmulationRequest { + string uri = 1; +} +message PauseToggleEmulationResponse { + string uri = 1; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// memory messages +////////////////////////////////////////////////////////////////////////////////////////////////// + +message DetectMemoryMappingRequest { + string uri = 1; + // optional fallback value to set in case detection fails for some reason + optional MemoryMapping fallbackMemoryMapping = 2; + // optional ROM header (from bus address $00:FFB0, at least $30 bytes long) to use for detection + // if not provided, the header will be read from the device + optional bytes romHeader00FFB0 = 3; +} +message DetectMemoryMappingResponse { + string uri = 1; + // the memory mapping mode detected + MemoryMapping memoryMapping = 2; + // true if confident we detected a mapping; false if using a fallback or default value + bool confidence = 3; + // the ROM header read from $00:FFB0, length is $50 bytes if server reads it, otherwise length of `romHeader00FFB0` from request + bytes romHeader00FFB0 = 4; +} + +message ReadMemoryRequest { + uint32 requestAddress = 1; + AddressSpace requestAddressSpace = 2; + MemoryMapping requestMemoryMapping = 4; + + uint32 size = 3; +} +message ReadMemoryResponse { + uint32 requestAddress = 1; + AddressSpace requestAddressSpace = 2; + MemoryMapping requestMemoryMapping = 6; + + // the address sent to the device and its space + uint32 deviceAddress = 3; + AddressSpace deviceAddressSpace = 4; + + bytes data = 5; +} +message WriteMemoryRequest { + uint32 requestAddress = 1; + AddressSpace requestAddressSpace = 2; + MemoryMapping requestMemoryMapping = 4; + + bytes data = 3; +} +message WriteMemoryResponse { + uint32 requestAddress = 1; + AddressSpace requestAddressSpace = 2; + MemoryMapping requestMemoryMapping = 6; + + uint32 deviceAddress = 3; + AddressSpace deviceAddressSpace = 4; + + uint32 size = 5; +} + +message SingleReadMemoryRequest { + string uri = 1; + ReadMemoryRequest request = 2; +} +message SingleReadMemoryResponse { + string uri = 1; + ReadMemoryResponse response = 2; +} + +message SingleWriteMemoryRequest { + string uri = 1; + WriteMemoryRequest request = 2; +} +message SingleWriteMemoryResponse { + string uri = 1; + WriteMemoryResponse response = 2; +} + +message MultiReadMemoryRequest { + string uri = 1; + repeated ReadMemoryRequest requests = 2; +} +message MultiReadMemoryResponse { + string uri = 1; + repeated ReadMemoryResponse responses = 2; +} + +message MultiWriteMemoryRequest { + string uri = 1; + repeated WriteMemoryRequest requests = 2; +} +message MultiWriteMemoryResponse { + string uri = 1; + repeated WriteMemoryResponse responses = 2; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// filesystem messages +////////////////////////////////////////////////////////////////////////////////////////////////// + +message ReadDirectoryRequest { + string uri = 1; + string path = 2; +} + +enum DirEntryType { + Directory = 0; + File = 1; +} + +message DirEntry { + string name = 1; + DirEntryType type = 2; +} + +message ReadDirectoryResponse { + string uri = 1; + string path = 2; + repeated DirEntry entries = 3; +} + +message MakeDirectoryRequest { + string uri = 1; + string path = 2; +} +message MakeDirectoryResponse { + string uri = 1; + string path = 2; +} + +message RemoveFileRequest { + string uri = 1; + string path = 2; +} +message RemoveFileResponse { + string uri = 1; + string path = 2; +} + +message RenameFileRequest { + string uri = 1; + string path = 2; + string newFilename = 3; +} +message RenameFileResponse { + string uri = 1; + string path = 2; + string newFilename = 3; +} + +message PutFileRequest { + string uri = 1; + string path = 2; + bytes data = 3; +} +message PutFileResponse { + string uri = 1; + string path = 2; + uint32 size = 3; +} + +message GetFileRequest { + string uri = 1; + string path = 2; +} +message GetFileResponse { + string uri = 1; + string path = 2; + uint32 size = 3; + bytes data = 4; +} + +message BootFileRequest { + string uri = 1; + string path = 2; +} +message BootFileResponse { + string uri = 1; + string path = 2; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// info messages +////////////////////////////////////////////////////////////////////////////////////////////////// + +message FieldsRequest { + string uri = 1; + repeated Field fields = 2; +} +message FieldsResponse { + string uri = 1; + repeated Field fields = 2; + repeated string values = 3; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// +// NWA messages (emu-nwaccess protocol pass through) +////////////////////////////////////////////////////////////////////////////////////////////////// + +message NWACommandRequest { + string uri = 1; + // emu-nwaccess command name: + string command = 2; + // command arguments: + string args = 3; + // an optional binary argument: + optional bytes binaryArg = 4; +} +message NWACommandResponse { + message NWAASCIIItem { + map item = 1; + } + + string uri = 1; + repeated NWAASCIIItem asciiReply = 2; + optional bytes binaryReplay = 3; +} diff --git a/HintMachine.sln b/HintMachine.sln index 409125d..13c4023 100644 --- a/HintMachine.sln +++ b/HintMachine.sln @@ -4,6 +4,8 @@ VisualStudioVersion = 17.7.34003.232 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HintMachine", "HintMachine\HintMachine.csproj", "{F07DABDF-B962-4696-B9DA-5E18E6E83D56}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HintMachine.SNIConnection", "HintMachine.SNI\HintMachine.SNIConnection.csproj", "{62A06E61-0F83-458F-B7E4-1A6A60B1EA89}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -14,6 +16,10 @@ Global {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Debug|Any CPU.Build.0 = Debug|Any CPU {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Release|Any CPU.ActiveCfg = Release|Any CPU {F07DABDF-B962-4696-B9DA-5E18E6E83D56}.Release|Any CPU.Build.0 = Release|Any CPU + {62A06E61-0F83-458F-B7E4-1A6A60B1EA89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62A06E61-0F83-458F-B7E4-1A6A60B1EA89}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62A06E61-0F83-458F-B7E4-1A6A60B1EA89}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62A06E61-0F83-458F-B7E4-1A6A60B1EA89}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/HintMachine/App.config b/HintMachine/App.config index 3e895f9..2b8342b 100644 --- a/HintMachine/App.config +++ b/HintMachine/App.config @@ -6,10 +6,22 @@ + + + + + + + + + + + + diff --git a/HintMachine/Assets/covers/sanrio_world_smash_ball.png b/HintMachine/Assets/covers/sanrio_world_smash_ball.png new file mode 100644 index 0000000..47b2d8b Binary files /dev/null and b/HintMachine/Assets/covers/sanrio_world_smash_ball.png differ diff --git a/HintMachine/ComboBoxPrompt.xaml b/HintMachine/ComboBoxPrompt.xaml new file mode 100644 index 0000000..494d675 --- /dev/null +++ b/HintMachine/ComboBoxPrompt.xaml @@ -0,0 +1,19 @@ + + + + + + + +