private static LuigiFileHeader GetLuigiHeaderForFork(ExecuteDeviceCommandAsyncTaskData data, Fork fork)
        {
            LuigiFileHeader luigiHeader = null;

            using (var memory = new System.IO.MemoryStream())
            {
                const uint Address     = 0u;
                var        bytesToRead = LuigiFileHeader.MaxHeaderSize;
                var        succeeded   = ReadForkToRam.Create(Address, fork.GlobalForkNumber, 0u, bytesToRead).Execute <bool>(data.Device.Port, data);
                byte[]     dataRead    = null;
                if (succeeded)
                {
                    dataRead = (byte[])DownloadDataBlockFromRam.Create(Address, bytesToRead).Execute(data.Device.Port, data, out succeeded);
                }
                if (succeeded)
                {
                    memory.Write(dataRead, 0, bytesToRead);
                    memory.Seek(0, System.IO.SeekOrigin.Begin);
                    try
                    {
                        luigiHeader = LuigiFileHeader.Inflate(memory);
                    }
                    catch (INTV.Core.UnexpectedFileTypeException)
                    {
                    }
                }
            }
            return(luigiHeader);
        }
Пример #2
0
        private bool LuigiNeedsUpdate(IRom rom, out IRom luigiRom, out bool treatAsTemporary)
        {
            luigiRom         = null;
            treatAsTemporary = false;
            System.Diagnostics.Debug.Assert(rom.Format != RomFormat.Luigi, "Only non-LUIGI format ROMs should be checked!");
            var romPath   = rom.RomPath;
            var luigiPath = Path.ChangeExtension(romPath, RomFormat.Luigi.FileExtension());

            if (romPath.IsPathOnRemovableDevice())
            {
                // look in app's local ROMs directory for a LUIGI file
                var localRomsDirectory = RomListConfiguration.Instance.RomsDirectory;
                luigiPath        = Path.Combine(localRomsDirectory, Path.GetFileName(luigiPath));
                treatAsTemporary = File.Exists(luigiPath);
                if (treatAsTemporary)
                {
                    luigiPath = luigiPath.EnsureUniqueFileName();
                }
            }
            var needsUpdate = !File.Exists(luigiPath);

            if (!needsUpdate)
            {
                var luigiHeader = LuigiFileHeader.GetHeader(luigiPath);
                if (luigiHeader != null)
                {
                    needsUpdate = (rom.Format != luigiHeader.OriginalRomFormat) || (rom.Crc != luigiHeader.OriginalRomCrc32) || (rom.CfgCrc != luigiHeader.OriginalCfgCrc32);
                }
                else
                {
                    needsUpdate = true;
                }
            }
            return(needsUpdate);
        }
Пример #3
0
        public void LuigiFileHeader_ReadVersionZeroHeader_DoesNotThrowAndReportsVersionZero()
        {
            var romPath = LuigiFileHeaderTestStorageAccess.Initialize(TestRomResources.TestLuigiFromBinWithVersionZeroHeaderPath).First();

            var header = LuigiFileHeader.GetHeader(romPath);

            Assert.Equal(0, header.Version);
        }
Пример #4
0
        public void GZipAccess_WriteRomResourceToGZipFile_ProducesExpectedResult(CompressedArchiveAccessImplementation implementation)
        {
            var gzipFileName = TemporaryFile.GenerateUniqueFilePath("tagalong", ".luigi.gz");

            using (TemporaryFile.CreateTemporaryFileWithPath(gzipFileName, createEmptyFile: false))
            {
                // Create on-disk GZIP
                var inputLength = 0L;
                var fileStream  = new FileStream(gzipFileName, FileMode.Create, FileAccess.Write);
                using (var gzip = CompressedArchiveAccess.Open(fileStream, CompressedArchiveFormat.GZip, CompressedArchiveAccessMode.Create, implementation))
                {
                    var testResourceName = "INTV.TestHelpers.Core.Resources.tagalong.luigi";
                    var newGZipEntryName = "tagalong.luigi";
                    var entry            = gzip.CreateEntry(newGZipEntryName);
                    using (var gzipStream = gzip.Open(entry.Name))
                        using (var sourceStream = typeof(TestRomResources).Assembly.GetManifestResourceStream(testResourceName))
                        {
                            sourceStream.CopyTo(gzipStream);
                            inputLength = sourceStream.Length;
                        }
                }

                // Now, see if we can extract it!
                var extractedRomPath = Path.Combine(Path.GetDirectoryName(gzipFileName), Path.GetFileNameWithoutExtension(gzipFileName));
                using (TemporaryFile.CreateTemporaryFileWithPath(extractedRomPath, createEmptyFile: false))
                {
                    var fileInfo = new FileInfo(gzipFileName);
                    Assert.True(fileInfo.Exists);
                    Assert.True(inputLength > fileInfo.Length); // Compressed we must be! On this, all depends.
                    using (var gzip = CompressedArchiveAccess.Open(gzipFileName, CompressedArchiveAccessMode.Read, implementation))
                    {
                        Assert.True(gzip.Entries.Any());
                        var entry = gzip.Entries.Single();
                        Assert.False(string.IsNullOrEmpty(entry.Name));
                        using (var outputFileStream = new FileStream(extractedRomPath, FileMode.Create, FileAccess.Write))
                            using (var gzipStream = gzip.OpenEntry(entry))
                            {
                                gzipStream.CopyTo(outputFileStream);
                            }
                    }

                    // Verify we have a valid LUIGI and it's got what we expect inside. Trust, but verify!
                    LuigiFileHeader header = null;
                    using (var outputFileStream = new FileStream(extractedRomPath, FileMode.Open, FileAccess.Read))
                    {
                        header = LuigiFileHeader.Inflate(outputFileStream);
                    }

                    Assert.NotNull(header);
                    Assert.Equal(RomFormat.Bin, header.OriginalRomFormat);
                    Assert.Equal(TestRomResources.TestBinCrc, header.OriginalRomCrc32);
                }
            }
        }
Пример #5
0
        public void LuigiFileHeader_IsPotentialLuigiFile_ProvidesCorrectResult(string resourceToRegister, string potentialLuigiFilePath, bool expectedIsPotentialLuigiFile)
        {
            var romFilePath = LuigiFileHeaderTestStorageAccess.Initialize(resourceToRegister).First();

            if (string.IsNullOrEmpty(potentialLuigiFilePath))
            {
                potentialLuigiFilePath = romFilePath;
            }

            Assert.Equal(expectedIsPotentialLuigiFile, LuigiFileHeader.PotentialLuigiFile(potentialLuigiFilePath));
        }
Пример #6
0
        public void LuigiFileHeader_SerializeHeaderFromTestRom_SerializesCorrectNumberOfBytes()
        {
            var romPath = LuigiFileHeaderTestStorageAccess.Initialize(TestRomResources.TestLuigiFromBinPath).First();
            var header  = LuigiFileHeader.GetHeader(romPath);

            using (var writer = new BinaryWriter(new System.IO.MemoryStream()))
            {
                var bytesSerialized = header.Serialize(writer);

                Assert.Equal(32, bytesSerialized);
            }
        }
Пример #7
0
        public void LuigiFileHeader_DeserializeWithTruncatedMagicKey_ThrowsUnexpectedFileTypeException()
        {
            var bogusHeaderMagic = "LT";

            using (var stream = new System.IO.MemoryStream(Encoding.UTF8.GetBytes(bogusHeaderMagic)))
            {
                using (var reader = new BinaryReader(stream))
                {
                    Assert.Throws <UnexpectedFileTypeException>(() => LuigiFileHeader.Inflate(reader));
                }
            }
        }
Пример #8
0
        public void LuigiFileHeader_GetFromValidLuigiRom_DoesNotThrowAndReportsExpectedVersion()
        {
            IReadOnlyList <string> paths;
            var storage = LuigiFileHeaderTestStorageAccess.Initialize(out paths, TestRomResources.TestLuigiFromRomPath);

            using (var reader = new BinaryReader(storage.Open(paths.First())))
            {
                var header = LuigiFileHeader.Inflate(reader);

                Assert.Equal(1, header.Version);
                Assert.Equal(32, header.DeserializeByteCount);
            }
        }
Пример #9
0
        public void LuigiFileHeader_SerializeDefaultVersionZeroHeader_SerializesCorrectNumberOfBytes()
        {
            var header = new LuigiFileHeader()
            {
                Reserved = new byte[LuigiFileHeader.ReservedHeaderBytesSize]
            };

            using (var writer = new BinaryWriter(new System.IO.MemoryStream()))
            {
                var bytesSerialized = header.Serialize(writer);

                Assert.Equal(16, bytesSerialized);
            }
        }
Пример #10
0
        public void LuigiFileHeader_DeserializeWithTruncatedHeaderAfterKey_ThrowsEndOfStreamException()
        {
            var truncatedHeader = new byte[]
            {
                0x4C, 0x54, 0x4F // LTO
            };

            using (var stream = new System.IO.MemoryStream(truncatedHeader))
            {
                using (var reader = new BinaryReader(stream))
                {
                    Assert.Throws <System.IO.EndOfStreamException>(() => LuigiFileHeader.Inflate(reader));
                }
            }
        }
Пример #11
0
        public void LuigiFileHeader_DeserializeWithTruncatedHeaderInUid_ThrowsEndOfStreamException()
        {
            var truncatedHeader = new byte[]
            {
                0x4C, 0x54, 0x4F, 0x01,                                                                         // LTO, version
                0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // features (v1)
            };

            using (var stream = new System.IO.MemoryStream(truncatedHeader))
            {
                using (var reader = new BinaryReader(stream))
                {
                    Assert.Throws <System.IO.EndOfStreamException>(() => LuigiFileHeader.Inflate(reader));
                }
            }
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="INTV.Core.Model.Program.LuigiFileMetadataProgramInformation"/> class.
 /// </summary>
 /// <param name="header">A LUIGI file header that describes the ROM's features.</param>
 /// <param name="metadata">Additional ROM metadata, if any.</param>
 public LuigiFileMetadataProgramInformation(LuigiFileHeader header, LuigiMetadataBlock metadata)
 {
     _features = ProgramFeatures.Combine(header.Features.ToProgramFeatures(), header.Features2.ToProgramFeatures());
     _crc      = new CrcData(header.OriginalRomCrc32, string.Empty, _features.ToIncompatibilityFlags());
     Metadata  = metadata;
     if (metadata != null)
     {
         _title  = metadata.LongNames.FirstOrDefault();
         _vendor = metadata.Publishers.FirstOrDefault();
         if (metadata.ReleaseDates.Any())
         {
             _year = metadata.ReleaseDates.First().Date.Year.ToString();
         }
         _shortName = metadata.ShortNames.FirstOrDefault();
     }
 }
Пример #13
0
        public void LuigiFileHeader_UpdateFeaturesOnScrambledLuigiRom_UpdatesFeatures(LuigiFeatureFlags newFeatures, bool forceFeatureUpdate)
        {
            var romPath = LuigiFileHeaderTestStorageAccess.Initialize(TestRomResources.TestLuigiScrambledForDevice0Path).First();

            var header      = LuigiFileHeader.GetHeader(romPath);
            var originalCrc = header.Crc;

            Assert.Equal(0xEC, header.Crc);

            var crcShouldChange = header.Features != newFeatures;

            header.UpdateFeatures(newFeatures, forceFeatureUpdate);

            Assert.Equal(newFeatures, header.Features);
            Assert.Equal(crcShouldChange, header.Crc != originalCrc);
        }
Пример #14
0
        public void LuigiFileHeader_UpdateFeaturesOnLuigiWithMetadata_UpdatesFeatures(LuigiFeatureFlags newFeatures, bool forceFeatureUpdate)
        {
            var romPath = LuigiFileHeaderTestStorageAccess.Initialize(TestRomResources.TestLuigiScrambledForDevice1Path).First();

            var header           = LuigiFileHeader.GetHeader(romPath);
            var originalCrc      = header.Crc;
            var expectedFeatures = header.WouldModifyFeatures(newFeatures, forceFeatureUpdate) ? newFeatures : header.Features;

            Assert.Equal(0x8B, originalCrc);

            var crcShouldChange = header.Features != expectedFeatures;

            header.UpdateFeatures(newFeatures, forceFeatureUpdate);

            Assert.Equal(expectedFeatures, header.Features);
            Assert.Equal(crcShouldChange, header.Crc != originalCrc);
        }
Пример #15
0
        /// <inheritdoc />
        internal override void LoadComplete(FileSystem fileSystem, bool updateRomList)
        {
            base.LoadComplete(fileSystem, updateRomList);
            if (Description == null)
            {
                if (Rom != null)
                {
                    if (Crc32 == 0)
                    {
                        var filePath = Rom.FilePath;
                        INTV.Core.Model.LuigiFileHeader luigiHeader = LuigiFileHeader.GetHeader(filePath);
                        if (luigiHeader != null)
                        {
                            string cfgFile = null;
                            string romFile = filePath;
                            uint   crc     = 0u;
                            if (luigiHeader.Version > 0)
                            {
                                crc = luigiHeader.OriginalRomCrc32;
                            }
                            else
                            {
                                var jzIntvConfiguration = INTV.Shared.Utility.SingleInstanceApplication.Instance.GetConfiguration <INTV.JzIntv.Model.Configuration>();
                                var luigiToBinPath      = jzIntvConfiguration.GetProgramPath(JzIntv.Model.ProgramFile.Luigi2Bin);
                                var luigiToBinResult    = RunExternalProgram.Call(luigiToBinPath, "\"" + filePath + "\"", System.IO.Path.GetDirectoryName(filePath));
                                if (luigiToBinResult == 0)
                                {
                                    romFile = System.IO.Path.ChangeExtension(filePath, RomFormat.Bin.FileExtension());
                                    cfgFile = System.IO.Path.ChangeExtension(filePath, ProgramFileKind.CfgFile.FileExtension());
                                    if (!System.IO.File.Exists(cfgFile))
                                    {
                                        cfgFile = null;
                                    }
                                }
                            }

                            var rom                = INTV.Core.Model.Rom.Create(romFile, cfgFile);
                            var programInfo        = rom.GetProgramInformation();
                            var programDescription = new ProgramDescription(rom.Crc, rom, programInfo);
                            Description = programDescription;
                        }
                    }
                }
            }
        }
Пример #16
0
        public void LuigiFileHeader_DeserializeWithFutureVersion_ThrowsInvalidOperationException()
        {
            var futureHeader = new byte[]
            {
                0x4C, 0x54, 0x4F, 0xD0,                                                                         // LTO, version -- bogus instead of 0x01
                0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // features (v1)
                0x81, 0x93, 0x43, 0xB0, 0x0C, 0x47, 0x3F, 0x12,                                                 // UID
                0x00, 0x00, 0x00,                                                                               // reserved
                0x8B                                                                                            // CRC
            };

            using (var stream = new System.IO.MemoryStream(futureHeader))
            {
                using (var reader = new BinaryReader(stream))
                {
                    Assert.Throws <InvalidOperationException>(() => LuigiFileHeader.Inflate(reader));
                }
            }
        }
Пример #17
0
        /// <summary>
        /// Initializes a new instance of the <see cref="INTV.LtoFlash.Model.CacheIndexEntry"/> class.
        /// </summary>
        /// <param name="rom">The ROM described the entry.</param>
        /// <param name="romAbsolutePath">Absolute path to the ROM.</param>
        public CacheIndexEntry(IRom rom, string romAbsolutePath)
        {
            var romStagingArea = SingleInstanceApplication.Instance.GetConfiguration <Configuration>().RomsStagingAreaPath;

            RomPath  = romAbsolutePath.Substring(romStagingArea.Length + 1);
            RomCrc32 = rom.Crc;
            RomSize  = (uint)(new FileInfo(romAbsolutePath)).Length;

            if (rom.Format == RomFormat.Bin)
            {
                var cfgFilePath = Path.ChangeExtension(romAbsolutePath, ProgramFileKind.CfgFile.FileExtension());
                if (File.Exists(cfgFilePath))
                {
                    CfgPath  = cfgFilePath.Substring(romStagingArea.Length + 1);
                    CfgCrc32 = INTV.Core.Utility.Crc32.OfFile(cfgFilePath);
                    CfgSize  = (uint)(new FileInfo(cfgFilePath)).Length;
                }
            }

            var luigiFilePath = rom.GetLtoFlashFilePath();

            LuigiPath = luigiFilePath.Substring(romStagingArea.Length + 1);
            if (!System.IO.File.Exists(luigiFilePath) && LuigiFileHeader.PotentialLuigiFile(romAbsolutePath))
            {
                // This has been known to happen in the transition between naming conventions, but also arises generally when
                // a ROM in the main ROM list has always been a LUIGI file. We have an original LUIGI file,
                // and find a cache entry, and are updating it -- we may not have the newly named LUIGI file yet. So, just copy it.
                // What happens is, *in the cache* we found foo.luigi, but the cached file path has become foo_luigi.luigi.
                if (System.IO.File.Exists(romAbsolutePath))
                {
                    System.IO.File.Copy(romAbsolutePath, luigiFilePath, true);
                }
            }
            LuigiCrc24 = INTV.Core.Utility.Crc24.OfFile(luigiFilePath);
            LuigiSize  = (uint)(new FileInfo(luigiFilePath)).Length;
        }
Пример #18
0
        private System.Exception UpdateFilePath(string filePath, bool forceUpdate)
        {
#if REPORT_PERFORMANCE
            System.Diagnostics.Debug.WriteLine(">>>> ENTER Fork.UpdateFilePath(" + filePath + "," + forceUpdate + ") + T:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            try
            {
#endif // REPORT_PERFORMANCE
            System.Exception error = null;
            if ((FileSystem != null) && FileSystem.Frozen)
            {
                // During save, do not attempt to regenerate contents.
                return(error);
            }
            try
            {
                // Do some special work if this is a ROM fork.
                if ((FileSystem != null) && (FileSystem.GetForkKind(this) == ForkKind.Program))
                {
                    if (forceUpdate)
                    {
#if REPORT_PERFORMANCE
                        System.Diagnostics.Debug.WriteLine(">>>>>> ENTER Fork.UpdateFilePath(forceUpdate): GetRom");
                        var stopwatch2 = System.Diagnostics.Stopwatch.StartNew();
                        try
                        {
#endif // REPORT_PERFORMANCE
                        var filesUsingFork = FileSystem.GetFilesUsingForks(new[] { this }).Values;
                        foreach (var program in filesUsingFork.OfType <Program>())
                        {
                            // Since "Program" entries using the same Fork will all point to this one,
                            // we don't need to re-do this work for every file that might point at the fork.
                            var rom = program.Description.GetRom();
                            if ((rom != null) && !string.IsNullOrEmpty(rom.RomPath))
                            {
                                var path = string.Empty;
#if REPORT_PERFORMANCE
                                var stopwatch3 = System.Diagnostics.Stopwatch.StartNew();
#endif // REPORT_PERFORMANCE
                                path = rom.PrepareForDeployment(LuigiGenerationMode.Standard);
#if REPORT_PERFORMANCE
                                stopwatch3.Stop();
                                AccumulatedUpdateFilePathForcedPrepareTime += stopwatch3.Elapsed;
#endif // REPORT_PERFORMANCE
                                UpdateFilePath(path);
                            }
                        }
#if REPORT_PERFORMANCE
                    }
                    finally
                    {
                        stopwatch2.Stop();
                        System.Diagnostics.Debug.WriteLine(">>>>>> EXIT  Fork.UpdateFilePath(forceUpdate): GetRom");     // took: + " + stopwatch2.Elapsed.ToString());
                        AccumulatedUpdateFilePathForcedTime += stopwatch2.Elapsed;
                    }
#endif // REPORT_PERFORMANCE
                    }
                    else
                    {
                        var rom = Rom;
                        var prepareForDeployment = string.IsNullOrEmpty(filePath) && (rom != null);
                        if (!prepareForDeployment && (rom != null))
                        {
                            // We think we don't have to prepare for deployment, but do have ROM set.
                            // Let's just make sure that we have that LUIGI file, shall we?
                            prepareForDeployment = string.IsNullOrEmpty(_filePath) || !System.IO.File.Exists(_filePath);
                            if (!prepareForDeployment && System.IO.File.Exists(_filePath))
                            {
                                // Check to see if it looks like a valid LUIGI file.
                                prepareForDeployment = LuigiFileHeader.GetHeader(_filePath) == null;
                            }
                        }
                        if (prepareForDeployment)
                        {
                            // No file path, but somehow we have a ROM. Force recreation of the LUIGI file. This can
                            // occur during sync from device.
                            UpdateFilePath(Rom.PrepareForDeployment(LuigiGenerationMode.Standard));
                        }
                        else if (rom == null)
                        {
                            if (!System.IO.File.Exists(filePath))
                            {
                                // No ROM, and the file we think we have does not exist.
                                ILfsFileInfo file;
                                rom = null;
                                if (FileSystem.GetFilesUsingForks(new[] { this }).TryGetValue(this, out file))
                                {
                                    switch (FileSystem.Origin)
                                    {
                                    case FileSystemOrigin.HostComputer:
                                        var description = ((Program)file).Description;
                                        rom = description == null ? null : description.GetRom();
                                        break;

                                    case FileSystemOrigin.LtoFlash:
                                        break;
                                    }
                                }
                                prepareForDeployment = (rom != null) && !System.IO.File.Exists(filePath) && !string.IsNullOrEmpty(rom.RomPath) && System.IO.File.Exists(rom.RomPath);
                                if (prepareForDeployment)
                                {
                                    _filePath = null;
                                    UpdateFilePath(rom.PrepareForDeployment(LuigiGenerationMode.Standard));
                                }
                            }

                            // We should *always* be a LUIGI file. If not, fix it!
                            if (!string.IsNullOrEmpty(filePath) && System.IO.File.Exists(filePath))
                            {
                                if (!LuigiFileHeader.PotentialLuigiFile(filePath))
                                {
                                    rom = INTV.Core.Model.Rom.Create(filePath, null);
                                    prepareForDeployment = rom != null;
                                    if (prepareForDeployment)
                                    {
                                        _filePath = null;
                                        UpdateFilePath(rom.PrepareForDeployment(LuigiGenerationMode.Standard));
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    if (forceUpdate)
                    {
                        _filePath = null;
                    }
                    UpdateFilePath(filePath);
                    if (forceUpdate && !string.IsNullOrEmpty(_filePath) && !System.IO.File.Exists(_filePath))
                    {
                        error = new System.IO.FileNotFoundException("File for fork " + GlobalForkNumber + " not found.", filePath, null);
                    }
                }
            }
            catch (System.IO.IOException e)
            {
                error = e;
                if (!forceUpdate)
                {
                    throw;
                }
            }
            return(error);

#if REPORT_PERFORMANCE
        }

        finally
        {
            stopwatch.Stop();
            System.Diagnostics.Debug.WriteLine(">>>> EXIT  Fork.UpdateFilePath(" + filePath + ",force)");     // took: + " + stopwatch.Elapsed.ToString());
            AccumulatedUpdateFilePathTime += stopwatch.Elapsed;
        }
#endif // REPORT_PERFORMANCE
        }
Пример #19
0
        public void LuigiFileHeader_DeserializeWithCorruptedFeatures_ThrowsInvalidDataException()
        {
            var badCrcHeader = new byte[]
            {
                0x4C, 0x54, 0x4F, 0x01,                                                                         // LTO, version
                0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // features (v1) (corrupted)
                0x81, 0x93, 0x43, 0xB0, 0x0C, 0x47, 0x3F, 0x12,                                                 // UID
                0x00, 0x00, 0x00,                                                                               // reserved
                0x8B                                                                                            // CRC
            };

            using (var stream = new System.IO.MemoryStream(badCrcHeader))
            {
                using (var reader = new BinaryReader(stream))
                {
                    var exception       = Assert.Throws <System.IO.InvalidDataException>(() => LuigiFileHeader.Inflate(reader));
                    var expectedMessage = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.InvalidDataBlockChecksumFormat, 0x61, 0x8B);
                    Assert.Equal(expectedMessage, exception.Message);
                }
            }
        }
Пример #20
0
        public void LuigiFileHeader_SerializeByteCount_IsNegativeOne()
        {
            var header = new LuigiFileHeader();

            Assert.Equal(-1, header.SerializeByteCount);
        }
        private static bool SyncForkData(Fork fork, ForkKind forkKind, Dictionary <ushort, string> forkSourceFileMap, Action <Fork, ForkKind> errorAction, ref ProgramDescription description)
        {
            var succeeded = true;

            if (fork != null)
            {
                var crc = 0u;
                switch (forkKind)
                {
                case ForkKind.Program:
                    var cfgCrc     = 0u;
                    var cacheEntry = CacheIndex.Find(fork.Crc24, fork.Size);
                    if (cacheEntry != null)
                    {
                        crc    = cacheEntry.RomCrc32;
                        cfgCrc = cacheEntry.CfgCrc32;
                    }
                    else
                    {
                        string romPath;
                        if (forkSourceFileMap.TryGetValue(fork.GlobalForkNumber, out romPath) && !string.IsNullOrEmpty(romPath) && System.IO.File.Exists(romPath))
                        {
                            if (LuigiFileHeader.PotentialLuigiFile(romPath))
                            {
                                var luigiHeader = LuigiFileHeader.GetHeader(romPath);
                                if (luigiHeader.Version > 0)
                                {
                                    crc    = luigiHeader.OriginalRomCrc32;
                                    cfgCrc = luigiHeader.OriginalCfgCrc32;
                                }
                            }
                            if (crc == 0u)
                            {
                                crc = Crc32.OfFile(romPath);
                            }
                        }
                        else
                        {
                            System.Console.WriteLine("SDFS");
                        }
                    }
                    if (crc != 0)
                    {
                        var romListDescriptions = INTV.Shared.Model.Program.ProgramCollection.Roms.Where(d => d.Crc == crc);
                        if (romListDescriptions.Any())
                        {
                            var romListDescription = romListDescriptions.FirstOrDefault(d => (d.Rom != null) && (d.Rom.CfgCrc == cfgCrc));
                            if (romListDescription != null)
                            {
                                description = romListDescription.Copy();
                            }
                        }
                    }
                    if (description == null)
                    {
                        var rom = fork.Rom;
                        if (rom == null)
                        {
                            string romPath;
                            succeeded = forkSourceFileMap.TryGetValue(fork.GlobalForkNumber, out romPath);
                            if (succeeded)
                            {
                                rom      = Rom.Create(romPath, null);
                                fork.Rom = rom;
                                if (string.IsNullOrEmpty(fork.FilePath))
                                {
                                    fork.FilePath = romPath;
                                }
                            }
                        }
                        if (rom != null)
                        {
                            var programInfo = rom.GetProgramInformation();
                            description = new ProgramDescription(rom.Crc, rom, programInfo);
                        }
                    }
                    break;

                case ForkKind.Manual:
                    string filePath;
                    if (forkSourceFileMap.TryGetValue(fork.GlobalForkNumber, out filePath) && System.IO.File.Exists(filePath))
                    {
                        fork.FilePath = filePath;
                        if (description != null)
                        {
                            description.Files.DefaultManualTextPath = fork.FilePath;
                        }
                    }
                    break;

                case ForkKind.JlpFlash:
                    // TODO / FIXME : We don't do anything with JLP save data forks when syncing from the file system.
                    break;

                case ForkKind.Vignette:
                case ForkKind.Reserved4:
                case ForkKind.Reserved5:
                case ForkKind.Reserved6:
                    succeeded = false;
                    break;
                }
            }
            if (!succeeded && (errorAction != null))
            {
                errorAction(fork, forkKind);
            }
            return(succeeded);
        }
Пример #22
0
        public void LuigiFileHeader_IsPotentialLuigiFile_ThrowsArgumentNullException()
        {
            LuigiFileHeaderTestStorageAccess.Initialize();

            Assert.Throws <ArgumentNullException>(() => LuigiFileHeader.PotentialLuigiFile(null));
        }
        private static string GetPathForFork(ExecuteDeviceCommandAsyncTaskData data, Fork fork, FileSystem deviceFileSystem, IEnumerable <ProgramDescription> roms, RomListConfiguration romsConfiguration, ref string destinationDir, out bool retrievalNecessary)
        {
            retrievalNecessary = false;
            string forkPath     = null;
            var    forkFileKind = ProgramFileKind.None;
            var    crc          = 0u;
            var    cfgCrc       = 0u;
            var    errors       = data.Result as FileSystemSyncErrors;

            // Determine what kind of fork this is.
            ILfsFileInfo fileContainingFork = null;
            var          forkKind           = deviceFileSystem.GetForkKind(fork, out fileContainingFork);

            switch (forkKind)
            {
            case ForkKind.Program:
                // Try to fetch LUIGI header from the fork.
                forkFileKind = ProgramFileKind.LuigiFile;
                forkPath     = GetRomPathForForkFromRomList(data, fork, roms, romsConfiguration.RomsDirectory, out crc, out cfgCrc);
                if (forkPath == null)
                {
                    forkPath = GetRomPathForForkFromCache(fork, romsConfiguration.RomsDirectory);
                }
                if ((forkPath == null) && string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = romsConfiguration.RomsDirectory;
                }
                break;

            case ForkKind.JlpFlash:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = romsConfiguration.RomsDirectory;     // seems sensible to keep save data file(s) next to the ROM
                }
                forkFileKind = ProgramFileKind.SaveData;
                break;

            case ForkKind.Manual:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = romsConfiguration.ManualsDirectory;
                }
                forkFileKind = ProgramFileKind.ManualText;
                break;

            case ForkKind.Vignette:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = Configuration.Instance.VignetteDataAreaPath;     // keep next to ROM?
                }
                forkFileKind = ProgramFileKind.Vignette;
                break;

            case ForkKind.Reserved4:
            case ForkKind.Reserved5:
            case ForkKind.Reserved6:
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = Configuration.Instance.ReservedDataAreaPath;     // keep next to ROM?
                }
                forkFileKind = ProgramFileKind.GenericSupportFile;
                errors.UnsupportedForks.Add(new Tuple <ILfsFileInfo, Fork>(fileContainingFork, fork));
                ////throw new UnsupportedForkKindException(forkKind);
                break;

            case ForkKind.None:
                // An orphaned fork. Retrieve it, but we can't really do much with it.
                if (string.IsNullOrEmpty(destinationDir))
                {
                    destinationDir = Configuration.Instance.RecoveredDataAreaPath;     // orphaned fork
                }
                forkFileKind = ProgramFileKind.None;
                errors.OrphanedForks.Add(fork);
                break;

            default:
                throw new UnsupportedForkKindException(forkKind);
            }

            if ((destinationDir != null) && (forkPath == null))
            {
                retrievalNecessary = true;
                var forkFileBaseName = (fileContainingFork == null) ? Configuration.Instance.GetForkDataFileName(fork.GlobalForkNumber) : fileContainingFork.LongName.EnsureValidFileName();
                var extension        = forkFileKind.FileExtension();

                // For the menu position fork, use the default extension; since it's in the manual fork slot, we want
                // to override the .txt extension.
                if (string.IsNullOrEmpty(extension) || (fork.Uid == Fork.MenuPositionForkUid))
                {
                    extension = Configuration.ForkExtension;
                }
                var forkFileName = System.IO.Path.ChangeExtension(forkFileBaseName, extension);
                var destFile     = System.IO.Path.Combine(destinationDir, forkFileName);
                if (System.IO.File.Exists(destFile))
                {
                    var existingCrc    = 0u;
                    var existingCfgCrc = 0u;
                    var luigiHeader    = LuigiFileHeader.GetHeader(destFile);
                    if ((luigiHeader != null) && (luigiHeader.Version > 0))
                    {
                        existingCrc    = luigiHeader.OriginalRomCrc32;
                        existingCfgCrc = luigiHeader.OriginalCfgCrc32;
                    }
                    if (existingCrc == 0)
                    {
                        existingCrc = Crc32.OfFile(destFile);
                    }
                    if (existingCfgCrc == 0)
                    {
                        var destCfgFile = System.IO.Path.ChangeExtension(destFile, ProgramFileKind.CfgFile.FileExtension());
                        if (System.IO.File.Exists(destCfgFile))
                        {
                            existingCfgCrc = Crc32.OfFile(destCfgFile);
                        }
                    }

                    // This is the equivalent of RomComparerStrict: We skip retrieval only if both the ROM CRCs match and, if available, the .cfg CRCs match.
                    if ((crc != 0) && (existingCrc == crc) && ((cfgCrc == 0) || (existingCfgCrc == cfgCrc)))
                    {
                        retrievalNecessary = false;
                        forkPath           = destFile;
                    }
                    else
                    {
                        forkPath = destFile.EnsureUniqueFileName();
                    }
                }
                else
                {
                    forkPath = destFile;
                }
            }
            if (!string.IsNullOrEmpty(forkPath) && !System.IO.File.Exists(forkPath))
            {
                retrievalNecessary = true;
                destinationDir     = System.IO.Path.GetDirectoryName(forkPath);
            }

            return(forkPath);
        }
Пример #24
0
        /// <summary>
        /// Gathers cache data to rebuild it if the index file is missing.
        /// </summary>
        /// <param name="cacheIndexDirectory">The director for which to rebuild the index.</param>
        /// <param name="rebuildIndexErrorMessages">Accumulates any errors encountered during the rebuild..</param>
        /// <returns>An enumerable containing index entries from the rebuild process.</returns>
        public static IEnumerable <CacheIndexEntry> GatherCacheData(string cacheIndexDirectory, IList <string> rebuildIndexErrorMessages)
        {
            var jzIntvConfiguration = SingleInstanceApplication.Instance.GetConfiguration <INTV.JzIntv.Model.Configuration>();
            var cachedRoms          = (new[] { cacheIndexDirectory }).IdentifyRomFiles(() => false, f => { }).ToList();
            var cachedLuigiRoms     = cachedRoms.Where(r => r.Format == RomFormat.Luigi).ToList();
            var nonLuigiRoms        = cachedRoms.Except(cachedLuigiRoms).ToList();

            var restoredCacheEntries = new List <CacheIndexEntry>();
            var cacheDirectoryLength = cacheIndexDirectory.Length + 1; // for cutting backslash out later...

            using (var comparer = CanonicalRomComparerStrict.Default)
            {
                foreach (var cachedLuigiRom in cachedLuigiRoms)
                {
                    // Get the LUIGI header.
                    LuigiFileHeader luigiHeader = LuigiFileHeader.GetHeader(cachedLuigiRom.RomPath);

                    // Initialize the LUIGI part of the cache entry.
                    var cacheEntry = new CacheIndexEntry();
                    cacheEntry.LuigiPath  = cachedLuigiRom.RomPath.Substring(cacheDirectoryLength);
                    cacheEntry.LuigiCrc24 = INTV.Core.Utility.Crc24.OfFile(cachedLuigiRom.RomPath);
                    cacheEntry.LuigiSize  = (uint)(new FileInfo(cachedLuigiRom.RomPath)).Length;

                    // Now fetch or recreate the ROM if missing. RomFormat.None indicates that the
                    // original has been located on disk. Otherwise, indicates the format to recreate from the LUIGI file.
                    var originalRomFormat = RomFormat.None;

                    if (luigiHeader.Version > 0)
                    {
                        // LUIGI format 1 and later contains information about the original ROM.
                        cacheEntry.RomCrc32 = luigiHeader.OriginalRomCrc32;
                        var originalRom = nonLuigiRoms.FirstOrDefault(r => (r.Format == luigiHeader.OriginalRomFormat) && r.IsEquivalentTo(cachedLuigiRom, comparer));
                        if (originalRom != null)
                        {
                            cacheEntry.RomPath = originalRom.RomPath.Substring(cacheDirectoryLength);
                            cacheEntry.RomSize = (uint)(new FileInfo(originalRom.RomPath)).Length;
                            restoredCacheEntries.Add(cacheEntry);
                            nonLuigiRoms.RemoveAll(r => (r.Format == luigiHeader.OriginalRomFormat) && r.IsEquivalentTo(cachedLuigiRom, comparer));
                        }
                        else
                        {
                            // Write down the format to reconstruct.
                            originalRomFormat = luigiHeader.OriginalRomFormat;
                        }
                    }
                    else
                    {
                        // Check for a .bin or .rom file.
                        originalRomFormat = RomFormat.Rom;
                        var originalFile = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, originalRomFormat.FileExtension());
                        if (!File.Exists(originalFile))
                        {
                            originalRomFormat = RomFormat.Bin;
                            foreach (var binFormatExtension in ProgramFileKindHelpers.RomFileExtensionsThatUseCfgFiles)
                            {
                                if (!string.IsNullOrEmpty(binFormatExtension))
                                {
                                    originalFile = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, binFormatExtension);
                                }
                                else
                                {
                                    originalFile = System.IO.Path.GetFileNameWithoutExtension(cachedLuigiRom.RomPath);
                                }
                                if (File.Exists(originalFile))
                                {
                                    break;
                                }
                            }
                        }
                        if (File.Exists(originalFile))
                        {
                            cacheEntry.RomCrc32 = INTV.Core.Utility.Crc32.OfFile(originalFile);
                            cacheEntry.RomPath  = originalFile.Substring(cacheDirectoryLength);
                            cacheEntry.RomSize  = (uint)(new FileInfo(originalFile)).Length;

                            if (originalRomFormat == RomFormat.Bin)
                            {
                                var cfgPath = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, ProgramFileKind.CfgFile.FileExtension());
                                if (!File.Exists(cfgPath))
                                {
                                    cacheEntry.RestoreCfgFile();
                                }
                            }

                            restoredCacheEntries.Add(cacheEntry);
                            nonLuigiRoms.RemoveAll(r => (r.Format == originalRomFormat) && r.IsEquivalentTo(cachedLuigiRom, comparer));
                            originalRomFormat = RomFormat.None;
                        }
                    }

                    if (originalRomFormat != RomFormat.None)
                    {
                        // Need to recreate the ROM from LUIGI.
                        var sourcePath       = "\"" + System.IO.Path.GetFileNameWithoutExtension(cachedLuigiRom.RomPath) + "\"";
                        var workingDir       = System.IO.Path.GetDirectoryName(cachedLuigiRom.RomPath);
                        var conversionApps   = jzIntvConfiguration.GetConverterApps(cachedLuigiRom, luigiHeader.OriginalRomFormat);
                        var conversionResult = 0;
                        foreach (var conversionApp in conversionApps)
                        {
                            var argument = luigiHeader.OriginalRomFormat.GetCommandLineArgForBin2Rom() + sourcePath;
                            conversionResult = INTV.Shared.Utility.RunExternalProgram.Call(conversionApp.Item1, argument, workingDir);
                            if (conversionResult != 0)
                            {
                                rebuildIndexErrorMessages.Add("Failed to reconstruct " + sourcePath + luigiHeader.OriginalRomFormat.FileExtension());
                                break;
                            }
                        }
                        if (conversionResult == 0)
                        {
                            cacheEntry.RomPath = System.IO.Path.ChangeExtension(cacheEntry.LuigiPath, luigiHeader.OriginalRomFormat.FileExtension());
                            cacheEntry.RomSize = (uint)(new FileInfo(System.IO.Path.Combine(cacheIndexDirectory, cacheEntry.RomPath))).Length;

                            if (originalRomFormat == RomFormat.Bin)
                            {
                                var cfgPath = System.IO.Path.ChangeExtension(cachedLuigiRom.RomPath, ProgramFileKind.CfgFile.FileExtension());
                                if (!File.Exists(cfgPath))
                                {
                                    cacheEntry.RestoreCfgFile();
                                }
#if false
                                if (File.Exists(cfgPath))
                                {
                                    cacheEntry.CfgCrc32 = INTV.Core.Utility.Crc32.OfFile(cfgPath);
                                    cacheEntry.CfgPath  = cfgPath.Substring(cacheDirectoryLength);
                                    cacheEntry.CfgSize  = (uint)(new FileInfo(cfgPath)).Length;
                                }
#endif // false
                            }

                            restoredCacheEntries.Add(cacheEntry);
                        }
                    }
                }

                foreach (var nonLuigiRom in nonLuigiRoms)
                {
                    var cacheEntry = new CacheIndexEntry();
                    cacheEntry.RomPath  = nonLuigiRom.RomPath.Substring(cacheDirectoryLength);
                    cacheEntry.RomCrc32 = nonLuigiRom.Crc;
                    cacheEntry.RomSize  = (uint)(new FileInfo(nonLuigiRom.RomPath)).Length;

                    if (nonLuigiRom.Format == RomFormat.Bin)
                    {
                        var cfgPath = System.IO.Path.ChangeExtension(nonLuigiRom.RomPath, ProgramFileKind.CfgFile.FileExtension());
                        if (!File.Exists(cfgPath))
                        {
                            cacheEntry.RestoreCfgFile();
#if false
                            var programInfo = ProgramInformationTable.Default.FindProgram(cacheEntry.RomCrc32);
                            var cfgFilePath = nonLuigiRom.GenerateStockCfgFile(programInfo);
                            if (string.Compare(cfgFilePath, cfgPath, true) != 0)
                            {
                                System.Diagnostics.Debug.WriteLine("LSDKFLSKDFJLSJDF");
                            }
#endif // false
                        }
#if false
                        if (File.Exists(cfgPath))
                        {
                            cacheEntry.CfgCrc32 = INTV.Core.Utility.Crc32.OfFile(cfgPath);
                            cacheEntry.CfgPath  = cfgPath.Substring(cacheDirectoryLength);
                            cacheEntry.CfgSize  = (uint)(new FileInfo(cfgPath)).Length;
                        }
#endif // false
                    }

                    var sourcePath    = System.IO.Path.GetFileNameWithoutExtension(nonLuigiRom.RomPath);
                    var workingDir    = System.IO.Path.GetDirectoryName(nonLuigiRom.RomPath);
                    var conversionApp = jzIntvConfiguration.GetConverterApps(nonLuigiRom, RomFormat.Luigi).First();
                    var result        = INTV.Shared.Utility.RunExternalProgram.Call(conversionApp.Item1, "\"" + sourcePath + "\"", workingDir);
                    if (result != 0)
                    {
                        rebuildIndexErrorMessages.Add(string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RestoreCacheIndex_FailedToReconstructFormat, sourcePath + RomFormat.Luigi.FileExtension()));
                    }
                    else
                    {
                        cacheEntry.LuigiPath = System.IO.Path.ChangeExtension(cacheEntry.RomPath, RomFormat.Luigi.FileExtension());
                        var fullLuigiPath = System.IO.Path.Combine(cacheIndexDirectory, cacheEntry.LuigiPath);
                        cacheEntry.LuigiCrc24 = INTV.Core.Utility.Crc24.OfFile(fullLuigiPath);
                        cacheEntry.LuigiSize  = (uint)(new FileInfo(fullLuigiPath)).Length;
                        restoredCacheEntries.Add(cacheEntry);
                    }
                }
            }

            return(restoredCacheEntries);
        }
Пример #25
0
        public void LuigiFileHeader_WouldModifyFeatures_ProducesCorrectResult(LuigiFeatureFlags newFeatures, bool forceFeatureUpdate, bool expectedWouldModify)
        {
            var header = new LuigiFileHeader();

            Assert.Equal(expectedWouldModify, header.WouldModifyFeatures(newFeatures, forceFeatureUpdate));
        }
        private static bool RetrieveForkData(this Device device, ExecuteDeviceCommandAsyncTaskData data, IEnumerable <Fork> forksToRetrieve, string destinationDirectory, IEnumerable <string> fileNames)
        {
            var configuration      = SingleInstanceApplication.Instance.GetConfiguration <Configuration>();
            var succeeded          = false;
            var numForks           = forksToRetrieve.Count();
            var forkFilenames      = fileNames == null ? null : fileNames.ToList();
            var forkBeingRetrieved = 0;

            foreach (var fork in forksToRetrieve)
            {
                if (data.AcceptCancelIfRequested())
                {
                    break;
                }
                ++forkBeingRetrieved;
                if (data != null)
                {
                    data.UpdateTaskProgress((double)forkBeingRetrieved / numForks, string.Format(Resources.Strings.DeviceMultistageCommand_BackupFileSystem_RetrievingFiles_Format, forkBeingRetrieved, numForks));
                }
                var bytesRemaining = (int)fork.Size;
                var offset         = 0;
                succeeded = false;
                using (var memory = new System.IO.MemoryStream())
                {
                    do
                    {
                        const uint Address     = 0u;
                        var        bytesToRead = System.Math.Min(bytesRemaining, Device.TotalRAMSize);
                        succeeded = ReadForkToRam.Create(Address, fork.GlobalForkNumber, (uint)offset, bytesToRead).Execute <bool>(device.Port, data);
                        byte[] dataRead = null;
                        if (succeeded)
                        {
                            dataRead = (byte[])DownloadDataBlockFromRam.Create(Address, bytesToRead).Execute(device.Port, data, out succeeded);
                        }
                        if (succeeded)
                        {
                            memory.Write(dataRead, offset, bytesToRead);
                            bytesRemaining -= bytesToRead;
                            offset         += bytesToRead;
                        }
                    }while (succeeded && (bytesRemaining > 0));

                    if (data != null)
                    {
                        data.UpdateTaskProgress((double)forkBeingRetrieved / numForks, string.Format(Resources.Strings.DeviceMultistageCommand_BackupFileSystem_SavingFiles_Format, forkBeingRetrieved, numForks));
                    }
                    memory.Seek(0, System.IO.SeekOrigin.Begin);
                    using (var tempFile = FileSystemFile.Inflate(memory))
                    {
                        var fileName = (forkFilenames == null) ? configuration.GetForkDataFileName(fork.GlobalForkNumber) : forkFilenames[forkBeingRetrieved - 1];
                        var forkPath = System.IO.Path.Combine(destinationDirectory, fileName);
                        forkPath            = forkPath.EnsureUniqueFileName();
                        data.FailureMessage = forkPath;
                        System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(forkPath));
                        try
                        {
                            // Test to see if this is a Fork containing a LUIGI file.
                            memory.Seek(0, System.IO.SeekOrigin.Begin);
                            LuigiFileHeader.Inflate(memory);
                            forkPath = System.IO.Path.ChangeExtension(forkPath, ProgramFileKind.LuigiFile.FileExtension());
                        }
                        catch (INTV.Core.UnexpectedFileTypeException)
                        {
                            // This is OK... we only want to execute certain functions if the file is a LUIGI file.
                        }
                        if (System.IO.File.Exists(forkPath))
                        {
                            var crcOfTarget = Crc32.OfFile(forkPath);
                            var crcOfSource = Crc32.OfFile(tempFile.FileInfo.FullName);
                            if (crcOfTarget != crcOfSource)
                            {
                                forkPath = forkPath.EnsureUniqueFileName();
                                System.IO.File.Copy(tempFile.FileInfo.FullName, forkPath);
                            }
                        }
                        else
                        {
                            System.IO.File.Copy(tempFile.FileInfo.FullName, forkPath);
                        }
                        fork.FilePath = forkPath;
                    }
                }
                if (!succeeded)
                {
                    break;
                }
            }
            return(succeeded);
        }
Пример #27
0
        /// <summary>
        /// Prepares a ROM for deployment to a Locutus device.
        /// </summary>
        /// <param name="rom">The ROM being prepared.</param>
        /// <param name="updateMode">Specifies the behavior of the LUIGI file generation.</param>
        /// <returns>The fully qualified path of the prepared output data file to deploy to Locutus.</returns>
        public static string PrepareForDeployment(this IRom rom, LuigiGenerationMode updateMode)
        {
#if REPORT_PERFORMANCE
#if RECORD_PREPARE_FOR_DEPLOYMENT_VISITS
            int visits;
            if (PrepareForDeploymentVisits.TryGetValue(rom.RomPath, out visits))
            {
                PrepareForDeploymentVisits[rom.RomPath] = ++visits;
            }
            else
            {
                PrepareForDeploymentVisits[rom.RomPath] = 1;
            }
#endif // RECORD_PREPARE_FOR_DEPLOYMENT_VISITS
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            try
            {
                var stopwatch2 = System.Diagnostics.Stopwatch.StartNew();
#endif // REPORT_PERFORMANCE
            rom.Validate();
#if REPORT_PERFORMANCE
            stopwatch2.Stop();
            _accumulatedPrepareValidateTime += stopwatch2.Elapsed;
#endif // REPORT_PERFORMANCE
            if ((updateMode == LuigiGenerationMode.Passthrough) && (rom.Format == RomFormat.Luigi))
            {
                return(rom.RomPath);
            }
#if REPORT_PERFORMANCE
            stopwatch2.Restart();
            var stopwatch3 = System.Diagnostics.Stopwatch.StartNew();
#endif // REPORT_PERFORMANCE
            var jzIntvConfiguration = SingleInstanceApplication.Instance.GetConfiguration <INTV.JzIntv.Model.Configuration>();
            var converterApps       = jzIntvConfiguration.GetConverterApps(rom, RomFormat.Luigi);
            if (!converterApps.Any())
            {
                converterApps = new[] { new Tuple <string, RomFormat>(JustCopy, RomFormat.Luigi) };
            }
            var converterApp = converterApps.First(); // rom.GetConverterApp(jzIntvConfiguration);
            if ((converterApp.Item1 != JustCopy) && (string.IsNullOrEmpty(converterApp.Item1) || !System.IO.File.Exists(converterApp.Item1)) && (rom.Format != RomFormat.Luigi))
            {
                var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_ConversionToolNotFound_Error_Format, converterApp);
                throw new LuigiFileGenerationException(message, Resources.Strings.RomToLuigiFailed_ConversionToolNotFound_Error_Description);
            }
#if REPORT_PERFORMANCE
            stopwatch3.Stop();
            _accumulatedPrepareConverterAppsTime += stopwatch3.Elapsed;
            stopwatch3.Restart();
#endif // REPORT_PERFORMANCE
            var romStagingArea   = SingleInstanceApplication.Instance.GetConfiguration <Configuration>().RomsStagingAreaPath;
            var stagingAreaPath  = rom.GetStagingAreaPath(romStagingArea);
            var cachedRomPath    = rom.GetCachedRomFilePath(stagingAreaPath);
            var cachedConfigPath = rom.GetCachedConfigFilePath(stagingAreaPath);
            var luigiFile        = rom.GetOutputFilePath(stagingAreaPath, ProgramFileKind.LuigiFile);
#if REPORT_PERFORMANCE
            stopwatch3.Stop();
            _accumulatedPrepareStagingTime += stopwatch3.Elapsed;
            stopwatch3.Restart();
#endif // REPORT_PERFORMANCE

            bool createLuigiFile = true;
            bool changed;
            bool isSourceFileInCache = rom.IsInCache(stagingAreaPath, out changed);
#if REPORT_PERFORMANCE
            stopwatch3.Stop();
            _accumulatedPrepareCacheLookupTime += stopwatch3.Elapsed;
#endif // REPORT_PERFORMANCE

#if REPORT_PERFORMANCE
            stopwatch2.Stop();
            _accumulatedPrepareSetupTime += stopwatch2.Elapsed;

            stopwatch2.Restart();
#endif // REPORT_PERFORMANCE
            var luigiHeader = rom.GetLuigiHeader();
            if (luigiHeader != null)
            {
                // If the given ROM is already a LUIGI file, see if we can determine whether it's already in our cache.
#if REPORT_OLD_LUIGI_FILES
                System.Diagnostics.Debug.Assert(luigiHeader.Version > 0, "Really, you've got some OLD LUIGI files. Delete them.");
#endif // REPORT_OLD_LUIGI_FILES
                var crc24 = INTV.Core.Utility.Crc24.OfFile(rom.RomPath);
                var size  = new System.IO.FileInfo(rom.RomPath).Length;
                var entry = CacheIndex.Find(crc24, (uint)size);
                isSourceFileInCache = entry != null;
                if (isSourceFileInCache)
                {
                    // Cases have been found in which, by moving files around on disk, the staging area path can change.
                    // The result of this, though, is that the *new* path in the cache is different than the extant one
                    // found in the cache. In this case, if the entry's location is different than the newly computed
                    // one, ignore the cache entry and make a new one by acting as if the file is not in the cache.
                    // FIXME This is a lazy fix. A better fix would be to remove the cached files and existing cache
                    // entry and re-add this new one. Or patch up the existing entry. Hell, maybe scrap the entire cache
                    // altogether as it's a bit of a bug farm and creating LUIGI files isn't all *that* expensive.
                    var stagingDirectory   = System.IO.Path.GetFileName(stagingAreaPath);
                    var stagingPathChanged = !entry.LuigiPath.StartsWith(stagingDirectory);
                    if (stagingPathChanged)
                    {
                        isSourceFileInCache = false;
                    }
                    else
                    {
                        luigiFile     = System.IO.Path.Combine(romStagingArea, entry.LuigiPath);
                        cachedRomPath = System.IO.Path.Combine(romStagingArea, entry.RomPath);
                        if (!string.IsNullOrEmpty(entry.CfgPath))
                        {
                            cachedConfigPath = System.IO.Path.Combine(romStagingArea, entry.CfgPath);
                        }
                    }
                }
            }
#if REPORT_PERFORMANCE
            stopwatch2.Stop();
            _accumulatedPrepareLuigiHeaderTime += stopwatch2.Elapsed;

            stopwatch2.Restart();
#endif // REPORT_PERFORMANCE

            if (isSourceFileInCache)
            {
                createLuigiFile = changed || !System.IO.File.Exists(luigiFile);
            }
            if (!isSourceFileInCache || changed)
            {
                cachedRomPath.ClearReadOnlyAttribute();
                cachedConfigPath.ClearReadOnlyAttribute();
                System.IO.File.Copy(rom.RomPath, cachedRomPath, true);
                if (!string.IsNullOrWhiteSpace(cachedConfigPath) && !string.IsNullOrEmpty(rom.ConfigPath) && System.IO.File.Exists(rom.ConfigPath) && (rom.ConfigPath != rom.RomPath))
                {
                    System.IO.File.Copy(rom.ConfigPath, cachedConfigPath, true);
                }
                else if ((string.IsNullOrEmpty(rom.ConfigPath) || !System.IO.File.Exists(rom.ConfigPath)) && System.IO.File.Exists(cachedConfigPath))
                {
                    // The ROM's configuration file path doesn't exist, but there's one in the cache. Remove it.
                    FileUtilities.DeleteFile(cachedConfigPath, false, 2);
                    cachedConfigPath = null; // this is OK, because the ClearReadOnlyAttribute() extension method is null-safe
                }
                cachedRomPath.ClearReadOnlyAttribute();
                cachedConfigPath.ClearReadOnlyAttribute();
            }
#if REPORT_PERFORMANCE
            stopwatch2.Stop();
            _accumulatedPrepareCachedChangedTime += stopwatch2.Elapsed;

            stopwatch2.Restart();
#endif // REPORT_PERFORMANCE

            if (createLuigiFile || ((updateMode == LuigiGenerationMode.FeatureUpdate) || (updateMode == LuigiGenerationMode.Reset)))
            {
                var argument = "\"" + cachedRomPath + "\"" + " \"" + luigiFile + "\"";
                var result   = -1;
                if (JustCopy == converterApp.Item1)
                {
                    System.IO.File.Copy(rom.RomPath, luigiFile, true);
                    result = 0;
                }
                else
                {
                    result = INTV.Shared.Utility.RunExternalProgram.Call(converterApp.Item1, argument, stagingAreaPath);
                }
                if (result == 0)
                {
                    if (!System.IO.File.Exists(luigiFile))
                    {
                        var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_OutputFileNotFound_Error_Format, rom.RomPath, System.IO.Path.GetFileNameWithoutExtension(luigiFile));
                        throw new LuigiFileGenerationException(message, Resources.Strings.RomToLuigiFailed_OutputFileNotFound_Error_Description_Format);
                    }
                    else if ((new System.IO.FileInfo(luigiFile)).Length > Device.TotalRAMSize)
                    {
                        var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_TooLarge_Error_Message_Format, rom.RomPath, luigiFile);
                        throw new LuigiFileGenerationException(message, Resources.Strings.RomToLuigiFailed_TooLarge_Description);
                    }

                    var description = INTV.Shared.Model.Program.ProgramCollection.Roms.FirstOrDefault(d => rom.IsEquivalentTo(d.Rom, RomComparerStrict.Default));
                    if (description != null)
                    {
                        LuigiFeatureFlags features = LuigiFeatureFlags.None;
#if DEBUG
                        var complainAboutOldLuigiFile = false;
#endif // DEBUG
                        luigiHeader = LuigiFileHeader.GetHeader(luigiFile);
                        features    = description.Features.LuigiFeaturesLo;
#if DEBUG
                        var isRecognizedRom     = !description.Features.GeneralFeatures.HasFlag(GeneralFeatures.UnrecognizedRom);
                        var hasFlagsFromCfgFile = luigiHeader.Features.HasFlag(LuigiFeatureFlags.FeatureFlagsExplicitlySet);
                        complainAboutOldLuigiFile = isRecognizedRom && hasFlagsFromCfgFile && ((luigiHeader.Features & ~LuigiFeatureFlags.FeatureFlagsExplicitlySet) != features);
#endif // DEBUG
#if DEBUG
                        if (complainAboutOldLuigiFile)
                        {
                            var message = "Known ROM has explicit flags from utility that are different than those set by LUI:\n\n";
                            message += "  LUI: " + features + "\n";
                            message += "  Utility: " + (luigiHeader.Features & ~LuigiFeatureFlags.FeatureFlagsExplicitlySet);
                            INTV.Shared.View.OSMessageBox.Show(message, "Feature Flags Inconsistency");
                        }
#endif // DEBUG
                        if (luigiHeader.WouldModifyFeatures(features, updateMode == LuigiGenerationMode.FeatureUpdate))
                        {
                            using (var file = System.IO.File.Open(luigiFile, System.IO.FileMode.Open, System.IO.FileAccess.ReadWrite))
                            {
                                luigiHeader.UpdateFeatures(features, updateMode == LuigiGenerationMode.FeatureUpdate);
                                file.Seek(0, System.IO.SeekOrigin.Begin);
                                luigiHeader.Serialize(new Core.Utility.BinaryWriter(file));
                            }
                        }
                    }
                    try
                    {
                        var cacheIndexEntry = new CacheIndexEntry(rom, cachedRomPath);
                        CacheIndex.Instance.AddEntry(cacheIndexEntry);
                    }
                    catch (Exception e)
                    {
                        var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.PrepareForDeployment_ErrorCreatingCacheEntryFormat, rom.RomPath);
                        throw new LuigiFileGenerationException(message, e.Message, e);
                    }
                }
                else
                {
                    var message     = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_InvalidOperation_Error_Message_Format, result);
                    var description = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_Error_Description_Format, converterApp);
                    throw new LuigiFileGenerationException(message, description);
                }
            }
            else
            {
                // If this is a different ROM that produces the same LUIGI, add an entry.
                var crc24 = INTV.Core.Utility.Crc24.OfFile(luigiFile);
                var size  = (uint)(new System.IO.FileInfo(luigiFile)).Length;
                if (CacheIndex.Find(crc24, size) == null)
                {
                    try
                    {
                        var cacheIndexEntry = new CacheIndexEntry(rom, cachedRomPath);
                        CacheIndex.Instance.AddEntry(cacheIndexEntry);
                    }
                    catch (Exception e)
                    {
                        var message = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.PrepareForDeployment_ErrorCreatingCacheEntryFormat, rom.RomPath);
                        throw new LuigiFileGenerationException(message, e.Message, e);
                    }
                }
            }
            ////catch (System.IO.PathTooLongException e)
            ////catch (System.IO.IOException e)
            ////catch (UnauthorizedAccessException e)
            ////catch (InvalidOperationException e)
            ////catch (LuigiFileGenerationException e)
#if REPORT_PERFORMANCE
            stopwatch2.Stop();
            _accumulatedPrepareLuigiUpdateTime += stopwatch2.Elapsed;
#endif // REPORT_PERFORMANCE

            if (string.IsNullOrEmpty(luigiFile) || !System.IO.File.Exists(luigiFile))
            {
                var message     = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_Error_Description_Format, rom);
                var description = string.Format(System.Globalization.CultureInfo.CurrentCulture, Resources.Strings.RomToLuigiFailed_InvalidOutputFileFormat, luigiFile);
                throw new LuigiFileGenerationException(message, description);
            }

#if REPORT_PERFORMANCE
            stopwatch.Stop();
#endif // REPORT_PERFORMANCE
            return(luigiFile);

#if REPORT_PERFORMANCE
        }

        finally
        {
            stopwatch.Stop();
            _accumulatedPrepareTime += stopwatch.Elapsed;
        }
#endif // REPORT_PERFORMANCE
        }