private static ILibsnesController Factory(ControllerType t, LibsnesCore.SnesSyncSettings ss)
        {
            switch (t)
            {
            case ControllerType.Unplugged:
                return(new SnesUnpluggedController());

            case ControllerType.Gamepad:
                return(new SnesController());

            case ControllerType.Multitap:
                return(new SnesMultitapController());

            case ControllerType.Payload:
                return(new SnesPayloadController());

            case ControllerType.Mouse:
                return(new SnesMouseController
                {
                    LimitAnalogChangeSensitivity = ss.LimitAnalogChangeSensitivity
                });

            case ControllerType.SuperScope:
                return(new SnesSuperScopeController());

            case ControllerType.Justifier:
                return(new SnesJustifierController());

            default:
                throw new InvalidOperationException();
            }
        }
示例#2
0
        private static ILibsnesController Factory(ControllerType t, LibsnesCore.SnesSyncSettings ss)
        {
            switch (t)
            {
            case ControllerType.Unplugged:
                return(new SnesUnpluggedController());

            case ControllerType.Gamepad:
                return(new SnesController());

            case ControllerType.Multitap:
                return(new SnesMultitapController());

            case ControllerType.Payload:
                return(new SnesPayloadController());

            case ControllerType.Mouse:
                return(new SnesMouseController
                {
                    LimitMouseSpeed = ss.LimitMouseSpeed
                });

            default:
                throw new InvalidOperationException();
            }
        }
        public LibsnesControllerDeck(LibsnesCore.SnesSyncSettings ss)
        {
            _ports = new[]
            {
                Factory(ss.LeftPort, ss),
                Factory(ss.RightPort, ss)
            };

            Definition = ControllerDefinitionMerger.GetMerged(_ports.Select(p => p.Definition), out var tmp);
            _mergers   = tmp.ToArray();

            // add buttons that the core itself will handle
            Definition.BoolButtons.Add("Reset");
            Definition.BoolButtons.Add("Power");
            Definition.Name = "SNES Controller";
        }
示例#4
0
		private static BkmMovie ImportSMV(string path, out string errorMsg, out string warningMsg)
		{
			errorMsg = warningMsg = string.Empty;
			BkmMovie m = new BkmMovie(path);
			FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
			BinaryReader r = new BinaryReader(fs);
			// 000 4-byte signature: 53 4D 56 1A "SMV\x1A"
			string signature = r.ReadStringFixedAscii(4);
			if (signature != "SMV\x1A")
			{
				errorMsg = "This is not a valid .SMV file.";
				r.Close();
				fs.Close();
				return null;
			}
			m.Header[HeaderKeys.PLATFORM] = "SNES";
			// 004 4-byte little-endian unsigned int: version number
			uint versionNumber = r.ReadUInt32();
			string version;
			switch (versionNumber)
			{
				case 1:
					version = "1.43";
					break;
				case 4:
					version = "1.51";
					break;
				case 5:
					version = "1.52";
					break;
				default:
					errorMsg = "SMV version not recognized. 1.43, 1.51, and 1.52 are currently supported.";
					r.Close();
					fs.Close();
					return null;
			}
			m.Comments.Add(EMULATIONORIGIN + " Snes9x version " + version);
			m.Comments.Add(MOVIEORIGIN + " .SMV");
			/*
			 008 4-byte little-endian integer: movie "uid" - identifies the movie-savestate relationship, also used as the
			 recording time in Unix epoch format
			*/
			uint uid = r.ReadUInt32();

			// 00C 4-byte little-endian unsigned int: rerecord count
			m.Rerecords = r.ReadUInt32();
			// 010 4-byte little-endian unsigned int: number of frames
			uint frameCount = r.ReadUInt32();
			// 014 1-byte flags "controller mask"
			byte controllerFlags = r.ReadByte();
			/*
			 * bit 0: controller 1 in use
			 * bit 1: controller 2 in use
			 * bit 2: controller 3 in use
			 * bit 3: controller 4 in use
			 * bit 4: controller 5 in use
			 * other: reserved, set to 0
			*/
			SimpleController controllers = new SimpleController { Type = new ControllerDefinition { Name = "SNES Controller" } };
			bool[] controllersUsed = new bool[5];
			for (int controller = 1; controller <= controllersUsed.Length; controller++)
			{
				controllersUsed[controller - 1] = (((controllerFlags >> (controller - 1)) & 0x1) != 0);
			}
			// 015 1-byte flags "movie options"
			byte movieFlags = r.ReadByte();
			/*
				 bit 0:
					 if "0", movie begins from an embedded "quicksave" snapshot
					 if "1", a SRAM is included instead of a quicksave; movie begins from reset
			*/
			if ((movieFlags & 0x1) == 0)
			{
				errorMsg = "Movies that begin with a savestate are not supported.";
				r.Close();
				fs.Close();
				return null;
			}
			// bit 1: if "0", movie is NTSC (60 fps); if "1", movie is PAL (50 fps)
			bool pal = (((movieFlags >> 1) & 0x1) != 0);
			m.Header[HeaderKeys.PAL] = pal.ToString();
			// other: reserved, set to 0
			/*
			 016 1-byte flags "sync options":
				 bit 0: MOVIE_SYNC2_INIT_FASTROM
				 other: reserved, set to 0
			*/
			r.ReadByte();
			/*
			 017 1-byte flags "sync options":
				 bit 0: MOVIE_SYNC_DATA_EXISTS
					 if "1", all sync options flags are defined.
					 if "0", all sync options flags have no meaning.
				 bit 1: MOVIE_SYNC_WIP1TIMING
				 bit 2: MOVIE_SYNC_LEFTRIGHT
				 bit 3: MOVIE_SYNC_VOLUMEENVX
				 bit 4: MOVIE_SYNC_FAKEMUTE
				 bit 5: MOVIE_SYNC_SYNCSOUND
				 bit 6: MOVIE_SYNC_HASROMINFO
					 if "1", there is extra ROM info located right in between of the metadata and the savestate.
				 bit 7: set to 0.
			*/
			byte syncFlags = r.ReadByte();
			/*
			 Extra ROM info is always positioned right before the savestate. Its size is 30 bytes if MOVIE_SYNC_HASROMINFO
			 is used (and MOVIE_SYNC_DATA_EXISTS is set), 0 bytes otherwise.
			*/
			int extraRomInfo = (((syncFlags >> 6) & 0x1) != 0 && (syncFlags & 0x1) != 0) ? 30 : 0;
			// 018 4-byte little-endian unsigned int: offset to the savestate inside file
			uint savestateOffset = r.ReadUInt32();
			// 01C 4-byte little-endian unsigned int: offset to the controller data inside file
			uint firstFrameOffset = r.ReadUInt32();
			int[] controllerTypes = new int[2];
			// The (.SMV 1.51 and up) header has an additional 32 bytes at the end
			if (version != "1.43")
			{
				// 020 4-byte little-endian unsigned int: number of input samples, primarily for peripheral-using games
				r.ReadBytes(4);
				/*
				 024 2 1-byte unsigned ints: what type of controller is plugged into ports 1 and 2 respectively: 0=NONE,
				 1=JOYPAD, 2=MOUSE, 3=SUPERSCOPE, 4=JUSTIFIER, 5=MULTITAP
				*/
				controllerTypes[0] = r.ReadByte();
				controllerTypes[1] = r.ReadByte();
				// 026 4 1-byte signed ints: controller IDs of port 1, or -1 for unplugged
				r.ReadBytes(4);
				// 02A 4 1-byte signed ints: controller IDs of port 2, or -1 for unplugged
				r.ReadBytes(4);
				// 02E 18 bytes: reserved for future use
				r.ReadBytes(18);
			}
			/*
			 After the header comes "metadata", which is UTF16-coded movie title string (author info). The metadata begins
			 from position 32 (0x20 (0x40 for 1.51 and up)) and ends at <savestate_offset -
			 length_of_extra_rom_info_in_bytes>.
			*/
			byte[] metadata = r.ReadBytes((int)(savestateOffset - extraRomInfo - ((version != "1.43") ? 0x40 : 0x20)));
			string author = NullTerminated(Encoding.Unicode.GetString(metadata).Trim());
			if (author != "")
			{
				m.Header[HeaderKeys.AUTHOR] = author;
			}
			if (extraRomInfo == 30)
			{
				// 000 3 bytes of zero padding: 00 00 00 003 4-byte integer: CRC32 of the ROM 007 23-byte ascii string
				r.ReadBytes(3);
				int crc32 = r.ReadInt32();
				m.Header[CRC32] = crc32.ToString();
				// the game name copied from the ROM, truncated to 23 bytes (the game name in the ROM is 21 bytes)
				string gameName = NullTerminated(Encoding.UTF8.GetString(r.ReadBytes(23)));
				m.Header[HeaderKeys.GAMENAME] = gameName;
			}
			r.BaseStream.Position = firstFrameOffset;
			/*
			 01 00 (reserved)
			 02 00 (reserved)
			 04 00 (reserved)
			 08 00 (reserved)
			 10 00 R
			 20 00 L
			 40 00 X
			 80 00 A
			 00 01 Right
			 00 02 Left
			 00 04 Down
			 00 08 Up
			 00 10 Start
			 00 20 Select
			 00 40 Y
			 00 80 B
			*/
			string[] buttons =
			{
				"Right", "Left", "Down", "Up", "Start", "Select", "Y", "B", "R", "L", "X", "A"
			};

			for (int frame = 0; frame <= frameCount; frame++)
			{
				controllers["Reset"] = true;
				for (int player = 1; player <= controllersUsed.Length; player++)
				{
					if (!controllersUsed[player - 1])
					{
						continue;
					}
					/*
					 Each frame consists of 2 bytes per controller. So if there are 3 controllers, a frame is 6 bytes and
					 if there is only 1 controller, a frame is 2 bytes.
					*/
					byte controllerState1 = r.ReadByte();
					byte controllerState2 = r.ReadByte();
					/*
					 In the reset-recording patch, a frame that contains the value FF FF for every controller denotes a
					 reset. The reset is done through the S9xSoftReset routine.
					*/
					if (controllerState1 != 0xFF || controllerState2 != 0xFF)
					{
						controllers["Reset"] = false;
					}
					/*
					 While the meaning of controller data (for 1.51 and up) for a single standard SNES controller pad
					 remains the same, each frame of controller data can contain additional bytes if input for peripherals
					 is being recorded.
					*/
					if (version != "1.43" && player <= controllerTypes.Length)
					{
						var peripheral = string.Empty;
						switch (controllerTypes[player - 1])
						{
							// NONE
							case 0:
								continue;
							// JOYPAD
							case 1:
								break;
							// MOUSE
							case 2:
								peripheral = "Mouse";
								// 5*num_mouse_ports
								r.ReadBytes(5);
								break;
							// SUPERSCOPE
							case 3:
								peripheral = "Super Scope";
								// 6*num_superscope_ports
								r.ReadBytes(6);
								break;
							// JUSTIFIER
							case 4:
								peripheral = "Justifier";
								// 11*num_justifier_ports
								r.ReadBytes(11);
								break;
							// MULTITAP
							case 5:
								peripheral = "Multitap";
								break;
						}
						if (peripheral != "" && warningMsg == "")
						{
							warningMsg = "Unable to import " + peripheral + ".";
						}
					}
					ushort controllerState = (ushort)(((controllerState1 << 4) & 0x0F00) | controllerState2);
					if (player <= BkmMnemonicConstants.PLAYERS[controllers.Type.Name])
					{
						for (int button = 0; button < buttons.Length; button++)
						{
							controllers["P" + player + " " + buttons[button]] = (
								((controllerState >> button) & 0x1) != 0
							);
						}
					}
					else if (warningMsg == "")
					{
						warningMsg = "Controller " + player + " not supported.";
					}
				}
				// The controller data contains <number_of_frames + 1> frames.
				if (frame == 0)
				{
					continue;
				}
				m.AppendFrame(controllers);
			}
			r.Close();
			fs.Close();

			LibsnesCore.SnesSyncSettings ss = new LibsnesCore.SnesSyncSettings();
			ss.Profile = "Compatibility";
			m.SyncSettingsJson = ConfigService.SaveWithType(ss);

			return m;
		}
示例#5
0
		// LSMV file format: http://tasvideos.org/Lsnes/Movieformat.html
		private static BkmMovie ImportLSMV(string path, out string errorMsg, out string warningMsg)
		{
			errorMsg = warningMsg = string.Empty;
			var m = new BkmMovie(path);
			var hf = new HawkFile(path);
			// .LSMV movies are .zip files containing data files.
			if (!hf.IsArchive)
			{
				errorMsg = "This is not an archive.";
				return null;
			}
			string platform = "SNES";
			foreach (var item in hf.ArchiveItems)
			{
				if (item.Name == "authors")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string authors = Encoding.UTF8.GetString(stream.ReadAllBytes());
					string author_list = "";
					string author_last = "";
					using (StringReader reader = new StringReader(authors))
					{
						string line;
						// Each author is on a different line.
						while ((line = reader.ReadLine()) != null)
						{
							string author = line.Trim();
							if (author != "")
							{
								if (author_last != "")
								{
									author_list += author_last + ", ";
								}
								author_last = author;
							}
						}
					}
					if (author_list != "")
					{
						author_list += "and ";
					}
					if (author_last != "")
					{
						author_list += author_last;
					}
					m.Header[HeaderKeys.AUTHOR] = author_list;
					hf.Unbind();
				}
				else if (item.Name == "coreversion")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string coreversion = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Comments.Add(COREORIGIN + " " + coreversion);
					hf.Unbind();
				}
				else if (item.Name == "gamename")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string gamename = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Header[HeaderKeys.GAMENAME] = gamename;
					hf.Unbind();
				}
				else if (item.Name == "gametype")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string gametype = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					// TODO: Handle the other types.
					switch (gametype)
					{
						case "gdmg":
							platform = "GB";
							break;
						case "ggbc":
						case "ggbca":
							platform = "GBC";
							break;
						case "sgb_ntsc":
						case "sgb_pal":
							platform = "SNES";
							Global.Config.GB_AsSGB = true;
							break;
					}
					bool pal = (gametype == "snes_pal" || gametype == "sgb_pal");
					m.Header[HeaderKeys.PAL] = pal.ToString();
					hf.Unbind();
				}
				else if (item.Name == "input")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string input = Encoding.UTF8.GetString(stream.ReadAllBytes());
					int lineNum = 0;
					using (StringReader reader = new StringReader(input))
					{
						lineNum++;
						string line;
						while ((line = reader.ReadLine()) != null)
						{
							if (line == "")
							{
								continue;
							}
							m = ImportTextFrame(line, lineNum, m, path, platform, ref warningMsg);
							if (errorMsg != "")
							{
								hf.Unbind();
								return null;
							}
						}
					}
					hf.Unbind();
				}
				else if (item.Name.StartsWith("moviesram."))
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					byte[] moviesram = stream.ReadAllBytes();
					if (moviesram.Length != 0)
					{
						errorMsg = "Movies that begin with SRAM are not supported.";
						hf.Unbind();
						return null;
					}
					hf.Unbind();
				}
				else if (item.Name == "port1")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string port1 = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Header[PORT1] = port1;
					hf.Unbind();
				}
				else if (item.Name == "port2")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string port2 = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Header[PORT2] = port2;
					hf.Unbind();
				}
				else if (item.Name == "projectid")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string projectid = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Header[PROJECTID] = projectid;
					hf.Unbind();
				}
				else if (item.Name == "rerecords")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string rerecords = Encoding.UTF8.GetString(stream.ReadAllBytes());
					int rerecordCount;
					// Try to parse the re-record count as an integer, defaulting to 0 if it fails.
					try
					{
						rerecordCount = int.Parse(rerecords);
					}
					catch
					{
						rerecordCount = 0;
					}
					m.Rerecords = (ulong)rerecordCount;
					hf.Unbind();
				}
				else if (item.Name.EndsWith(".sha256"))
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string rom = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					int pos = item.Name.LastIndexOf(".sha256");
					string name = item.Name.Substring(0, pos);
					m.Header[SHA256 + "_" + name] = rom;
					hf.Unbind();
				}
				else if (item.Name == "savestate")
				{
					errorMsg = "Movies that begin with a savestate are not supported.";
					return null;
				}
				else if (item.Name == "subtitles")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string subtitles = Encoding.UTF8.GetString(stream.ReadAllBytes());
					using (StringReader reader = new StringReader(subtitles))
					{
						string line;
						while ((line = reader.ReadLine()) != null)
							m = ImportTextSubtitle(line, m, path);
					}
					hf.Unbind();
				}
				else if (item.Name == "starttime.second")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string startSecond = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Header[STARTSECOND] = startSecond;
					hf.Unbind();
				}
				else if (item.Name == "starttime.subsecond")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string startSubSecond = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Header[STARTSUBSECOND] = startSubSecond;
					hf.Unbind();
				}
				else if (item.Name == "systemid")
				{
					hf.BindArchiveMember(item.Index);
					var stream = hf.GetStream();
					string systemid = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
					m.Comments.Add(EMULATIONORIGIN + " " + systemid);
					hf.Unbind();
				}
			}

			m.Header[HeaderKeys.PLATFORM] = platform;

			LibsnesCore.SnesSyncSettings ss = new LibsnesCore.SnesSyncSettings();
			ss.Profile = "Compatibility";
			m.SyncSettingsJson = ConfigService.SaveWithType(ss);

			return m;
		}