Esempio n. 1
0
        public NESSyncSettingsForm()
        {
            InitializeComponent();

            SyncSettings = ((NES)Global.Emulator).GetSyncSettings();

            if ((Global.Emulator as NES).HasMapperProperties)
            {
                DTDB = new DataTableDictionaryBind <string, string>(SyncSettings.BoardProperties);
                dataGridView1.DataSource = DTDB.Table;
                InfoLabel.Visible        = false;
            }
            else
            {
                BoardPropertiesGroupBox.Enabled = false;
                dataGridView1.DataSource        = null;
                dataGridView1.Enabled           = false;
                InfoLabel.Visible = true;
            }

            RegionComboBox.Items.AddRange(Enum.GetNames(typeof(NES.NESSyncSettings.Region)));
            RegionComboBox.SelectedItem = Enum.GetName(typeof(NES.NESSyncSettings.Region), SyncSettings.RegionOverride);

            if (SyncSettings.InitialWRamStatePattern != null && SyncSettings.InitialWRamStatePattern.Any())
            {
                var sb = new StringBuilder();
                foreach (var b in SyncSettings.InitialWRamStatePattern)
                {
                    sb.Append(b.ToHexString(2));
                }

                RamPatternOverrideBox.Text = sb.ToString();
            }
        }
Esempio n. 2
0
        private static NES CreateCore()
        {
            string xmlPath      = Path.Combine(PathUtils.ExeDirectoryPath, "NesCarts.xml");
            var    bootGodBytes = File.ReadAllBytes(xmlPath);

            BootGodDb.GetDatabaseBytes = () => bootGodBytes;

            var rom      = GetRom();
            var gameInfo = GameInfo.NullInstance;

            var pathEntries = new PathEntryCollection();

            pathEntries.ResolveWithDefaults();

            var cfp      = new CoreFileProvider(Console.WriteLine, new FirmwareManager(), pathEntries, new Dictionary <string, string>());
            var coreComm = new CoreComm(Console.WriteLine, Console.WriteLine, cfp);

            var settings     = new NES.NESSettings();
            var syncSettings = new NES.NESSyncSettings();
            var nes          = new NES(coreComm, gameInfo, rom, settings, syncSettings);

            for (int i = 0; i < 200; i++)
            {
                nes.FrameAdvance(EmptyController, true, true);
            }

            return(nes);
        }
Esempio n. 3
0
 public NesVsSettings(
     MainForm mainForm,
     NES.NESSyncSettings syncSettings)
 {
     _mainForm = mainForm;
     _settings = syncSettings;
     InitializeComponent();
 }
Esempio n. 4
0
        public NESSyncSettingsForm()
        {
            InitializeComponent();
            SyncSettings             = (NES.NESSyncSettings)Global.Emulator.GetSyncSettings();
            DTDB                     = new DataTableDictionaryBind <string, string>(SyncSettings.BoardProperties);
            dataGridView1.DataSource = DTDB.Table;

            comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
            comboBox1.Items.AddRange(Enum.GetNames(typeof(NES.NESSyncSettings.Region)));
            comboBox1.SelectedItem = Enum.GetName(typeof(NES.NESSyncSettings.Region), SyncSettings.RegionOverride);
        }
Esempio n. 5
0
        private void NesVsSettings_Load(object sender, EventArgs e)
        {
            _nes      = (NES)Global.Emulator;
            _settings = _nes.GetSyncSettings();

            Dipswitch1CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_1;
            Dipswitch2CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_2;
            Dipswitch3CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_3;
            Dipswitch4CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_4;
            Dipswitch5CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_5;
            Dipswitch6CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_6;
            Dipswitch7CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_7;
            Dipswitch8CheckBox.Checked = _settings.VSDipswitches.Dip_Switch_8;
        }
Esempio n. 6
0
        public NesControllerSettings()
        {
            InitializeComponent();
            SyncSettings = (NES.NESSyncSettings)Global.Emulator.GetSyncSettings();

            // TODO: use combobox extension and add descriptions to enum values
            comboBoxFamicom.Items.AddRange(NESControlSettings.GetFamicomExpansionValues().ToArray());
            comboBoxNESL.Items.AddRange(NESControlSettings.GetNesPortValues().ToArray());
            comboBoxNESR.Items.AddRange(NESControlSettings.GetNesPortValues().ToArray());

            comboBoxFamicom.SelectedItem = SyncSettings.Controls.FamicomExpPort;
            comboBoxNESL.SelectedItem    = SyncSettings.Controls.NesLeftPort;
            comboBoxNESR.SelectedItem    = SyncSettings.Controls.NesRightPort;
            checkBoxFamicom.Checked      = SyncSettings.Controls.Famicom;
        }
        public NesControllerSettings(ISettingsAdapter settable)
        {
            _settable     = settable;
            _syncSettings = (NES.NESSyncSettings)_settable.GetSyncSettings();
            InitializeComponent();
            Icon = Properties.Resources.GameControllerIcon;

            // TODO: use combobox extension and add descriptions to enum values
            comboBoxFamicom.Items.AddRange(NESControlSettings.GetFamicomExpansionValues().Cast <object>().ToArray());
            comboBoxNESL.Items.AddRange(NESControlSettings.GetNesPortValues().Cast <object>().ToArray());
            comboBoxNESR.Items.AddRange(NESControlSettings.GetNesPortValues().Cast <object>().ToArray());

            comboBoxFamicom.SelectedItem = _syncSettings.Controls.FamicomExpPort;
            comboBoxNESL.SelectedItem    = _syncSettings.Controls.NesLeftPort;
            comboBoxNESR.SelectedItem    = _syncSettings.Controls.NesRightPort;
            checkBoxFamicom.Checked      = _syncSettings.Controls.Famicom;
        }
        public NesControllerSettings(
            MainForm mainForm,
            NES.NESSyncSettings syncSettings)
        {
            _mainForm     = mainForm;
            _syncSettings = syncSettings;
            InitializeComponent();

            // TODO: use combobox extension and add descriptions to enum values
            comboBoxFamicom.Items.AddRange(NESControlSettings.GetFamicomExpansionValues().ToArray());
            comboBoxNESL.Items.AddRange(NESControlSettings.GetNesPortValues().ToArray());
            comboBoxNESR.Items.AddRange(NESControlSettings.GetNesPortValues().ToArray());

            comboBoxFamicom.SelectedItem = _syncSettings.Controls.FamicomExpPort;
            comboBoxNESL.SelectedItem    = _syncSettings.Controls.NesLeftPort;
            comboBoxNESR.SelectedItem    = _syncSettings.Controls.NesRightPort;
            checkBoxFamicom.Checked      = _syncSettings.Controls.Famicom;
        }
Esempio n. 9
0
        public NESSyncSettingsForm(
            IMainFormForConfig mainForm,
            NES.NESSyncSettings syncSettings,
            bool hasMapperProperties)
        {
            _mainForm        = mainForm;
            _syncSettings    = syncSettings;
            DialogController = mainForm.DialogController;
            InitializeComponent();
            HelpBtn.Image = Properties.Resources.Help;

            if (hasMapperProperties)
            {
                _dataTableDictionary     = new DataTableDictionaryBind <string, string>(_syncSettings.BoardProperties);
                dataGridView1.DataSource = _dataTableDictionary.Table;
                InfoLabel.Visible        = false;
            }
            else
            {
                BoardPropertiesGroupBox.Enabled = false;
                dataGridView1.DataSource        = null;
                dataGridView1.Enabled           = false;
                InfoLabel.Visible = true;
            }

            RegionComboBox.Items.AddRange(Enum.GetNames(typeof(NES.NESSyncSettings.Region)).Cast <object>().ToArray());
            RegionComboBox.SelectedItem = Enum.GetName(typeof(NES.NESSyncSettings.Region), _syncSettings.RegionOverride);

            if (_syncSettings.InitialWRamStatePattern != null && _syncSettings.InitialWRamStatePattern.Any())
            {
                var sb = new StringBuilder();
                foreach (var b in _syncSettings.InitialWRamStatePattern)
                {
                    sb.Append($"{b:X2}");
                }

                RamPatternOverrideBox.Text = sb.ToString();
            }
        }
Esempio n. 10
0
        public NESSyncSettingsForm()
        {
            InitializeComponent();

            SyncSettings = ((NES)Global.Emulator).GetSyncSettings();

            if ((Global.Emulator as NES).HasMapperProperties)
            {
                DTDB = new DataTableDictionaryBind <string, string>(SyncSettings.BoardProperties);
                dataGridView1.DataSource = DTDB.Table;
                InfoLabel.Visible        = false;
            }
            else
            {
                BoardPropertiesGroupBox.Enabled = false;
                dataGridView1.DataSource        = null;
                dataGridView1.Enabled           = false;
                InfoLabel.Visible = true;
            }

            RegionComboBox.Items.AddRange(Enum.GetNames(typeof(NES.NESSyncSettings.Region)));
            RegionComboBox.SelectedItem = Enum.GetName(typeof(NES.NESSyncSettings.Region), SyncSettings.RegionOverride);
        }
Esempio n. 11
0
        /// <exception cref="Exception">found <c>ControllerSNES</c></exception>
        public IEnumerable <PadSchema> GetPadSchemas(IEmulator core, Action <string> showMessageBox)
        {
            if (core is NES || core is SubNESHawk)
            {
                NES.NESSyncSettings ss = null;
                bool isFds             = false;
                int  fdsButtonCount    = 0;
                if (core is NES nesHawk)
                {
                    ss             = nesHawk.GetSyncSettings();
                    isFds          = nesHawk.IsFDS;
                    fdsButtonCount = nesHawk.ControllerDefinition.BoolButtons.Count(b => b.StartsWith("FDS Insert "));
                }
                else if (core is SubNESHawk subNesHawk)
                {
                    ss             = subNesHawk.GetSyncSettings();
                    isFds          = subNesHawk.IsFds;
                    fdsButtonCount = subNesHawk.ControllerDefinition.BoolButtons.Count(b => b.StartsWith("FDS Insert "));
                }

                if (ss.Controls.Famicom)
                {
                    yield return(StandardController(1));

                    yield return(Famicom2ndController());

                    switch (ss.Controls.FamicomExpPort)
                    {
                    default:
                    case "UnpluggedFam":
                        break;

                    case "Zapper":
                        yield return(Zapper(3));

                        break;

                    case "ArkanoidFam":
                        yield return(ArkanoidPaddle(3));

                        break;

                    case "Famicom4P":
                        yield return(StandardController(3));

                        yield return(StandardController(4));

                        break;

                    case "FamilyBasicKeyboard":
                        yield return(FamicomFamilyKeyboard(3));

                        break;

                    case "OekaKids":
                        yield return(OekaKidsTablet(3));

                        break;
                    }
                }
                else
                {
                    var currentControllerNo = 1;
                    switch (ss.Controls.NesLeftPort)
                    {
                    default:
                    case "UnpluggedNES":
                        break;

                    case "ControllerNES":
                        yield return(StandardController(1));

                        currentControllerNo++;
                        break;

                    case "Zapper":
                        yield return(Zapper(1));

                        currentControllerNo++;
                        break;

                    case "ArkanoidNES":
                        yield return(ArkanoidPaddle(1));

                        currentControllerNo++;
                        break;

                    case "FourScore":
                        yield return(StandardController(1));

                        yield return(StandardController(2));

                        currentControllerNo += 2;
                        break;

                    case "PowerPad":
                        yield return(PowerPad(1));

                        currentControllerNo++;
                        break;

                    case "ControllerSNES":
                        throw new Exception("TODO");
                    }

                    switch (ss.Controls.NesRightPort)
                    {
                    default:
                    case "UnpluggedNES":
                        break;

                    case "ControllerNES":
                        yield return(StandardController(currentControllerNo));

                        break;

                    case "Zapper":
                        yield return(Zapper(currentControllerNo));

                        break;

                    case "ArkanoidNES":
                        yield return(ArkanoidPaddle(currentControllerNo));

                        break;

                    case "FourScore":
                        yield return(StandardController(currentControllerNo));

                        yield return(StandardController(currentControllerNo + 1));

                        currentControllerNo += 2;
                        break;

                    case "PowerPad":
                        yield return(PowerPad(currentControllerNo));

                        break;

                    case "ControllerSNES":
                        throw new Exception("TODO");
                    }

                    if (currentControllerNo == 0)
                    {
                        yield return(null);
                    }
                }

                if (isFds)
                {
                    yield return(FdsConsoleButtons(fdsButtonCount));
                }
                else
                {
                    yield return(NesConsoleButtons());
                }
            }
            else
            // Quicknes Can support none, one or two controllers.
            {
                var ss = ((QuickNES)core).GetSyncSettings();
                if (ss.LeftPortConnected && ss.RightPortConnected)
                {
                    // Set both controllers
                    yield return(StandardController(1));

                    yield return(StandardController(2));
                }
                else if (ss.LeftPortConnected && !ss.RightPortConnected)
                {
                    yield return(StandardController(1));
                }
                else if (!ss.LeftPortConnected && ss.RightPortConnected)
                {
                    yield return(StandardController(1));
                }

                yield return(NesConsoleButtons());
            }
        }
Esempio n. 12
0
        protected override void RunImport()
        {
            var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName;

            Result.Movie.HeaderEntries[HeaderKeys.Core] = neshawkName;
            const string emulator = "FCEUX";
            var          platform = "NES";    // TODO: FDS?

            var syncSettings = new NES.NESSyncSettings();

            var controllerSettings = new NESControlSettings
            {
                NesLeftPort  = nameof(UnpluggedNES),
                NesRightPort = nameof(UnpluggedNES)
            };

            _deck = controllerSettings.Instantiate((x, y) => true);
            AddDeckControlButtons();

            Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform;

            using var sr = SourceFile.OpenText();
            string line;

            while ((line = sr.ReadLine()) != null)
            {
                if (line == "")
                {
                    continue;
                }

                if (line[0] == '|')
                {
                    ImportInputFrame(line);
                }
                else if (line.ToLower().StartsWith("sub"))
                {
                    var subtitle = ImportTextSubtitle(line);

                    if (!string.IsNullOrEmpty(subtitle))
                    {
                        Result.Movie.Subtitles.AddFromString(subtitle);
                    }
                }
                else if (line.ToLower().StartsWith("emuversion"))
                {
                    Result.Movie.Comments.Add($"{EmulationOrigin} {emulator} version {ParseHeader(line, "emuVersion")}");
                }
                else if (line.ToLower().StartsWith("version"))
                {
                    string version = ParseHeader(line, "version");

                    if (version != "3")
                    {
                        Result.Warnings.Add("Detected a .fm2 movie version other than 3, which is unsupported");
                    }
                    else
                    {
                        Result.Movie.Comments.Add($"{MovieOrigin} .fm2 version 3");
                    }
                }
                else if (line.ToLower().StartsWith("romfilename"))
                {
                    Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "romFilename");
                }
                else if (line.ToLower().StartsWith("cdgamename"))
                {
                    Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "cdGameName");
                }
                else if (line.ToLower().StartsWith("romchecksum"))
                {
                    string blob = ParseHeader(line, "romChecksum");
                    byte[] md5  = DecodeBlob(blob);
                    if (md5 != null && md5.Length == 16)
                    {
                        Result.Movie.HeaderEntries[MD5] = md5.BytesToHexString().ToLower();
                    }
                    else
                    {
                        Result.Warnings.Add("Bad ROM checksum.");
                    }
                }
                else if (line.ToLower().StartsWith("comment author"))
                {
                    Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author");
                }
                else if (line.ToLower().StartsWith("rerecordcount"))
                {
                    int.TryParse(ParseHeader(line, "rerecordCount"), out var rerecordCount);
                    Result.Movie.Rerecords = (ulong)rerecordCount;
                }
                else if (line.ToLower().StartsWith("guid"))
                {
                    // We no longer care to keep this info
                }
                else if (line.ToLower().StartsWith("startsfromsavestate"))
                {
                    // If this movie starts from a savestate, we can't support it.
                    if (ParseHeader(line, "StartsFromSavestate") == "1")
                    {
                        Result.Errors.Add("Movies that begin with a savestate are not supported.");
                        break;
                    }
                }
                else if (line.ToLower().StartsWith("palflag"))
                {
                    Result.Movie.HeaderEntries[HeaderKeys.Pal] = ParseHeader(line, "palFlag");
                }
                else if (line.ToLower().StartsWith("port0"))
                {
                    if (ParseHeader(line, "port0") == "1")
                    {
                        controllerSettings.NesLeftPort = nameof(ControllerNES);
                        _deck = controllerSettings.Instantiate((x, y) => false);
                        AddDeckControlButtons();
                    }
                }
                else if (line.ToLower().StartsWith("port1"))
                {
                    if (ParseHeader(line, "port1") == "1")
                    {
                        controllerSettings.NesRightPort = nameof(ControllerNES);
                        _deck = controllerSettings.Instantiate((x, y) => false);
                        AddDeckControlButtons();
                    }
                }
                else if (line.ToLower().StartsWith("port2"))
                {
                    if (ParseHeader(line, "port2") == "1")
                    {
                        Result.Errors.Add("Famicom port not yet supported");
                        break;
                    }
                }
                else if (line.ToLower().StartsWith("fourscore"))
                {
                    bool fourscore = ParseHeader(line, "fourscore") == "1";
                    if (fourscore)
                    {
                        // TODO: set controller config sync settings
                        controllerSettings.NesLeftPort  = nameof(FourScore);
                        controllerSettings.NesRightPort = nameof(FourScore);
                    }

                    _deck = controllerSettings.Instantiate((x, y) => false);
                }
                else
                {
                    Result.Movie.Comments.Add(line);                     // Everything not explicitly defined is treated as a comment.
                }
            }

            syncSettings.Controls         = controllerSettings;
            Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
        }
Esempio n. 13
0
 public NesVsSettings(ISettingsAdapter settable)
 {
     _settable = settable;
     _settings = (NES.NESSyncSettings)_settable.GetSyncSettings();
     InitializeComponent();
 }
Esempio n. 14
0
        protected override void RunImport()
        {
            var neshawkName = ((CoreAttribute)Attribute.GetCustomAttribute(typeof(NES), typeof(CoreAttribute))).CoreName;

            Result.Movie.HeaderEntries[HeaderKeys.Core] = neshawkName;

            using var r = new BinaryReader(SourceFile.Open(FileMode.Open, FileAccess.Read));
            var signature = new string(r.ReadChars(4));

            if (signature != "FCM\x1A")
            {
                Result.Errors.Add("This is not a valid .FCM file.");
                return;
            }

            Result.Movie.HeaderEntries[HeaderKeys.Platform] = "NES";

            var syncSettings = new NES.NESSyncSettings();

            var controllerSettings = new NESControlSettings
            {
                NesLeftPort  = nameof(ControllerNES),
                NesRightPort = nameof(ControllerNES)
            };

            _deck = controllerSettings.Instantiate((x, y) => true);
            AddDeckControlButtons();

            // 004 4-byte little-endian unsigned int: version number, must be 2
            uint version = r.ReadUInt32();

            if (version != 2)
            {
                Result.Errors.Add(".FCM movie version must always be 2.");
                return;
            }

            Result.Movie.Comments.Add($"{MovieOrigin} .FCM version {version}");

            // 008 1-byte flags
            byte flags = r.ReadByte();

            /*
             * bit 0: reserved, set to 0
             * bit 1:
             * if "0", movie begins from an embedded "quicksave" snapshot
             * if "1", movie begins from reset or power-on[1]
             */
            if (((flags >> 1) & 0x1) == 0)
            {
                Result.Errors.Add("Movies that begin with a savestate are not supported.");
                return;
            }

            /*
             * bit 2:
             * if "0", NTSC timing
             * if "1", "PAL" timing
             * Starting with version 0.98.12 released on September 19, 2004, a "PAL" flag was added to the header but
             * unfortunately it is not reliable - the emulator does not take the "PAL" setting from the ROM, but from a user
             * preference. This means that this site cannot calculate movie lengths reliably.
             */
            bool pal = ((flags >> 2) & 0x1) != 0;

            Result.Movie.HeaderEntries[HeaderKeys.Pal] = pal.ToString();

            // other: reserved, set to 0
            bool syncHack = ((flags >> 4) & 0x1) != 0;

            Result.Movie.Comments.Add($"SyncHack {syncHack}");

            // 009 1-byte flags: reserved, set to 0
            r.ReadByte();

            // 00A 1-byte flags: reserved, set to 0
            r.ReadByte();

            // 00B 1-byte flags: reserved, set to 0
            r.ReadByte();

            // 00C 4-byte little-endian unsigned int: number of frames
            uint frameCount = r.ReadUInt32();

            // 010 4-byte little-endian unsigned int: rerecord count
            uint rerecordCount = r.ReadUInt32();

            Result.Movie.Rerecords = rerecordCount;

            /*
             * 018 4-byte little-endian unsigned int: offset to the savestate inside file
             * The savestate offset is <header_size + length_of_metadata_in_bytes + padding>. The savestate offset should be
             * 4-byte aligned. At the savestate offset there is a savestate file. The savestate exists even if the movie is
             * reset-based.
             */
            r.ReadUInt32();

            // 01C 4-byte little-endian unsigned int: offset to the controller data inside file
            uint firstFrameOffset = r.ReadUInt32();

            // 020 16-byte md5sum of the ROM used
            byte[] md5 = r.ReadBytes(16);
            Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower();

            // 030 4-byte little-endian unsigned int: version of the emulator used
            uint emuVersion = r.ReadUInt32();

            Result.Movie.Comments.Add($"{EmulationOrigin} FCEU {emuVersion}");

            // 034 name of the ROM used - UTF8 encoded nul-terminated string.
            var gameBytes = new List <byte>();

            while (r.PeekChar() != 0)
            {
                gameBytes.Add(r.ReadByte());
            }

            // Advance past null byte.
            r.ReadByte();
            string gameName = Encoding.UTF8.GetString(gameBytes.ToArray());

            Result.Movie.HeaderEntries[HeaderKeys.GameName] = gameName;

            /*
             * After the header comes "metadata", which is UTF8-coded movie title string. The metadata begins after the ROM
             * name and ends at the savestate offset. This string is displayed as "Author Info" in the Windows version of the
             * emulator.
             */
            var authorBytes = new List <byte>();

            while (r.PeekChar() != 0)
            {
                authorBytes.Add(r.ReadByte());
            }

            // Advance past null byte.
            r.ReadByte();
            string author = Encoding.UTF8.GetString(authorBytes.ToArray());

            Result.Movie.HeaderEntries[HeaderKeys.Author] = author;

            // Advance to first byte of input data.
            r.BaseStream.Position = firstFrameOffset;

            var controllers = new SimpleController
            {
                Definition = _deck.GetDefinition()
            };

            string[] buttons = { "A", "B", "Select", "Start", "Up", "Down", "Left", "Right" };
            bool     fds     = false;

            int frame = 1;

            while (frame <= frameCount)
            {
                byte update = r.ReadByte();

                // aa: Number of delta bytes to follow
                int delta  = (update >> 5) & 0x3;
                int frames = 0;

                /*
                 * The delta byte(s) indicate the number of emulator frames between this update and the next update. It is
                 * encoded in little-endian format and its size depends on the magnitude of the delta:
                 * Delta of:	  Number of bytes:
                 * 0			  0
                 * 1-255		  1
                 * 256-65535	  2
                 * 65536-(2^24-1) 3
                 */
                for (int b = 0; b < delta; b++)
                {
                    frames += r.ReadByte() * (int)Math.Pow(2, b * 8);
                }

                frame += frames;
                while (frames > 0)
                {
                    Result.Movie.AppendFrame(controllers);
                    if (controllers["Reset"])
                    {
                        controllers["Reset"] = false;
                    }

                    frames--;
                }

                if (((update >> 7) & 0x1) != 0)
                {
                    // Control update: 0x1aabbbbb
                    bool reset   = false;
                    int  command = update & 0x1F;

                    // 0xbbbbb:
                    controllers["Reset"] = command == 1;
                    switch (command)
                    {
                    case 0:                             // Do nothing
                        break;

                    case 1:                             // Reset
                        reset = true;
                        break;

                    case 2:                             // Power cycle
                        reset = true;
                        if (frame != 1)
                        {
                            controllers["Power"] = true;
                        }

                        break;

                    case 7:                             // VS System Insert Coin
                        Result.Warnings.Add($"Unsupported command: VS System Insert Coin at frame {frame}");
                        break;

                    case 8:                             // VS System Dipswitch 0 Toggle
                        Result.Warnings.Add($"Unsupported command: VS System Dipswitch 0 Toggle at frame {frame}");
                        break;

                    case 24:                             // FDS Insert
                        fds = true;
                        Result.Warnings.Add($"Unsupported command: FDS Insert at frame {frame}");
                        break;

                    case 25:                             // FDS Eject
                        fds = true;
                        Result.Warnings.Add($"Unsupported command: FDS Eject at frame {frame}");
                        break;

                    case 26:                             // FDS Select Side
                        fds = true;
                        Result.Warnings.Add($"Unsupported command: FDS Select Side at frame {frame}");
                        break;

                    default:
                        Result.Warnings.Add($"Unknown command: {command} detected at frame {frame}");
                        break;
                    }

                    /*
                     * 1 Even if the header says "movie begins from reset", the file still contains a quicksave, and the
                     * quicksave is actually loaded. This flag can't therefore be trusted. To check if the movie actually
                     * begins from reset, one must analyze the controller data and see if the first non-idle command in the
                     * file is a Reset or Power Cycle type control command.
                     */
                    if (!reset && frame == 1)
                    {
                        Result.Errors.Add("Movies that begin with a savestate are not supported.");
                        return;
                    }
                }
                else
                {
                    /*
                     * Controller update: 0aabbccc
                     * bb: Gamepad number minus one (?)
                     */
                    int player = ((update >> 3) & 0x3) + 1;
                    if (player > 2)
                    {
                        Result.Errors.Add("Four score not yet supported.");
                        return;
                    }

                    /*
                     * ccc:
                     * 0	  A
                     * 1	  B
                     * 2	  Select
                     * 3	  Start
                     * 4	  Up
                     * 5	  Down
                     * 6	  Left
                     * 7	  Right
                     */
                    int button = update & 0x7;

                    /*
                     * The controller update toggles the affected input. Controller update data is emitted to the movie file
                     * only when the state of the controller changes.
                     */
                    controllers[$"P{player} {buttons[button]}"] = !controllers[$"P{player} {buttons[button]}"];
                }

                Result.Movie.AppendFrame(controllers);
            }

            if (fds)
            {
                Result.Movie.HeaderEntries[HeaderKeys.BoardName] = "FDS";
            }

            syncSettings.Controls         = controllerSettings;
            Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
        }
Esempio n. 15
0
        protected override void RunImport()
        {
            using var fs = SourceFile.Open(FileMode.Open, FileAccess.Read);
            using var r  = new BinaryReader(fs);
            // 000 4-byte signature: 46 4D 56 1A "FMV\x1A"
            var signature = new string(r.ReadChars(4));

            if (signature != "FMV\x1A")
            {
                Result.Errors.Add("This is not a valid .FMV file.");
                return;
            }

            // 004 1-byte flags:
            byte flags = r.ReadByte();

            // bit 7: 0=reset-based, 1=savestate-based
            if (((flags >> 2) & 0x1) != 0)
            {
                Result.Errors.Add("Movies that begin with a savestate are not supported.");
                return;
            }

            Result.Movie.HeaderEntries[HeaderKeys.PLATFORM] = "NES";
            var syncSettings = new NES.NESSyncSettings();

            // other bits: unknown, set to 0
            // 005 1-byte flags:
            flags = r.ReadByte();

            // bit 5: is a FDS recording
            bool fds;

            if (((flags >> 5) & 0x1) != 0)
            {
                fds = true;
                Result.Movie.HeaderEntries[HeaderKeys.BOARDNAME] = "FDS";
            }
            else
            {
                fds = false;
            }

            // bit 6: uses controller 2
            bool controller2 = ((flags >> 6) & 0x1) != 0;

            // bit 7: uses controller 1
            bool controller1 = ((flags >> 7) & 0x1) != 0;

            // other bits: unknown, set to 0
            // 006 4-byte little-endian unsigned int: unknown, set to 00000000
            r.ReadInt32();

            // 00A 4-byte little-endian unsigned int: rerecord count minus 1
            uint rerecordCount = r.ReadUInt32();

            /*
             * The rerecord count stored in the file is the number of times a savestate was loaded. If a savestate was never
             * loaded, the number is 0. Famtasia however displays "1" in such case. It always adds 1 to the number found in
             * the file.
             */
            Result.Movie.Rerecords = rerecordCount + 1;

            // 00E 2-byte little-endian unsigned int: unknown, set to 0000
            r.ReadInt16();

            // 010 64-byte zero-terminated emulator identifier string
            string emuVersion = NullTerminated(new string(r.ReadChars(64)));

            Result.Movie.Comments.Add($"{EmulationOrigin} Famtasia version {emuVersion}");
            Result.Movie.Comments.Add($"{MovieOrigin} .FMV");

            // 050 64-byte zero-terminated movie title string
            string description = NullTerminated(new string(r.ReadChars(64)));

            Result.Movie.Comments.Add(description);
            if (!controller1 && !controller2 && !fds)
            {
                Result.Warnings.Add("No input recorded.");
            }

            var controllerSettings = new NESControlSettings
            {
                NesLeftPort  = controller1 ? nameof(ControllerNES) : nameof(UnpluggedNES),
                NesRightPort = controller2 ? nameof(ControllerNES) : nameof(UnpluggedNES)
            };

            _deck = controllerSettings.Instantiate((x, y) => true);
            syncSettings.Controls.NesLeftPort  = controllerSettings.NesLeftPort;
            syncSettings.Controls.NesRightPort = controllerSettings.NesRightPort;

            AddDeckControlButtons();

            var controllers = new SimpleController
            {
                Definition = _deck.GetDefinition()
            };

            /*
             * 01 Right
             * 02 Left
             * 04 Up
             * 08 Down
             * 10 B
             * 20 A
             * 40 Select
             * 80 Start
             */
            string[] buttons = { "Right", "Left", "Up", "Down", "B", "A", "Select", "Start" };
            bool[]   masks   = { controller1, controller2, fds };

            /*
             * The file has no terminator byte or frame count. The number of frames is the <filesize minus 144> divided by
             * <number of bytes per frame>.
             */
            int bytesPerFrame = 0;

            for (int player = 1; player <= masks.Length; player++)
            {
                if (masks[player - 1])
                {
                    bytesPerFrame++;
                }
            }

            long frameCount = (fs.Length - 144) / bytesPerFrame;

            for (long frame = 1; frame <= frameCount; frame++)
            {
                /*
                 * Each frame consists of 1 or more bytes. Controller 1 takes 1 byte, controller 2 takes 1 byte, and the FDS
                 * data takes 1 byte. If all three exist, the frame is 3 bytes. For example, if the movie is a regular NES game
                 * with only controller 1 data, a frame is 1 byte.
                 */
                for (int player = 1; player <= masks.Length; player++)
                {
                    if (!masks[player - 1])
                    {
                        continue;
                    }

                    byte controllerState = r.ReadByte();
                    if (player != 3)
                    {
                        for (int button = 0; button < buttons.Length; button++)
                        {
                            controllers[$"P{player} {buttons[button]}"] = ((controllerState >> button) & 0x1) != 0;
                        }
                    }
                    else
                    {
                        Result.Warnings.Add("FDS commands are not properly supported.");
                    }
                }

                Result.Movie.AppendFrame(controllers);
            }

            Result.Movie.SyncSettingsJson = ConfigService.SaveWithType(syncSettings);
        }
Esempio n. 16
0
        protected override void RunImport()
        {
            Result.Movie.HeaderEntries[HeaderKeys.Core] = CoreNames.NesHawk;
            const string emulator = "FCEUX";
            var          platform = VSystemID.Raw.NES;    // TODO: FDS?

            var syncSettings = new NES.NESSyncSettings();

            var controllerSettings = new NESControlSettings
            {
                NesLeftPort  = nameof(UnpluggedNES),
                NesRightPort = nameof(UnpluggedNES)
            };

            _deck = controllerSettings.Instantiate((x, y) => true).AddSystemToControllerDef();

            Result.Movie.HeaderEntries[HeaderKeys.Platform] = platform;

            using var sr = SourceFile.OpenText();
            string line;

            while ((line = sr.ReadLine()) != null)
            {
                if (line == "")
                {
                    continue;
                }

                if (line[0] == '|')
                {
                    ImportInputFrame(line);
                }
                else if (line.ToLower().StartsWith("sub"))
                {
                    var subtitle = ImportTextSubtitle(line);

                    if (!string.IsNullOrEmpty(subtitle))
                    {
                        Result.Movie.Subtitles.AddFromString(subtitle);
                    }
                }
                else if (line.ToLower().StartsWith("emuversion"))
                {
                    Result.Movie.Comments.Add($"{EmulationOrigin} {emulator} version {ParseHeader(line, "emuVersion")}");
                }
                else if (line.ToLower().StartsWith("version"))
                {
                    string version = ParseHeader(line, "version");

                    if (version != "3")
                    {
                        Result.Warnings.Add("Detected a .fm2 movie version other than 3, which is unsupported");
                    }
                    else
                    {
                        Result.Movie.Comments.Add($"{MovieOrigin} .fm2 version 3");
                    }
                }
                else if (line.ToLower().StartsWith("romfilename"))
                {
                    Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "romFilename");
                }
                else if (line.ToLower().StartsWith("cdgamename"))
                {
                    Result.Movie.HeaderEntries[HeaderKeys.GameName] = ParseHeader(line, "cdGameName");
                }
                else if (line.ToLower().StartsWith("romchecksum"))
                {
                    string blob = ParseHeader(line, "romChecksum");
                    byte[] md5  = DecodeBlob(blob);
                    if (md5 != null && md5.Length == 16)
                    {
                        Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower();
                    }
                    else
                    {
                        Result.Warnings.Add("Bad ROM checksum.");
                    }
                }
                else if (line.ToLower().StartsWith("comment author"))
                {
                    Result.Movie.HeaderEntries[HeaderKeys.Author] = ParseHeader(line, "comment author");
                }
                else if (line.ToLower().StartsWith("rerecordcount"))
                {
                    Result.Movie.Rerecords = (ulong)(int.TryParse(ParseHeader(line, "rerecordCount"), out var rerecordCount) ? rerecordCount : default);