public GGHawkLink(CoreComm comm, GameInfo game_L, byte[] rom_L, GameInfo game_R, byte[] rom_R, /*string gameDbFn,*/ object settings, object syncSettings) { var ser = new BasicServiceProvider(this); linkSettings = (GGLinkSettings)settings ?? new GGLinkSettings(); linkSyncSettings = (GGLinkSyncSettings)syncSettings ?? new GGLinkSyncSettings(); _controllerDeck = new GGHawkLinkControllerDeck(GGHawkLinkControllerDeck.DefaultControllerName, GGHawkLinkControllerDeck.DefaultControllerName); CoreComm = comm; var temp_set_L = new SMS.SMSSettings(); var temp_set_R = new SMS.SMSSettings(); var temp_sync_L = new SMS.SMSSyncSettings(); var temp_sync_R = new SMS.SMSSyncSettings(); L = new SMS(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, game_L, rom_L, temp_set_L, temp_sync_L); R = new SMS(new CoreComm(comm.ShowMessage, comm.Notify) { CoreFileProvider = comm.CoreFileProvider }, game_R, rom_R, temp_set_R, temp_sync_R); ser.Register <IVideoProvider>(this); ser.Register <ISoundProvider>(this); _tracer = new TraceBuffer { Header = L.Cpu.TraceHeader }; ser.Register <ITraceable>(_tracer); ServiceProvider = ser; SetupMemoryDomains(); HardReset(); L.stand_alone = false; R.stand_alone = false; }
protected override void RunImport() { using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read); using var r = new BinaryReader(fs); // 0000: 4-byte signature: "MMV\0" string signature = new string(r.ReadChars(4)); if (signature != "MMV\0") { Result.Errors.Add("This is not a valid .MMV file."); return; } // 0004: 4-byte little endian unsigned int: dega version uint emuVersion = r.ReadUInt32(); Result.Movie.Comments.Add($"{MovieOrigin} .MMV"); Result.Movie.Comments.Add($"{EmulationOrigin} Dega version {emuVersion}"); // 0008: 4-byte little endian unsigned int: frame count uint frameCount = r.ReadUInt32(); // 000c: 4-byte little endian unsigned int: rerecord count uint rerecordCount = r.ReadUInt32(); Result.Movie.Rerecords = rerecordCount; // 0010: 4-byte little endian flag: begin from reset? uint reset = r.ReadUInt32(); if (reset == 0) { Result.Errors.Add("Movies that begin with a savestate are not supported."); return; } // 0014: 4-byte little endian unsigned int: offset of state information r.ReadUInt32(); // 0018: 4-byte little endian unsigned int: offset of input data r.ReadUInt32(); // 001c: 4-byte little endian unsigned int: size of input packet r.ReadUInt32(); // 0020-005f: string: author info (UTF-8) string author = NullTerminated(new string(r.ReadChars(64))); Result.Movie.HeaderEntries[HeaderKeys.AUTHOR] = author; // 0060: 4-byte little endian flags byte flags = r.ReadByte(); // bit 0: unused // bit 1: "PAL" bool pal = ((flags >> 1) & 0x1) != 0; Result.Movie.HeaderEntries[HeaderKeys.PAL] = pal.ToString(); // bit 2: Japan bool japan = ((flags >> 2) & 0x1) != 0; Result.Movie.HeaderEntries["Japan"] = japan.ToString(); // bit 3: Game Gear (version 1.16+) bool isGameGear; if (((flags >> 3) & 0x1) != 0) { isGameGear = true; Result.Movie.HeaderEntries.Add("IsGGMode", "1"); } else { isGameGear = false; } Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "SMS"; // System Id is still SMS even if game gear // bits 4-31: unused r.ReadBytes(3); // 0064-00e3: string: rom name (ASCII) string gameName = NullTerminated(new string(r.ReadChars(128))); Result.Movie.HeaderEntries[HeaderKeys.GAMENAME] = gameName; // 00e4-00f3: binary: rom MD5 digest byte[] md5 = r.ReadBytes(16); Result.Movie.HeaderEntries[MD5] = md5.BytesToHexString().ToLower(); var ss = new SMS.SMSSyncSettings { ControllerType = "Standard" }; var controllers = new SimpleController { Definition = isGameGear ? SMS.SmsController : SMS.GGController }; /* * 76543210 * bit 0 (0x01): up * bit 1 (0x02): down * bit 2 (0x04): left * bit 3 (0x08): right * bit 4 (0x10): 1 * bit 5 (0x20): 2 * bit 6 (0x40): start (Master System) * bit 7 (0x80): start (Game Gear) */ string[] buttons = { "Up", "Down", "Left", "Right", "B1", "B2" }; for (int frame = 1; frame <= frameCount; frame++) { /* * Controller data is made up of one input packet per frame. Each packet currently consists of 2 bytes. The * first byte is for controller 1 and the second controller 2. The Game Gear only uses the controller 1 input * however both bytes are still present. */ for (int player = 1; player <= 2; player++) { byte controllerState = r.ReadByte(); for (int button = 0; button < buttons.Length; button++) { controllers[$"P{player} {buttons[button]}"] = ((controllerState >> button) & 0x1) != 0; } if (player == 1) { controllers["Pause"] = (((controllerState >> 6) & 0x1) != 0 && (!isGameGear)) || (((controllerState >> 7) & 0x1) != 0 && isGameGear); } } Result.Movie.AppendFrame(controllers); } Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(ss); }