Beispiel #1
0
        private static uint GetCrcs(LuigiFileHeader header, string romPath, string cfgPath, out uint cfgCrc, out bool usedLuigiFileCrc)
        {
            usedLuigiFileCrc = false;
            uint romCrc = 0;

            cfgCrc = 0;
            if (header != null)
            {
                if (header.Version > 0)
                {
                    if ((header.OriginalRomFormat == RomFormat.Bin) || (header.OriginalRomFormat == RomFormat.Rom))
                    {
                        romCrc = header.OriginalRomCrc32; // Report the original file's CRC to help identify the ROM.
                    }
                    if (header.OriginalRomFormat == RomFormat.Bin)
                    {
                        cfgCrc = header.OriginalCfgCrc32;
                    }
                }
            }
            if (romCrc == 0 && StreamUtilities.FileExists(romPath))
            {
                usedLuigiFileCrc = true;
                romCrc           = INTV.Core.Utility.Crc32.OfFile(romPath);
            }
            return(romCrc);
        }
Beispiel #2
0
 /// <inheritdoc />
 public override bool Validate()
 {
     IsValid = !string.IsNullOrEmpty(RomPath);
     if (IsValid)
     {
         IsValid = StreamUtilities.FileExists(RomPath);
     }
     return(IsValid);
 }
        public void StreamUtilitiesWithTestStorage_CallOpenFileStream_ReturnsValidStream()
        {
            var storage  = new TestStorageAccess();
            var testPath = @"~/open_create_path.dat";

            using (var stream = StreamUtilities.OpenFileStream(testPath, storage))
            {
                Assert.True(StreamUtilities.FileExists(testPath, storage));
                Assert.NotNull(stream);
            }
            Assert.False(StreamUtilities.FileExists(testPath, storage));
        }
        public void StreamUtilities_InitializeCheckDefaultAndRemoveStorage_Succeeds()
        {
            var storageAcces = new MyPrivateStorageAccess();

            // We use a privately defined type for the storage access to check initialize and remove, which will
            // also guarantee that there is a default storage access at least for the duration of this test.
            Assert.True(StreamUtilities.Initialize(storageAcces));
            Assert.False(StreamUtilities.Initialize(storageAcces));
            Assert.NotNull(StreamUtilities.DefaultStorageAccess);
            Assert.False(StreamUtilities.FileExists(@"~-=</.\/_\/.\>=-~"));
            Assert.True(StreamUtilities.Remove(storageAcces));
            Assert.False(StreamUtilities.Remove(storageAcces));
        }
Beispiel #5
0
        /// <summary>
        /// Get the Crc32 values of a ROM.
        /// </summary>
        /// <param name="romPath">Absolute path of the ROM whose CRC32 value is desired.</param>
        /// <param name="cfgPath">Absolute path of the configuration (.cfg) file whose CRC32 is desired. May be <c>null</c>, depending on the ROM.</param>
        /// <param name="cfgCrc">Receives the CRC32 of the configuration file, if applicable. Could be zero.</param>
        /// <returns>CRC32 of the ROM file.</returns>
        /// <remarks>Instead of zero, should the Crc32.InitialValue be used as a sentinel 'invalid' value?</remarks>
        internal static uint GetCrcs(string romPath, string cfgPath, out uint cfgCrc)
        {
            cfgCrc = 0;
            uint romCrc = 0;

            if (!string.IsNullOrEmpty(romPath) && StreamUtilities.FileExists(romPath))
            {
                romCrc = Crc32.OfFile(romPath);
            }
            if (!string.IsNullOrEmpty(cfgPath) && StreamUtilities.FileExists(cfgPath))
            {
                cfgCrc = Crc32.OfFile(cfgPath);
            }
            return(romCrc);
        }
        /// <summary>
        /// Gets the ROM to use from a <see cref="ProgramDescription"/> for deployment to a device, e.g. Intellicart, CC3, LTO Flash!, emulator, et. al.
        /// </summary>
        /// <param name="programDescription">A <see cref="ProgramDescription"/> that wraps a specific <see cref="IRom"/>.</param>
        /// <returns>The specific <see cref="IRom"/> to use for deployment.</returns>
        /// <remarks>A specific <see cref="IRom"/> may refer to a file that is not presently accessible, e.g. a ROM on a CD-ROM drive, network volume, or other
        /// non-fixed-disk location. In such a case, if the <paramref name="programDescription"/> supplies alternative location(s), the first suitable alternative
        /// location that can be accessed will be used to return a viable <see cref="IRom"/> that, hopefully, is the equivalent of the original.</remarks>
        public static IRom GetRom(this ProgramDescription programDescription)
        {
            var rom     = programDescription.Rom;
            var usesCfg = !string.IsNullOrEmpty(rom.ConfigPath);

            if (!StreamUtilities.FileExists(rom.RomPath) || (usesCfg && !StreamUtilities.FileExists(rom.ConfigPath)))
            {
                var alternateRomPaths = programDescription.Files.AlternateRomImagePaths.ToList();
                var alternateCfgPaths = programDescription.Files.AlternateRomConfigurationFilePaths.ToList();

                if (usesCfg && (alternateRomPaths.Count != alternateCfgPaths.Count))
                {
                    throw new InvalidOperationException(Resources.Strings.ProgramDescription_MissingAlternateCfgFile);
                }

                var    foundAlternate = false;
                string romPath        = null;
                string cfgPath        = null;
                for (var i = 0; (i < alternateRomPaths.Count) && !foundAlternate; ++i)
                {
                    if (StreamUtilities.FileExists(alternateRomPaths[i]))
                    {
                        romPath = alternateRomPaths[i];
                        if (usesCfg)
                        {
                            if (StreamUtilities.FileExists(alternateCfgPaths[i]))
                            {
                                // This code assumes (but cannot check -- silly PCL has no Path API) that the .cfg and ROM are in the same directory for the same index.
                                cfgPath        = alternateCfgPaths[i];
                                foundAlternate = true;
                            }
                        }
                        else
                        {
                            foundAlternate = true;
                        }
                    }
                }
                if (foundAlternate)
                {
                    rom = new AlternateRom(romPath, cfgPath, rom);
                }
            }
            return(rom);
        }
Beispiel #7
0
        /// <inheritdoc />
        public override bool Validate()
        {
            var isValid = !string.IsNullOrEmpty(RomPath);

            if (isValid)
            {
                isValid = StreamUtilities.FileExists(RomPath);
            }
            if (isValid)
            {
                if (!string.IsNullOrEmpty(ConfigPath))
                {
                    isValid = StreamUtilities.FileExists(ConfigPath);
                }
            }
            IsValid = isValid;
            return(IsValid);
        }
Beispiel #8
0
        /// <summary>
        /// Locates the first data block of the requested type in the ROM.
        /// </summary>
        /// <typeparam name="T">The type of LUIGI block to locate.</typeparam>
        /// <returns>The data block, or <c>null</c> if no block of the requested type is in the ROM.</returns>
        internal T LocateDataBlock <T>() where T : LuigiDataBlock
        {
            var dataBlock = default(T);

            if (StreamUtilities.FileExists(RomPath))
            {
                using (var file = StreamUtilities.OpenFileStream(RomPath))
                {
                    if (file != null)
                    {
                        if (file.Length > 0)
                        {
                            var desiredBlockType = LuigiDataBlock.GetBlockType <T>();
                            var luigiHeader      = LuigiFileHeader.Inflate(file);
                            var bytesRead        = luigiHeader.DeserializeByteCount;

                            // Start looking for desired block immediately after header.
                            var block = LuigiDataBlock.Inflate(file);
                            bytesRead += block.DeserializeByteCount;
                            if (StopIfScrambleKeyBlockFound(desiredBlockType, block.Type))
                            {
                                // Stop looking. If we hit the scramble key, there's nothing more to be looked at.
                                bytesRead = (int)file.Length;
                            }
                            while ((bytesRead < file.Length) && (block.Type != desiredBlockType) && (block.Type != LuigiDataBlockType.EndOfFile))
                            {
                                block      = LuigiDataBlock.Inflate(file);
                                bytesRead += block.DeserializeByteCount;
                                if (StopIfScrambleKeyBlockFound(desiredBlockType, block.Type))
                                {
                                    break;
                                }
                            }
                            if (block.Type == desiredBlockType)
                            {
                                dataBlock = block as T;
                            }
                        }
                    }
                }
            }
            return(dataBlock);
        }
Beispiel #9
0
        /// <inheritdoc />
        public override uint RefreshCrc(out bool changed)
        {
            var crc = _crc;

            if (IsValid)
            {
                if (StreamUtilities.FileExists(RomPath))
                {
                    uint dontCare;
                    _crc = GetCrcs(RomPath, null, out dontCare);
                    if (crc == 0)
                    {
                        crc = _crc; // lazy initialization means on first read, we should never get a change
                    }
                }
            }
            changed = crc != _crc;
            return(_crc);
        }
Beispiel #10
0
        private static uint GetCrcs(RomFormat format, string romPath, string cfgPath, out uint cfgCrc)
        {
            cfgCrc = 0;
            uint romCrc = 0;

            if (StreamUtilities.FileExists(romPath))
            {
                byte replacementByte = AutoBaudBytes[format];

#if IGNORE_METADATA_FOR_CRC
                var metadataRange  = new List <Range <int> >();
                var metadataOffset = GetMetadataOffset();
                metadataRange.Add(new Range <int>(metadataOffset, int.MaxValue));
                romCrc = Crc32.OfFile(romPath, format != RomFormat.Intellicart, replacementByte, metadataRange);
#else
                romCrc = Crc32.OfFile(romPath, format != RomFormat.Intellicart, replacementByte);
#endif // IGNORE_METADATA_FOR_CRC
            }
            return(romCrc);
        }
Beispiel #11
0
        /// <summary>
        /// Gets a path to an existing stock config file path given its canonical CFG identifier.
        /// </summary>
        /// <param name="stockConfigFileNumber">A positive integer value indicating which canonical configuration file whose disk location is desired.</param>
        /// <returns>The absolute path to the file, or <c>null</c> if a file for the given canonical configuration file does not exist.</returns>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if <paramref name="stockConfigFileNumber"/> is less than zero.</exception>
        public static string GetStockCfgFilePath(int stockConfigFileNumber)
        {
            var stockCfgFileName = stockConfigFileNumber.ToString(CultureInfo.InvariantCulture) + ProgramFileKind.CfgFile.FileExtension();
            var stockCfgUri      = new Uri(DefaultToolsDirectory + stockCfgFileName);
            var stockCfgFilePath = Uri.UnescapeDataString(stockCfgUri.AbsolutePath); // Need to unescape spaces.

#if WIN
            stockCfgFilePath = stockCfgFilePath.Replace('/', System.IO.Path.DirectorySeparatorChar);
#elif PCL
            // NOTE: This will, of course, cause trouble if we build PCL for non-Windows platforms, in which case the proper
            // solution is to register the 'FixUpUri' method and use it instead... In fact, it would be better to just have a
            // file system interface to use for stuff like this... Maybe imported via MEF or some such...
            stockCfgFilePath = stockCfgFilePath.Replace('/', '\\');
#endif // WIN
            if (!StreamUtilities.FileExists(stockCfgFilePath))
            {
                stockCfgFilePath = null;
            }
            return(stockCfgFilePath);
        }
Beispiel #12
0
        /// <inheritdoc />
        public override uint RefreshCfgCrc(out bool changed)
        {
            var cfgCrc = _cfgCrc;

            if (IsValid)
            {
                if (!string.IsNullOrEmpty(ConfigPath))
                {
                    if (StreamUtilities.FileExists(ConfigPath))
                    {
                        GetCrcs(null, ConfigPath, out _cfgCrc);
                        if (cfgCrc == 0)
                        {
                            cfgCrc = _cfgCrc; // lazy initialization means on first read, we should never get a change
                        }
                    }
                }
            }
            changed = cfgCrc != _cfgCrc;
            return(_cfgCrc);
        }
Beispiel #13
0
        /// <summary>
        /// Ensures that a configuration file can be located for a given ROM.
        /// </summary>
        /// <param name="rom">A ROM that may need a configuration (.cfg) file.</param>
        /// <param name="programInfo">Detailed <see cref="IProgramInformation"/> providing detailed data about the ROM.</param>
        /// <returns><c>true</c> if <paramref name="rom"/> uses an existing stock CFG file, <c>false</c> otherwise.</returns>
        /// <remarks>If a ROM requires a configuration file, but one cannot be found, the best possible matching file will be searched for. This is only applicable
        /// to .bin format ROMs (or their compatriots, .itv and .int files, typically). If <paramref name="rom"/> either does not provide a .cfg file, or
        /// the file it provides cannot be found, the best possible match based on <paramref name="programInfo"/> will be used. If <paramref name="programInfo"/>
        /// is also null, the CRC of the ROM will be checked against the active ROM database in memory for a possible stock .cfg file match. If the matching
        /// stock file is found, then the function returns <c>true</c>.</remarks>
        public static bool EnsureCfgFileProvided(this IRom rom, IProgramInformation programInfo)
        {
            var usesStockCfgFile = false;

            if ((rom.Format == RomFormat.Bin) && (string.IsNullOrEmpty(rom.ConfigPath) || !StreamUtilities.FileExists(rom.ConfigPath)))
            {
                var cfgFilePath = GetStockCfgFile(rom.Crc, rom.RomPath, programInfo);
                usesStockCfgFile = !string.IsNullOrEmpty(cfgFilePath);
                if (usesStockCfgFile)
                {
                    rom.UpdateCfgFile(cfgFilePath);
                }
            }
            return(usesStockCfgFile);
        }
Beispiel #14
0
        /// <summary>
        /// Safely retrieves the LUIGI header for a ROM.
        /// </summary>
        /// <param name="rom">The ROM whose LUIGI header is requested.</param>
        /// <returns>The <see cref="LuigiFileHeader"/> for the ROM, or <c>null</c> if the ROM is not in the LUIGI format.</returns>
        public static LuigiFileHeader GetLuigiHeader(this IRom rom)
        {
            LuigiFileHeader luigiHeader = null;

            if ((rom != null) && (rom.Format == RomFormat.Luigi) && !string.IsNullOrEmpty(rom.RomPath) && StreamUtilities.FileExists(rom.RomPath) && LuigiFileHeader.PotentialLuigiFile(rom.RomPath))
            {
                luigiHeader = LuigiFileHeader.GetHeader(rom.RomPath);
            }
            return(luigiHeader);
        }
        public void StreamUtilitiesWithTestStorage_CallFileExistsWithNonexistentPath_ReturnsFalse()
        {
            var storage = new TestStorageAccess();

            Assert.False(StreamUtilities.FileExists(@"SomeInvalidPathThatDoesNotExist", storage));
        }
        /// <summary>
        /// Updates the validation state of the given support file.
        /// </summary>
        /// <param name="whichFile">Which file to validate.</param>
        /// <param name="crc">If non-zero, the CRC of the program ROM to compare against if validating the ROM file.</param>
        /// <param name="programDescription">The program description being validated (if applicable).</param>
        /// <param name="peripherals">The peripherals attached to the system, used for compatibility checks.</param>
        /// <param name="connectedPeripheralsHistory">The peripherals that have been attached to the system over time.</param>
        /// <param name="reportIfModified">If <c>true</c>, check if the file has been modified, not just whether it exists.</param>
        /// <returns>The updated state of the file.</returns>
        public ProgramSupportFileState ValidateSupportFile(ProgramFileKind whichFile, uint crc, IProgramDescription programDescription, IEnumerable <IPeripheral> peripherals, IEnumerable <IPeripheral> connectedPeripheralsHistory, bool reportIfModified)
        {
            var validationState = ProgramSupportFileState.None;

            switch (whichFile)
            {
            case ProgramFileKind.Rom:
                var isValid = Rom.Validate();
                if (!string.IsNullOrEmpty(RomImagePath))
                {
                    var previousValidationState = ProgramSupportFileState.None;
                    _supportFileStates.TryGetValue(whichFile, out previousValidationState);
                    if (!StreamUtilities.FileExists(RomImagePath))
                    {
                        validationState = ProgramSupportFileState.Missing;
                        if (AlternateRomImagePaths.Any(p => StreamUtilities.FileExists(p)))
                        {
                            validationState = ProgramSupportFileState.MissingWithAlternateFound;
                        }
                    }
                    else if (reportIfModified)
                    {
                        isValid = _programRom.IsValid;
                        if (crc != 0)
                        {
                            // In some cases, the CRC provided is actually Rom.Crc, so if they match, recompute the CRC.
                            var cfgCrc = 0u;
                            isValid = (Rom.Crc == crc) && (crc == GetRefreshedCrcForRom(RomImagePath, RomConfigurationFilePath, out cfgCrc) && (Rom.CfgCrc == cfgCrc));
                        }
                        validationState = isValid ? ProgramSupportFileState.PresentAndUnchanged : ProgramSupportFileState.PresentButModified;
                    }
                    switch (validationState)
                    {
                    case ProgramSupportFileState.PresentAndUnchanged:
                    case ProgramSupportFileState.None:
                        // Treat a ROM file's missing or modified state as higher priority to report than peripheral-related information.
                        // This bit of code is entirely LTO Flash!-specific in its assumptions. If there should ever be other
                        // peripheral-specific needs to address here, a larger architectural change may be necessary. While the
                        // language of the states here is neutral, the basis of this check is not.
                        var rom = programDescription == null ? Rom : programDescription.Rom;
                        var requiresPeripheral = rom.IsLtoFlashOnlyRom();
                        if (requiresPeripheral)
                        {
                            var isUniversallyCompatible          = rom.GetTargetDeviceUniqueId() == LuigiScrambleKeyBlock.AnyLTOFlashId;
                            var matchesPeripheralInDeviceHistory = isUniversallyCompatible || ((connectedPeripheralsHistory != null) && (connectedPeripheralsHistory.FirstOrDefault(p => p.IsRomCompatible(programDescription)) != null));
                            var canRunOnConnected = isUniversallyCompatible || ((peripherals != null) && (peripherals.FirstOrDefault(p => p.IsRomCompatible(programDescription)) != null));

                            if (peripherals == null)
                            {
                                // If previous validation state was due to peripheral, retain it, since we don't
                                // have any peripherals to check against.
                                if (validationState != previousValidationState)
                                {
                                    switch (previousValidationState)
                                    {
                                    case ProgramSupportFileState.RequiredPeripheralAvailable:
                                    case ProgramSupportFileState.RequiredPeripheralIncompatible:
                                    case ProgramSupportFileState.RequiredPeripheralNotAttached:
                                    case ProgramSupportFileState.RequiredPeripheralUnknown:
                                        validationState = previousValidationState;
                                        break;

                                    case ProgramSupportFileState.None:
                                        validationState = matchesPeripheralInDeviceHistory ? ProgramSupportFileState.RequiredPeripheralNotAttached : ProgramSupportFileState.RequiredPeripheralUnknown;
                                        break;

                                    default:
                                        // TODO: Decide if the following is a bug:
                                        // 0: Presume a scrambled (unique) LUIGI, no device or device history provided (null)
                                        // 1. Initially ROM's file is missing, but its alternate is found - this caches the 'MissingButAlternateFound' state
                                        // 2. Update ROM to use alternate path as primary path
                                        // 3. Re-validate
                                        // At this point, the "Present and unmodified" state is used -- despite the ROM requiring
                                        // a specific device.
                                        // Why is this considered correct at this time?
                                        // a) When no devices or device history are give (nulls), it's impossible to know. So just use the simple state of he file.
                                        // b) It MAY be a bug that, if we pass in EMPTY peripheral / history lists that we should consider something different... but
                                        //    then again, should we report 'unknown peripheral' at that time? Or would reporting 'not attached' be better?
                                        //    What about 'universally' scrambled ROMs? Using 'not attached' may be more accurate then as well...
                                        // The case of scrambled ROMs likely needs more careful consideration generally...
                                        break;
                                    }
                                }
                            }
                            else
                            {
                                if (peripherals.Any())
                                {
                                    if (canRunOnConnected)
                                    {
                                        validationState = ProgramSupportFileState.RequiredPeripheralAvailable;
                                    }
                                    else
                                    {
                                        validationState = ProgramSupportFileState.RequiredPeripheralIncompatible;
                                    }
                                }
                                else
                                {
                                    validationState = matchesPeripheralInDeviceHistory ? ProgramSupportFileState.RequiredPeripheralNotAttached : ProgramSupportFileState.RequiredPeripheralUnknown;
                                }
                            }
                        }
                        break;

                    default:
                        break;
                    }
                }
                break;

            default:
                // TODO: Implement remaining validation code.
                break;
            }
            _supportFileStates[whichFile] = validationState;
            return(validationState);
        }