internal BaseSection(NStream stream, long discOffset, byte[] data, long size) { this.Stream = stream; this.DiscOffset = discOffset; this.Data = data; this.Size = size; }
internal long WriteRecoveryPartitionData(Stream crcStream, bool unscrub, WiiPartitionSection ps, int channelNo, out string tempFileName, out string fileName, out NStream output) { if (ps.Header.Type == PartitionType.Update) { fileName = string.Format("{0}_{1}_", BitConverter.ToString(ps.Header.ContentSha1).Replace("-", ""), ps.Header.IsKorean ? "K" : "N"); } else { fileName = string.Format("{0}_{1}_{2}_{3}_", this.NStream.Id8, channelNo.ToString().PadLeft(2, '0'), SourceFiles.CleanseFileName(ps.Header.Id).PadRight(4), ps.Header.IsKorean ? "K" : "N"); } tempFileName = Path.Combine(Settings.OtherRecoveryFilesPath, fileName + "TEMP"); Directory.CreateDirectory(Settings.OtherRecoveryFilesPath); output = new NStream(File.Create(tempFileName)); output.Write(ps.Header.Data, 0, (int)ps.Header.Size); crcStream.Write(ps.Header.Data, 0, (int)ps.Header.Size); long read = ps.Header.Size; foreach (WiiPartitionGroupSection pg in ps.Sections) { if (unscrub) { pg.Unscrub(null); } output.Write(pg.Encrypted, 0, (int)pg.Size); crcStream.Write(pg.Encrypted, 0, (int)pg.Size); read = ps.Size; } return(read); }
private void ensurePosition(NStream stream, long discOffset) { if (stream.Position != discOffset) { stream.Seek(discOffset, SeekOrigin.Begin); //stream will seek forward } }
private bool applyFixes(WiiPartitionHeaderSection partHdr, NStream inStream) { bool changed = false; try { if (inStream.Id == "010E" && partHdr.Id == "RELS") { _hdr.Data[0] = (byte)'4'; _log?.LogDetail("Disc ID swapped from 010E to 410E"); changed = true; } //if (inStream.Id.StartsWith("RSB") && partHdr.Id.StartsWith("HA8")) //Super Smash Brothers Brawl if (inStream.Id.StartsWith("RSB") && partHdr.Type != PartitionType.Update && partHdr.Type != PartitionType.Data) //Super Smash Brothers Brawl { WiiPartitionInfo part = _hdr.Partitions.FirstOrDefault(a => a.DiscOffset == partHdr.DiscOffset); if (part != null && part.Table == 0) { part.Table = 1; //WBM swaps this for some reason //_log?.LogDetail("Fixed SSBB partition table - channel location moved"); _log?.LogDetail(string.Format("Fixed SSBB partition table - channel {0} moved to table 1 from 0", partHdr.Id)); changed = true; } } } catch (Exception ex) { throw new HandledException(ex, "RestoreReaderWii.applyFixes"); } return(changed); }
internal BaseSection(NStream stream, long discOffset, byte[] data, long size) { Stream = stream; DiscOffset = discOffset; Data = data; Size = size; }
internal NDisc(ILog log, NStream nStream, string sourceFileName) { NStream = nStream; SourceFileName = sourceFileName; _settings = new Settings(NStream.IsGameCube ? DiscType.GameCube : DiscType.Wii); this.Log = log; }
public static FileSystem Parse(Stream fstData, long fstOffset, long length, string id, bool isGc) { MemorySection ms = MemorySection.Read(fstData, length); FstFile ff = new FstFile(null) { Name = "fst.bin", DataOffset = fstOffset, Offset = NStream.DataToOffset(fstOffset, !isGc), IsNonFstFile = true, Length = (int)fstData.Length }; return(Parse(ms, ff, id, isGc)); }
public NDisc(Converter cvt, string sourceFileName) { SourceFileName = sourceFileName; NStream = cvt.NStream; NStream.Initialize(true); _settings = new Settings(NStream.IsGameCube ? DiscType.GameCube : DiscType.Wii); Log = cvt; }
internal void CloseStream() { try { if (NStream != null) { NStream.Close(); } } catch { } }
public Converter(SourceFile sourceFile, bool cacheLogsWhileProcessing) { _inProgress = false; _cacheLogsWhileProcessing = cacheLogsWhileProcessing; _logCache = null; _logCacheLock = new Object(); _sourceFile = sourceFile; _nstream = sourceFile.OpenNStream(); _detailLinesOutput = 0; _settings = new Settings(_nstream.IsGameCube ? DiscType.GameCube : DiscType.Wii); }
public NDisc(ILog log, Stream stream) { NStream = (stream is NStream) ? (NStream)stream : new NStream(stream); if (!(stream is NStream)) { NStream.Initialize(true); } _settings = new Settings(NStream.IsGameCube ? DiscType.GameCube : DiscType.Wii); Log = log; }
internal WiiFillerSectionItem(NStream stream, long discOffset, byte[] data, long size, bool useBuff, JunkStream junk) : base(stream, discOffset, data, size) { _useBuff = useBuff; _junk = junk; if (_junk != null) { _junk.Position = discOffset; _junkData = new byte[this.Data.Length]; _junk.Read(_junkData, 0, (int)base.Size); Array.Clear(_junkData, 0, 28); base.Data = _useBuff ? data : _junkData; } }
internal WiiFillerSection(NStream stream, bool updatePartition, long discOffset, long size, long updateSkip, string overrideJunkId, bool generateUpdateFiller, bool generateOtherFiller, bool forceFillerJunk) { _stream = stream; _buff = new byte[0x40000]; //junkstream block size _junkId = overrideJunkId ?? stream.Id; this.DiscOffset = discOffset; this.Size = size; _srcSize = size - updateSkip; _generateUpdateFiller = generateUpdateFiller || size > _srcSize; _generateOtherFiller = generateOtherFiller; _forceFillerJunk = forceFillerJunk; _updatePartiton = updatePartition; }
public Converter(SourceFile sourceFile, bool cacheLogsWhileProcessing) { _inProgress = false; _cacheLogsWhileProcessing = cacheLogsWhileProcessing; _logCache = null; _logCacheLock = new object(); _sourceFile = sourceFile; _nstream = sourceFile.OpenNStream(); _detailLinesOutput = 0; _settings = new Settings(_nstream.IsGameCube ? DiscType.GameCube : DiscType.Wii); //IConfigurationRoot configuration = new ConfigurationBuilder() // .SetBasePath(Directory.GetCurrentDirectory()) // .AddJsonFile("appsettings.json").Build(); }
internal WiiPartitionSection(NStream stream, WiiDiscHeaderSection header, NStream readPartitionStream, long discOffset) { _stream = readPartitionStream; _discHdr = header; _partialFst = 0; _seek = -1; //calc the header byte[] partHdrTmp = new byte[0x400]; //read enough to get all the details we need _stream.Read(partHdrTmp, 0, partHdrTmp.Length); //need to read this to get header length byte[] partHdrLen = new byte[4]; Array.Copy(partHdrTmp, 0x2b8, partHdrLen, 0, 4); //location of partion header length long hdrLen = bigEndian(BitConverter.ToUInt32(partHdrLen, 0)) * 4; byte[] partHdr = new byte[hdrLen]; Array.Copy(partHdrTmp, partHdr, partHdrTmp.Length); _stream.Read(partHdr, partHdrTmp.Length, partHdr.Length - partHdrTmp.Length); Header = new WiiPartitionHeaderSection(_discHdr, readPartitionStream, discOffset, partHdr, partHdr.Length); if (Header.IsRvtH) { throw new Exception("RVT-H image detected - this image type is not currently supported."); } byte[] data = new byte[GroupSize]; //this is an awful work around that blocks are scrubbed but the wiistream can't unscrub them because the partition ID is unknown Dictionary <int, int> IsoDecUnscub = new Dictionary <int, int>(); //must cache this because of a dependency int dataLen = (int)Math.Min(data.Length, Header.PartitionSize); _stream.Read(data, 0, dataLen, (a, b) => IsoDecUnscub.Add(a, b)); //defer the decryption because we don't have the partition id etc WiiPartitionGroupSection ps = new WiiPartitionGroupSection(stream, _discHdr, Header, data, Header.DiscOffset + Header.Data.Length, dataLen, true); Header.Initialise(ps); //defered unscrubbing foreach (KeyValuePair <int, int> x in IsoDecUnscub) { _stream.JunkStream.Position = _stream.OffsetToData(x.Key); _stream.JunkStream.Read(ps.Decrypted, x.Key, x.Value); } _firstSection = ps; }
internal WiiPartitionGroupSection(NStream stream, WiiDiscHeaderSection hdr, WiiPartitionHeaderSection partHdr, byte[] data, long discOffset, long size, bool encrypted) : base(stream, discOffset, new byte[(0x400 * 32) * 64], size) { _isIsoDec = hdr.IsIsoDecPartition(partHdr.DiscOffset); _partHdr = partHdr; _h3Table = _partHdr.H3Table; _idx = 0; _maxLength = base.Data.Length; //0x8000 per block * 64 = 0x200000 _data = new WiiPartitionGroupEncryptionState((int)WiiPartitionSection.GroupSize, this.Key, _h3Table); this.IsEncrypted = encrypted || !data.Equals(0x26c, new byte[20], 0, 20); this.Junk = new byte[WiiPartitionSection.GroupSize]; _unscrubValid = new bool[64]; _data.Populate(data, (int)size, this.IsEncrypted && !_isIsoDec, _isIsoDec, _idx); initialise(); }
private string getPassesLine(NStream nstream, List <Processor> passes) { StringBuilder sb = new StringBuilder(); sb.Append(string.Format("{0} Pass{1}: ", passes.Count.ToString(), passes.Count == 1 ? "" : "es")); sb.AppendFormat("[{0}]", nstream.ExtensionString()); for (int i = 0; i < passes.Count; i++) { sb.Append(" >> ["); if (passes.Count > 1) { sb.AppendFormat("{0}:", (i + 1).ToString()); } sb.Append(passes[i].Title); sb.Append("]"); } return(sb.ToString()); }
private static uint recurseFst(MemorySection ms, FstFolder folder, long names, uint i, string id, bool isGc) { uint j; uint hdr = ms.ReadUInt32B((int)(12 * i)); long name = names + hdr & 0x00ffffffL; int type = (int)(hdr >> 24); string nm = ms.ReadStringToNull((int)name, Encoding.GetEncoding("shift-jis")); uint size = ms.ReadUInt32B((int)(12 * i + 8)); if (type == 1) { FstFolder f = i == 0 ? folder : new FstFolder(folder) { Name = nm }; if (i != 0) { folder.Folders.Add(f); } for (j = i + 1; j < size;) { j = recurseFst(ms, f, names, j, id, isGc); } return(size); } else { int pos = (int)(12 * i + 4); long doff = ms.ReadUInt32B(pos) * (isGc ? 1L : 4L); //offset in data size = ms.ReadUInt32B((int)(12 * i + 8)); long off = NStream.DataToOffset(doff, !isGc); //offset in raw partition folder.Files.Add(new FstFile(folder) { DataOffset = doff, Offset = off, Length = size, Name = nm, PartitionId = id, OffsetInFstFile = pos }); return(i + 1); } }
public NStream OpenNStream(bool readAsDisc) { if (!File.Exists(this.FilePath)) { throw new HandledException("SourceFile.Open - '{0}' does not exist.", (this.FilePath) ?? ""); } Stream fs = null; if (!this.IsArchive) { try { fs = this.OpenStream(); NStream nStream = new NStream(new StreamForward(fs, null)); nStream.Initialize(readAsDisc); return(nStream); } catch (Exception ex) { throw new HandledException(ex, "SourceFile.OpenNStream '{0}'", this.FilePath ?? ""); } } else { string arcType = ""; IArchive archive; if (!this.IsSplit) { try { arcType = ((this.AllFiles.Length == 0) ? "" : "multipart ") + "archive"; archive = ArchiveFactory.Open(this.FilePath); //handles multipart archives } catch (Exception ex) { throw new HandledException(ex, "SourceFile.OpenNStream ({0}) '{1}'", arcType, this.FilePath ?? ""); } } else { try { arcType = "split archive"; fs = this.OpenStream(); archive = ArchiveFactory.Open(fs); //handles multipart archives } catch (Exception ex) { throw new HandledException(ex, "SourceFile.OpenNStream ({0}) '{1}'", arcType, this.FilePath ?? ""); } } IArchiveEntry ent; string key = ""; try { //zip uses / path separator rar uses \ int pathLen = string.IsNullOrEmpty(this.Path) ? 0 : this.Path.Length + 1; ent = archive.Entries.FirstOrDefault(e => e.Key.Length == pathLen + this.Name.Length && e.Key.StartsWith(this.Path) && e.Key.EndsWith(this.Name)); if (ent == null) { throw new Exception("Open archive file entry failure"); } key = ent.Key; } catch (Exception ex) { throw new HandledException(ex, "SourceFile.OpenNStream ({0}) '{1}' failed to open entry '{2}'", arcType, this.FilePath ?? "", this.Name ?? ""); } try { if (ent != null) { NStream nStream = new NStream(new StreamForward((long)ent.Size, ent.OpenEntryStream(), archive)); nStream.Initialize(true); return(nStream); } } catch (Exception ex) { throw new HandledException(ex, "SourceFile.OpenNStream ({0}) '{1}' failed to stream entry '{2}'", arcType, this.FilePath ?? "", key ?? ""); } return(null); } }
public bool Unscrub(List <JunkRedumpPatch> junkPatches) { bool changed = false; //bool performCheck = true; //start on the assumption last was valid as it would be hassle to work out List <FstFile> nulls = new List <FstFile>(); bool good = _data.IsValid(false); //forces decrypt and hash cache build and test - does not test data in blocks matchs H0 table Parallel.For(0, _data.UsedBlocks, bi => _unscrubValid[bi] = _data.BlockIsValid(bi)); //test data matches H0 table for (int bi = 0; bi < _data.UsedBlocks; bi++) { if (!_unscrubValid[bi]) { if (_junk == null) { _junk = new JunkStream(_partHdr.Id, _partHdr.DiscNo, _partHdr.PartitionDataSize); } _junk.Position = NStream.OffsetToData(this.Offset + _data.BlockDataOffset(bi), true); _junk.Read(_data.Decrypted, _data.BlockDataOffset(bi), 0x7c00); _data.MarkBlockUnscrubbedAndDirty(bi); changed = true; } } if (junkPatches != null && junkPatches.Count != 0) { foreach (JunkRedumpPatch jp in junkPatches) { if (jp.Offset >= this.DiscOffset && jp.Offset < this.DiscOffset + this.Size) { Array.Copy(jp.Data, 0, _data.Decrypted, jp.Offset - this.DiscOffset, jp.Data.Length); _data.MarkBlockDirty((int)((jp.Offset - this.DiscOffset) / 0x8000)); //560de532 } } } if (changed) { good = _data.IsValid(true); //true as changes were made if (!good) { bool zerod = false; List <Tuple <long, int, FstFile> > h3Nulls = new List <Tuple <long, int, FstFile> >(_partHdr.ScrubManager.H3Nulls); if (h3Nulls.Count != 0) { int dataLen = (int)NStream.HashedLenToData(this.Decrypted.Length); foreach (Tuple <long, int, FstFile> n in h3Nulls) { if (n.Item1 >= this.DataOffset && n.Item1 < this.DataOffset + dataLen) { int idx = (int)((n.Item1 - this.DataOffset) / 0x7c00); _data.MarkBlockDirty(idx); int pos = (int)NStream.DataToOffset(n.Item1 - this.DataOffset, true); Array.Clear(_data.Decrypted, pos, n.Item2); zerod = true; } } } if (zerod) { good = _data.IsValid(true); } if (!good) { this.H3Errors++; } } } return(changed); }
private long writeDestGap(long nullsPos, Stream dest, long pos, long size, bool firstOrLastFile, List <JunkRedumpPatch> patches, long junkStart, NStream nstream) //gap 0 == 28 null, 1 == junk, -1 == auto detect { long nulls = 0; long maxNulls = Math.Max(0, nullsPos - pos); //0x1cL //maxNulls = 0x1c; if (size < maxNulls) //need to test this commented if { nulls = size; } else { nulls = maxNulls; if (size >= 0x40000 && !firstOrLastFile) { nulls = 0; if (pos % 0x4L != 0) { nulls += 0x4L - pos % 0x4L; //pad to 4 byte boundary } } } if (junkStart != 0 && junkStart > pos) { nulls = Math.Min(junkStart - pos, size); if (junkStart < pos + size) { junkStart = 0; //revert to normal } } if (patches != null) { patches.RemoveAll(a => a.Offset < pos); } long copied = 0; if (nulls != 0 && (patches.Count == 0 || patches[0].Offset != pos)) //if there are strange bytes in the padding then the full padding must be replaced (no cases as yet, never used) { ByteStream.Zeros.Copy(dest, nulls); pos += nulls; copied = nulls; } int c = 0; while (patches.Count != 0 && patches[0].Offset >= pos && patches[0].Offset < (pos - copied) + size) { if (patches.Count != 0 && patches[0].Offset > pos) { nstream.JunkStream.Position = pos; c = (int)(patches[0].Offset - pos); nstream.JunkStream.Copy(dest, c); copied += c; pos += c; } c = (int)Math.Min(size - copied, patches[0].Data.Length); dest.Write(patches[0].Data, 0, c); _log?.LogDetail(string.Format("Junk patched at 0x{0} - {1} byte{2}", patches[0].Offset.ToString("X8"), patches[0].Data.Length.ToString(), patches[0].Data.Length == 1 ? "" : "s")); patches.RemoveAt(0); copied += c; pos += c; nulls = copied; } if (copied < size) { nstream.JunkStream.Position = pos; nstream.JunkStream.Copy(dest, size - nulls); } return(size); }
private long writeFile(ref long nullsPos, Stream dest, long dstPos, ref bool firstFile, Stream srcStream, FstFile lastFile, FstFile file, List <JunkRedumpPatch> patches, NStream nstream, long junkStart, bool isJunkFile) { //Debug.WriteLine(string.Format(@"{0} : {1} : {2} : {3}/{4}", file.DataOffset.ToString("X8"), (file.DataOffset + file.Length).ToString("X8"), /*(nextFile.DataOffset - lastEnd).ToString("X8"),*/ file.Length.ToString("X8"), file.Path, file.Name)); //file found long gap = writeDestGap(nullsPos, dest, dstPos, file.DataOffset - dstPos, firstFile, patches, junkStart, nstream); firstFile = false; bool missing = false; if (file.Length == 0) { // missing = true; } else if (isJunkFile) { missing = true; MemorySection ms = MemorySection.Read(srcStream, 8); long size = ms.ReadUInt32B(0); GapType gt = (GapType)(size & 0b11); size &= 0xFFFFFFFC; //set nullsPos value if zerobyte file without junk if (gt == GapType.JunkFile) { nullsPos = Math.Min(nullsPos - (dstPos + gap), 0); //reset the nulls long nulls = (size & 0xFC) >> 2; long junkFileLen = ms.ReadUInt32B(4); if (junkFileLen != file.Length) { throw new HandledException(string.Format("NKit Junk file restoration length mismatch {0}: {1}", file.DataOffset.ToString("X8"), file.Name)); } ByteStream.Zeros.Copy(dest, nulls); nstream.JunkStream.Position = (dstPos + gap) + nulls; nstream.JunkStream.Copy(dest, junkFileLen - nulls); _log?.LogDetail(string.Format("Generated file content with Junk {0}: {1}", file.DataOffset.ToString("X8"), file.Name)); } else { throw new HandledException(string.Format("NKit Junk file restoration bytes invalid {0}: {1}", file.DataOffset.ToString("X8"), file.Name)); } } else { byte[] f = new byte[Math.Min(0x30, file.Length)]; srcStream.Read(f, 0, f.Length); //then read while junk is created if (lastFile.DataOffset == file.DataOffset && lastFile.Length == 0) //null file overlapped this file so set nullsPos to have a gap (XGIII) needs fst sorting by offset then size { nullsPos = dstPos + 0x1CL; //will already be aligned } int nulls = (int)(nullsPos - (dstPos + gap)); nstream.JunkStream.Position = file.DataOffset; //async junk gen int countNulls = 0; for (int i = 0; i < f.Length && f[i] == 0; i++) { countNulls++; } if (f.Length > nulls && countNulls < f.Length) //don't test all nulls { missing = nstream.JunkStream.Compare(f, 0, f.Length, Math.Max(0, nulls)) == f.Length; } if (missing) { _log?.LogDetail(string.Format("File content is Junk {0}: {1}", file.DataOffset.ToString("X8"), file.Name)); } dest.Write(f, 0, f.Length); srcStream.Copy(dest, file.Length - f.Length); //copy file } if (!missing) //reset the gap when no junk { nullsPos = dstPos + gap + file.Length + 0x1c; if (nullsPos % 4 != 0) { nullsPos += 4 - (nullsPos % 4); } } return(gap + file.Length); }
public void Read(Context ctx, NStream inStream, Stream outStream, Coordinator pc) { string resultMsg = ""; if (!Settings.ConfigFileFound) { _log?.Log("!! No config file found - This is required to restore and validate images"); } NCrc crc = new NCrc(); Settings settings = ctx.Settings; List <string> addedFiles = new List <string>(); DatData data = ctx.Dats; RecoveryData rec = ctx.Recovery; List <JunkRedumpPatch> patches = rec.JunkPatches; MemorySection hdr = null; try { long junkStart = settings.JunkStartOffsets.FirstOrDefault(a => a.Id8 == inStream.Id8)?.Offset ?? 0; string forceJunkId = settings.JunkIdSubstitutions.FirstOrDefault(a => a.Id8 == inStream.Id8)?.JunkId; if (forceJunkId != null) { _log?.LogDetail(string.Format("Using ID {0} for junk not image ID {1}", forceJunkId, inStream.Id)); //result.ImageInfo.JunkId = forceJunkId; inStream.ChangeJunk(forceJunkId); } if (junkStart != 0) { _log?.LogDetail(string.Format("Junk forced to start at 0x{0}", junkStart.ToString("X8"))); } hdr = inStream.DiscHeader; FstFileItem goodFst = rec.GcBinFiles.Where(a => a is FstFileItem).Cast <FstFileItem>().FirstOrDefault(a => a.Id8 == inStream.Id8 && !(inStream.Id8 == "GNHE5d0000" && a.Length == 116)); if (goodFst != null) { _log?.LogDetail(string.Format("Recovery: {0}", goodFst.Filename)); } if (goodFst == null && rec.GcNewBinFiles != null) { goodFst = rec.GcNewBinFiles.Where(a => a is FstFileItem).Cast <FstFileItem>().FirstOrDefault(a => a.Id8 == inStream.Id8); } if (goodFst != null) { goodFst.Populate(); } ApploaderFileItem goodAldr = goodFst == null ? null : (ApploaderFileItem)rec.GcBinFiles.FirstOrDefault(a => a.Crc == goodFst.AppLoadCrc); if (goodAldr == null && rec.GcNewBinFiles != null) { goodAldr = goodFst == null ? null : (ApploaderFileItem)rec.GcNewBinFiles.FirstOrDefault(a => a.Crc == goodFst.AppLoadCrc); } //is it an action replay (custom hacks) if ((hdr.ReadUInt32B(0x420) == 0 && inStream.Id8 == "GNHE5d0000") || inStream.Id8 == "DTLX010000" || inStream.Id8 == "102E010007") { if (inStream.Id8 == "102E010007") { resultMsg = "Aging Disc detected - Skipping recover"; } else { resultMsg = "Datel Action Replay detected - Skipping recover"; } try { using (CryptoStream target = new CryptoStream(outStream, crc, CryptoStreamMode.Write)) { target.Write(hdr.Data, 0, (int)hdr.Size); pc.ReaderCheckPoint1PreWrite(forceJunkId, 0); //size that we will output from this read inStream.Copy(target, pc.OutputSize - hdr.Size); } _log?.LogDetail(resultMsg); } catch (Exception ex) { throw new HandledException(ex, resultMsg); } crc.Snapshot("files"); } else { //############################################################################ //# READ DISC START //########### Header (boot.bin) 0 (read by base stream already) long srcPos = hdr.Size; _log?.LogDetail(string.Format("Header Read: {0} - {1}", friendly(inStream.Id), friendly(inStream.Title))); //########### Header Info (bi2.bin) 0x440 MemorySection bi2bin = MemorySection.Read(inStream, 0x2000); srcPos += bi2bin.Size; //########### APPLOADER (appldr.bin) 0x2440 - Action Reply can have 0 as the main.dol MemorySection appldr = MemorySection.Read(inStream, Math.Min(hdr.ReadUInt32B(0x420) == 0 ? uint.MaxValue : hdr.ReadUInt32B(0x420), hdr.ReadUInt32B(0x424)) - srcPos); srcPos += appldr.Size; //########### APP (main.dol) MemorySection maindol; if (hdr.ReadUInt32B(0x420) < hdr.ReadUInt32B(0x424)) { maindol = MemorySection.Read(inStream, hdr.ReadUInt32B(0x424) - srcPos); } else { maindol = new MemorySection(new byte[0]); } srcPos += maindol.Size; //########### FST (fst.bin) MemorySection srcFst = MemorySection.Read(inStream, hdr.ReadUInt32B(0x428)); srcPos += hdr.ReadUInt32B(0x428); //############################################################################ //# CORRECT ISSUES List <FstFile> srcFiles = FileSystem.Parse(srcFst, null, inStream.Id, true)?.Files?.OrderBy(a => a.Offset)?.ThenBy(a => a.Length)?.ToList(); if (srcFiles == null) { throw new HandledException(string.Format("FST Corrupt or misaligned, could not be parsed at position 0x{0}", hdr.ReadUInt32B(0x424).ToString("X8"))); } uint crcTmp = Crc.Compute(appldr.Data, 0, Math.Min((int)appldr.Size, 0x20 + (int)(appldr.ReadUInt32B(0x14) + appldr.ReadUInt32B(0x18)))); //adjust appldr if (goodAldr != null && crcTmp != goodAldr.Crc) { _log?.LogDetail(string.Format("Replacing appldr.bin crc {0} with Recovery appldr.bin {1}", crcTmp.ToString("X8"), goodAldr.Crc.ToString("X8"))); addedFiles.Add(goodAldr.Filename); appldr = new MemorySection(File.ReadAllBytes(goodAldr.Filename)); } //adjust main.dol if (goodFst != null) { if (goodFst.MainDolOffset != hdr.ReadUInt32B(0x420)) { //main.dol is before fst in image and after in goodfst if (hdr.ReadUInt32B(0x420) < hdr.ReadUInt32B(0x424) && goodFst.MainDolOffset > goodFst.FstOffset) { _log?.LogDetail(string.Format("Skipping main.dol at 0x{0} using 0x{1}", hdr.ReadUInt32B(0x420).ToString("X8"), goodFst.MainDolOffset.ToString("X8"))); maindol = new MemorySection(new byte[0]); } else { _log?.LogDetail(string.Format("Moving main.dol address 0x{0} to 0x{1}", hdr.ReadUInt32B(0x420).ToString("X8"), goodFst.MainDolOffset.ToString("X8"))); } hdr.WriteUInt32B(0x420, (uint)goodFst.MainDolOffset); } if (goodFst.Region != (Region)bi2bin.ReadUInt32B(0x18)) { _log?.LogDetail(string.Format("Region Changed to {0} from {1}", goodFst.Region.ToString(), ((Region)bi2bin.ReadUInt32B(0x18)).ToString())); bi2bin.WriteUInt32B(0x18, (uint)goodFst.Region); } if (goodFst.MaxFst != hdr.ReadUInt32B(0x42C)) { _log?.LogDetail(string.Format("Max Fst Size changed to {0} from {1}", goodFst.MaxFst.ToString("X8"), hdr.ReadUInt32B(0x42C).ToString("X8"))); hdr.WriteUInt32B(0x42C, (uint)goodFst.MaxFst); } string newTitle = Encoding.ASCII.GetString(goodFst.Title); if (newTitle != hdr.ReadString(0x20, 0x60 - 0x20)) { _log?.LogDetail(string.Format("Title changed to '{0}' from '{1}'", newTitle.TrimEnd('\0'), hdr.ReadString(0x20, 0x60 - 0x20).TrimEnd('\0'))); hdr.Write(0x20, goodFst.Title); } } MemorySection fst = srcFst; List <FstFile> fstFiles = srcFiles; crcTmp = Crc.Compute(srcFst.Data); if (goodFst != null && crcTmp != goodFst.Crc) { _log?.LogDetail(string.Format("Replacing fst.bin crc {0} with Recovery fst {1}", crcTmp.ToString("X8"), goodFst.Crc.ToString("X8"))); addedFiles.Add(goodFst.Filename); fst = new MemorySection(goodFst.FstData); fstFiles = FileSystem.Parse(fst, null, inStream.Id, true).Files.OrderBy(a => a.Offset).ThenBy(a => a.Length).ToList(); } //adjust fst.bin if (goodFst != null && (goodFst.FstOffset != hdr.ReadUInt32B(0x424) || goodFst.FstData.Length != hdr.ReadUInt32B(0x428))) { _log?.LogDetail(string.Format("Moving / Resizing fst.bin address 0x{0} (Length {1}) to 0x{2} (Length {3})", hdr.ReadUInt32B(0x424).ToString("X8"), hdr.ReadUInt32B(0x428).ToString(), goodFst.FstOffset.ToString("X8"), goodFst.FstData.Length.ToString())); hdr.WriteUInt32B(0x424, (uint)goodFst.FstOffset); hdr.WriteUInt32B(0x428, (uint)goodFst.FstData.Length); } int c; int failCount = 0; int filesMoved = 0; int nkitJunkFiles = 0; foreach (FstFile f in srcFiles) { FstFile[] fnd = fstFiles.Where(a => a.Name == f.Name && a.Path == f.Path).ToArray(); if (fnd.Length == 0) { failCount++; _log?.LogDetail(string.Format("FST Error: No File Found - {0}/{1} (Length {2})", f.Path, f.Name, f.Length.ToString())); } else if (inStream.IsNkit && (c = fnd.Count(a => a.Length != 0 && f.Length == 0)) == 1) { nkitJunkFiles++; } else if ((c = fnd.Count(a => a.Length == f.Length)) != 1) { failCount++; _log?.LogDetail(string.Format("FST Error: File Size bad - {0}/{1} (Length {2}){3}", f.Path, f.Name, f.Length.ToString(), c <= 1 ? "" : string.Format(" {0} files found", c.ToString()))); } else if (f.DataOffset != fnd[0].DataOffset) { filesMoved++; } if (failCount >= 10) { break; } } if (failCount != 0) { if (failCount >= 10) { throw new HandledException(string.Format("{0} or more FST errors found", failCount.ToString())); } } if (filesMoved != 0) { _log?.LogDetail(string.Format("{0} file{1} of {2} will be repositioned when rebuilding this image", filesMoved.ToString(), filesMoved == 1 ? "" : "s", fstFiles.Count.ToString())); } if (nkitJunkFiles != 0) { _log?.LogDetail(string.Format("{0} file{1} of {2} will be generated from junk when rebuilding this image", nkitJunkFiles.ToString(), nkitJunkFiles == 1 ? "" : "s", fstFiles.Count.ToString())); } if (goodFst != null) { //is the data output so far correct if (!bruteForceValidHeader(ctx, goodFst, hdr, bi2bin, appldr, maindol, fst, pc)) { throw new HandledException(string.Format("Post FST Crc Failed 0x{0}", goodFst.PostFstCrc.ToString("X8"))); } } //############################################################################ //# WRITE DISC START CryptoStream target = new CryptoStream(outStream, crc, CryptoStreamMode.Write); target.Write(hdr.Data, 0, (int)hdr.Size); pc.ReaderCheckPoint1PreWrite(forceJunkId, 0); //size that we will output from this read long dstPos = hdr.Data.Length; crc.Snapshot("boot.bin"); target.Write(bi2bin.Data, 0, (int)bi2bin.Size); dstPos += bi2bin.Size; crc.Snapshot("bi2.bin"); target.Write(appldr.Data, 0, (int)appldr.Size); dstPos += appldr.Size; ByteStream.Zeros.Copy(target, Math.Min(hdr.ReadUInt32B(0x420), hdr.ReadUInt32B(0x424)) - dstPos); dstPos += Math.Min(hdr.ReadUInt32B(0x420), hdr.ReadUInt32B(0x424)) - dstPos; crc.Snapshot("appldr.bin"); target.Write(maindol.Data, 0, (int)maindol.Size); dstPos += maindol.Size; if (goodFst != null) { long padding = goodFst.FstOffset - dstPos; ByteStream.Zeros.Copy(target, padding); dstPos += padding; } crc.Snapshot("main.dol"); target.Write(fst.Data, 0, fst.Data.Length); dstPos += fst.Size; bool firstFile = true; Dictionary <FstFile, byte[]> cache = new Dictionary <FstFile, byte[]>(); crc.Snapshot("fst.bin"); //############################################################################ //# WRITE THE FILESYSTEM int fidx = -1; FstFile lastFile = new FstFile(null) { DataOffset = hdr.ReadUInt32B(0x424), Offset = hdr.ReadUInt32B(0x424), Length = fst.Size }; FstFile nextFile = fstFiles[++fidx]; FstFile cacheFile = null; long nullsPos = dstPos + 0x1c; if (nullsPos % 4 != 0) { nullsPos += 4 - (nullsPos % 4); } //########### FILES foreach (FstFile f in srcFiles) //read the files and write them out as goodFiles (possible order difference) { while ((cacheFile = cache.Keys.FirstOrDefault(a => (a.Length == nextFile.Length || (inStream.IsNkit && a.Length == 0)) && a.Name == nextFile.Name && a.Path == nextFile.Path)) != null) //write cache { bool isJunk = inStream.IsNkit && cacheFile.Length == 0 && cacheFile.Name == nextFile.Name && cacheFile.Path == nextFile.Path; using (MemoryStream cacheStream = new MemoryStream(cache[cacheFile])) { dstPos += writeFile(ref nullsPos, target, dstPos, ref firstFile, cacheStream, lastFile, nextFile, patches, inStream, junkStart, isJunk); } cache.Remove(cacheFile); lastFile = nextFile; nextFile = fidx + 1 < fstFiles.Count ? fstFiles[++fidx] : null; } if (f.DataOffset - srcPos > 0) //skip src junk { inStream.Copy(ByteStream.Zeros, f.DataOffset - srcPos); srcPos += f.DataOffset - srcPos; } if (nextFile != null && (f.Length != nextFile.Length || f.Name != nextFile.Name || f.Path != nextFile.Path)) //cache file (nkit junk files are cached) { bool isNkitJunk = inStream.IsNkit && f.Length == 0 && nextFile.Length != 0 && f.Name == nextFile.Name && f.Path == nextFile.Path; byte[] cacheItem = new byte[isNkitJunk ? 8 : f.Length]; inStream.Read(cacheItem, 0, cacheItem.Length); //read the nkit junk data - details (real length and null counts) cache.Add(f, cacheItem); srcPos += cacheItem.Length; } else //copy file { dstPos += writeFile(ref nullsPos, target, dstPos, ref firstFile, inStream, lastFile, nextFile, patches, inStream, junkStart, false); lastFile = nextFile; nextFile = fidx + 1 < fstFiles.Count ? fstFiles[++fidx] : null; srcPos += f.Length; } } while (nextFile != null && fidx < fstFiles.Count && (cacheFile = cache.Keys.FirstOrDefault(a => a.Length == nextFile.Length && a.Name == nextFile.Name && a.Path == nextFile.Path)) != null) { using (MemoryStream cacheStream = new MemoryStream(cache[cacheFile])) { dstPos += writeFile(ref nullsPos, target, dstPos, ref firstFile, cacheStream, lastFile, nextFile, patches, inStream, junkStart, false); } cache.Remove(cacheFile); lastFile = nextFile; nextFile = fidx + 1 < fstFiles.Count ? fstFiles[++fidx] : null; } writeDestGap(nullsPos, target, dstPos, pc.OutputSize - dstPos, true, patches, junkStart, inStream); crc.Snapshot("files"); } resultMsg = "MatchFail"; uint finalCrc = crc.FullCrc(true); if (ctx.Dats.RedumpData.FirstOrDefault(a => a.Crc == finalCrc) != null) { resultMsg = "Match Redump"; } if (ctx.Dats.CustomData.FirstOrDefault(a => a.Crc == finalCrc) != null) { resultMsg = "Match Custom"; } pc.ReaderCheckPoint2Complete(crc, false, finalCrc, finalCrc, VerifyIsWrite, hdr.Data, resultMsg); pc.ReaderCheckPoint3Complete(); } catch (Exception ex) { throw pc.SetReaderException(ex, "RestoreReaderGc.Read - Read and repair"); //don't let the writer lock } }
public void Read(Context ctx, NStream inStream, Stream outStream, Coordinator pc) { NCrc crc = new NCrc(); try { CryptoStream target = new CryptoStream(outStream, crc, CryptoStreamMode.Write); uint outputCrc = 0; if (inStream.IsNkit || inStream.IsGameCube || !EncryptWiiPartitions) { pc.ReaderCheckPoint1PreWrite(null, 0); //size that we will output from this read if (inStream.HeaderRead) { target.Write(inStream.DiscHeader.Data, 0, (int)inStream.DiscHeader.Size); //write the header inStream.Copy(target, inStream.Length - inStream.DiscHeader.Size); } else { inStream.Copy(target, inStream.Length); } } else { WiiDiscHeaderSection hdr = null; using (NDisc disc = new NDisc(_log, inStream)) { foreach (IWiiDiscSection s in disc.EnumerateSections(inStream.Length)) //ctx.ImageLength { if (s is WiiDiscHeaderSection) { hdr = (WiiDiscHeaderSection)s; target.Write(hdr.Data, 0, hdr.Data.Length); //write the header pc.ReaderCheckPoint1PreWrite(null, 0); //size that we will output from this read //ctx.ImageLength } else if (s is WiiPartitionSection) { WiiPartitionSection ps = (WiiPartitionSection)s; //bool lengthChanged = inStream.CheckLength(ps.DiscOffset, ps.Header.PartitionSize); target.Write(ps.Header.Data, 0, (int)ps.Header.Size); foreach (WiiPartitionGroupSection pg in ps.Sections) { target.Write(pg.Encrypted, 0, (int)pg.Size); } } else if (s is WiiFillerSection) { WiiFillerSection fs = (WiiFillerSection)s; if (fs.Size != 0) { foreach (WiiFillerSectionItem item in ((WiiFillerSection)s).Sections) { target.Write(item.Data, 0, (int)item.Size); } } } } } } crc.Snapshot("iso"); if (inStream.IsNkit) { outputCrc = inStream.DiscHeader.ReadUInt32B(0x208); //assume source nkit crc is correct } else { outputCrc = crc.FullCrc(true); } pc.ReaderCheckPoint2Complete(crc, false, outputCrc, outputCrc, this.VerifyIsWrite, null, null); pc.ReaderCheckPoint3Complete(); } catch (Exception ex) { throw pc.SetReaderException(ex, "IsoReader.Read - Read Image"); //don't let the writer lock } }
public ExtractResult ExtractRecoveryFilesGc() { MemorySection hdr = NStream.DiscHeader; MemorySection bi2bin; List <ExtractRecoveryResult> result = new List <ExtractRecoveryResult>(); NCrc crc = new NCrc(); uint appLdrCrc = 0; MemorySection fst = null; uint fstCrc = 0; string fn; string tmpFullName; Log?.Log(string.Format("Processing: {0}", SourceFileName)); int storeType; using (CryptoStream bw = new CryptoStream(ByteStream.Zeros, crc, CryptoStreamMode.Write)) { bw.Write(hdr.Data, 0, (int)hdr.Size); bi2bin = MemorySection.Read(NStream, 0x2000); bw.Write(bi2bin.Data, 0, (int)bi2bin.Size); MemorySection aplHdr = MemorySection.Read(NStream, 0x20); byte[] appLdr = new byte[0x20 + aplHdr.ReadUInt32B(0x14) + aplHdr.ReadUInt32B(0x18)]; Array.Copy(aplHdr.Data, appLdr, (int)aplHdr.Size); NStream.Read(appLdr, (int)aplHdr.Size, appLdr.Length - (int)aplHdr.Size); appLdrCrc = Crc.Compute(appLdr); fn = string.Format("appldr[{0}][{1}].bin", aplHdr.ReadString(0, 10).Replace("/", ""), appLdrCrc.ToString("X8")); Log?.Log(string.Format(" 1 of 2 - Extracting appldr.bin Recovery File: {0}", fn)); tmpFullName = Path.Combine(Settings.OtherRecoveryFilesPath, fn + "_TEMP") + appLdrCrc.ToString("X8"); if (File.Exists(tmpFullName)) { File.Delete(tmpFullName); } File.WriteAllBytes(tmpFullName, appLdr); if ((storeType = storeRecoveryFile(this.Settings.RedumpAppldrCrcs.Contains(appLdrCrc), tmpFullName, fn, Settings.RecoveryFilesPath, Settings.OtherRecoveryFilesPath, false)) != 0) { result.Add(new ExtractRecoveryResult() { FileName = fn, Extracted = true, Type = PartitionType.Other, IsNew = storeType == 2, IsGameCube = true }); } bw.Write(appLdr, 0, appLdr.Length); //add to fullcrc NStream.Copy(bw, hdr.ReadUInt32B(0x424) - (0x2440 + appLdr.Length)); byte[] fstData = new byte[(int)((4 * 4) + (0x60 - 0x20) + hdr.ReadUInt32B(0x428))]; fst = new MemorySection(fstData); //info: dolAddr, fstAddr, maxfst, region, title, fst NStream.Read(fstData, (4 * 4) + (0x60 - 0x20), (int)hdr.ReadUInt32B(0x428)); fst.WriteUInt32B(0x00, hdr.ReadUInt32B(0x420)); //dol fst.WriteUInt32B(0x04, hdr.ReadUInt32B(0x424)); //fstAddr fst.WriteUInt32B(0x08, hdr.ReadUInt32B(0x42C)); //maxfst fst.WriteUInt32B(0x0c, bi2bin.ReadUInt32B(0x18)); //region fst.Write(0x10, hdr.Read(0x20, 0x60 - 0x20)); //title fstCrc = Crc.Compute(fstData, (4 * 4) + (0x60 - 0x20), (int)hdr.ReadUInt32B(0x428)); //fst bw.Write(fstData, (4 * 4) + (0x60 - 0x20), (int)hdr.ReadUInt32B(0x428)); crc.Snapshot("crc"); } //fst checksums are postfst fn = string.Format("fst[{0}][{1}][{2}][{3}].bin", NStream.Id8, appLdrCrc.ToString("X8"), fstCrc.ToString("X8"), crc.FullCrc().ToString("X8")); Log?.Log(string.Format(" 2 of 2 - Extracting fst.bin Recovery File: {0}", fn)); tmpFullName = Path.Combine(Settings.OtherRecoveryFilesPath, fn + "_TEMP") + crc.FullCrc().ToString("X8"); if (File.Exists(tmpFullName)) { File.Delete(tmpFullName); } File.WriteAllBytes(tmpFullName, fst.Data); if ((storeType = storeRecoveryFile(this.Settings.RedumpFstCrcs.Contains(crc.FullCrc()), tmpFullName, fn, Settings.RecoveryFilesPath, Settings.OtherRecoveryFilesPath, false)) != 0) { result.Add(new ExtractRecoveryResult() { FileName = fn, Extracted = true, Type = PartitionType.Other, IsNew = storeType == 2, IsGameCube = true }); } return(createExtractResult((Region)bi2bin.Read8(0x18), result.ToArray())); }
/// <summary> /// Extracts files from partitions, this is not random access. The iso is read in it's entirety /// </summary> public ExtractResult ExtractRecoveryFilesWii() { List <ExtractRecoveryResult> result = new List <ExtractRecoveryResult>(); List <WiiPartitionInfo> toExtract = new List <WiiPartitionInfo>(); WiiDiscHeaderSection hdr = null; WiiPartitionHeaderSection pHdr = null; NStream target = null; bool extracting = false; int channel = 1; string lastPartitionId = null; PartitionType lastPartitionType = PartitionType.Other; string fileName = null; string tmpFileName = null; int extracted = 0; Crc crc = new Crc(); Stream crcStream = null; long imageSize = this.NStream.RecoverySize; //for Wii: pHdr.PartitionDataLength bool isIso = false; //force to always scrub //Path.GetExtension(_name).ToLower() == ".iso"; foreach (IWiiDiscSection s in this.EnumerateSectionsFix(false, true, false)) { if (s is WiiDiscHeaderSection) { hdr = (WiiDiscHeaderSection)s; Log?.Log(string.Format("Processing: {0}", SourceFileName)); toExtract = hdr.Partitions.Where(a => a.Type != PartitionType.Data && a.Type != PartitionType.GameData).ToList(); if (toExtract.Count() == 0) { Log?.Log(string.Format(" Skipped: No Recovery Partitions to extract - {0}", SourceFileName)); break; } } else if (s is WiiPartitionSection) { WiiPartitionSection ps = (WiiPartitionSection)s; if (!toExtract.Any(a => a.DiscOffset == ps.DiscOffset)) { Log?.Log(string.Format(" Skipping {0} Partition: [{1}]...", ps.Header.Type.ToString(), SourceFiles.CleanseFileName(ps.Header.Id).PadRight(4))); continue; } extracting = true; pHdr = ps.Header; extracting = true; Log?.Log(string.Format(" {0} of {1} - Extracting {2} Recovery Partition: [{3}]", (extracted + 1).ToString(), toExtract.Count().ToString(), ps.Header.Type.ToString(), SourceFiles.CleanseFileName(ps.Header.Id).PadRight(4))); crcStream = new CryptoStream(ByteStream.Zeros, crc, CryptoStreamMode.Write); crc.Initialize(); WriteRecoveryPartitionData(crcStream, isIso, ps, channel, out tmpFileName, out fileName, out target); lastPartitionId = ps.Id; lastPartitionType = ps.Header.Type; } else if (s is WiiFillerSection) { JunkStream junk = new JunkStream(lastPartitionType != PartitionType.Data ? hdr.ReadString(0, 4) : lastPartitionId, hdr.Read8(6), lastPartitionType == PartitionType.Update ? 0 : imageSize); WiiFillerSection fs = (WiiFillerSection)s; if (extracting) { int storeType; if ((storeType = WriteRecoveryPartitionFiller(crcStream, junk, fs.DiscOffset, pHdr.Type == PartitionType.Update, false, fs, target, tmpFileName, ref fileName, crc, false)) != 0) { result.Add(new ExtractRecoveryResult() { FileName = fileName, Extracted = true, Type = PartitionType.Other, IsNew = storeType == 2, IsGameCube = false }); } if (pHdr.Type != PartitionType.Update) { channel++; } extracted++; extracting = false; bool complete = (extracted == toExtract.Count()); if (complete) { break; } } } } return(createExtractResult((Region)hdr.ReadUInt32B(0x4e000), result.ToArray())); }
public void Write(Context ctx, Stream inStream, Stream outStream, Coordinator pc) { try { WiiDiscHeaderSection hdr = null; WiiPartitionHeaderSection pHdr = null; string lastPartitionId = null; PartitionType lastPartitionType = PartitionType.Other; NCrc crc = new NCrc(); Crc updateCrc = new Crc(); bool updateRemoved = false; string updateTmpFileName = null; string updateFileName = null; bool extractingUpdate = false; CryptoStream updateCrcStream = null; NStream updateTarget = null; CryptoStream target = null; MemorySection removedUpdateFiller = null; int preservedHashCount = 0; NkitInfo nkitDiscInfo = new NkitInfo(); long fstFileAlignment = -1; WiiPartitionSection lastPart = null; long dstPos = 0; long imageSize = pc.OutputSize; //for Wii: pHdr.PartitionDataLength string ignoreJunkId; pc.WriterCheckPoint1WriteReady(out ignoreJunkId); //wait until read has written the header and set the length NDisc disc = new NDisc(_log, inStream); foreach (IWiiDiscSection s in disc.EnumerateSections(imageSize)) //no fixing, image should be good //ctx.ImageLength { if (s is WiiDiscHeaderSection) { hdr = (WiiDiscHeaderSection)s; hdr.Write8(0x60, 1); hdr.Write8(0x61, 1); fstFileAlignment = ctx?.Settings?.PreserveFstFileAlignment?.FirstOrDefault(a => a.Item1 == hdr.Id8)?.Item2 ?? -1; target = new CryptoStream(outStream, crc, CryptoStreamMode.Write); target.Write(hdr.Data, 0, hdr.Data.Length); //write the header nkitDiscInfo.BytesData += hdr.Size; dstPos += hdr.Size; crc.Snapshot("Disc Header"); } else if (s is WiiPartitionSection) { WiiPartitionSection ps = (WiiPartitionSection)s; WiiHashStore hashes = new WiiHashStore(ps.PartitionDataLength); ScrubManager scrub = ps.Header.ScrubManager; NkitInfo nkitPartInfo = new NkitInfo(); if (ps.Header.Type == PartitionType.Update && ctx.Settings.NkitUpdatePartitionRemoval && hdr.Partitions.Count() > 1) //only remove update if there's more partitions { updateRemoved = true; extractingUpdate = true; pHdr = ps.Header; extractingUpdate = true; updateCrcStream = new CryptoStream(ByteStream.Zeros, updateCrc, CryptoStreamMode.Write); crc.Initialize(); nkitPartInfo.BytesData += disc.WriteRecoveryPartitionData(updateCrcStream, false, ps, 0, out updateTmpFileName, out updateFileName, out updateTarget); _log?.LogDetail(string.Format("Extracted and Removed {0} Recovery Partition: [{1}]", ps.Header.Type.ToString(), SourceFiles.CleanseFileName(ps.Header.Id).PadRight(4))); removedUpdateFiller = new MemorySection(new byte[0x8000]); removedUpdateFiller.Write(0, hdr.Read(0x40000, 0x100)); //backup the original partition table in case it's non standard in some way } else { target.Write(ps.Header.Data, 0, (int)ps.Header.Size); nkitDiscInfo.BytesData += ps.Header.Size; crc.Snapshot(ps.Id + " Hdr"); crc.Crcs.Last().PatchData = ps.Header.Data; dstPos += ps.Header.Size; ps.NewDiscOffset = dstPos; //long written = 0; using (StreamCircularBuffer decrypted = new StreamCircularBuffer(ps.PartitionDataLength, null, null, output => { //read decrypted partition foreach (WiiPartitionGroupSection pg in ps.Sections) { nkitPartInfo.BytesHashesData += (pg.Size / 0x8000 * 0x400); //size of input hashes if (pg.PreserveHashes()) { nkitPartInfo.BytesHashesPreservation += hashes.Preserve(pg.Offset, pg.Decrypted, pg.Size); if (++preservedHashCount >= 1500) //too many will bomb the patch caching when converting back. Something is wrong { throw pc.SetWriterException(new HandledException("Over 1500 hashes preserved, aborting as image is corrupt or poorly scrubbed.")); } } #if !DECRYPT for (int i = 0; i < pg.Size / 0x8000; i++) { output.Write(pg.Decrypted, (i * 0x8000) + 0x400, 0x7c00); } #else output.Write(pg.Decrypted, 0, (int)pg.Size); #endif } })) { long len = partitionWrite(decrypted, crc, target, ps, ctx, pc, nkitPartInfo, scrub, hashes, fstFileAlignment); ps.NewPartitionDataLength = len; dstPos += len; lastPart = ps; } } NkitFormat.LogNkitInfo(nkitPartInfo, _log, ps.Id, false); lastPartitionId = ps.Id; lastPartitionType = ps.Header.Type; } else if (s is WiiFillerSection) { WiiFillerSection fs = (WiiFillerSection)s; ScrubManager scrub = new ScrubManager(null); JunkStream junk = new JunkStream(lastPartitionType != PartitionType.Data ? hdr.ReadString(0, 4) : lastPartitionId, hdr.Read8(6), lastPartitionType == PartitionType.Update ? 0 : imageSize); if (lastPartitionType == PartitionType.Update && updateRemoved) { //preserve the original partition table and update filename target.Write(removedUpdateFiller.Data, 0, (int)removedUpdateFiller.Size); //remove update partition by adding a 32k placeholder. Avoid having a non update partition at 0x50000 nkitDiscInfo.BytesPreservationDiscPadding += removedUpdateFiller.Size; nkitDiscInfo.BytesGaps += fs.Size; dstPos += removedUpdateFiller.Size; crc.Snapshot(string.Format("{0}Replacement Filler", string.IsNullOrEmpty(lastPartitionId) ? "" : (" " + SourceFiles.CleanseFileName(lastPartitionId).PadRight(4)))); if (extractingUpdate) { int storeType; if ((storeType = disc.WriteRecoveryPartitionFiller(updateCrcStream, junk, fs.DiscOffset, true, true, fs, updateTarget, updateTmpFileName, ref updateFileName, updateCrc, true)) != 0) { _log.LogDetail(string.Format("{0}Update recovery partition stored: {1}", storeType == 2 ? "Other " : "", updateFileName)); } extractingUpdate = false; } } else { if (fs.Size != 0) { using (StreamCircularBuffer filler = new StreamCircularBuffer(fs.Size, null, null, output => { foreach (WiiFillerSectionItem item in ((WiiFillerSection)s).Sections) { output.Write(item.Data, 0, (int)item.Size); } })) { Gap gap = new Gap(fs.Size, false); long srcPos = fs.DiscOffset; long gapLen = gap.Encode(filler, ref srcPos, lastPartitionType == PartitionType.Update ? fs.Size : 0x1cL, fs.Size, junk, scrub, target, _log); nkitDiscInfo.BytesPreservationData += gapLen; nkitDiscInfo.BytesGaps += fs.Size; dstPos += gapLen; if (lastPart != null) { lastPart.NewPartitionDataLength += gapLen; } } } //pad partition to 32k int gapLen2 = (int)(dstPos % 0x8000 == 0 ? 0 : 0x8000 - (dstPos % 0x8000)); ByteStream.Zeros.Copy(target, gapLen2); nkitDiscInfo.BytesPreservationDiscPadding += gapLen2; dstPos += gapLen2; if (lastPart != null) { lastPart.NewPartitionDataLength += gapLen2; } if (lastPart != null) { lastPart.Header.WriteUInt32B(0x2bc, (uint)(lastPart.NewPartitionDataLength / 4)); //updates the array in the crc data hdr.Partitions.First(a => a.DiscOffset == lastPart.DiscOffset).DiscOffset = (lastPart.NewDiscOffset - 0x20000); } crc.Snapshot(string.Format("{0}{1}Files+Filler", lastPartitionId ?? "", string.IsNullOrEmpty(lastPartitionId) ? "" : " ")); } } } NkitFormat.LogNkitInfo(nkitDiscInfo, _log, hdr.Id, true); foreach (CrcItem ci in crc.Crcs.Where(a => a.PatchData != null)) { ci.PatchCrc = Crc.Compute(ci.PatchData); } NCrc readerCrcs; uint validationCrc; pc.WriterCheckPoint2Complete(out readerCrcs, out validationCrc, hdr.Data, dstPos); //wait until reader has completed and get crc patches. if (updateRemoved && crc.Crcs.Length > 2) //freeloader wii only has update partition { hdr.RemoveUpdatePartition(crc.Crcs[2].Offset); } hdr.UpdateOffsets(); hdr.WriteString(0x200, 8, "NKIT v01"); //header and version hdr.Write8(0x60, 1); hdr.Write8(0x61, 1); hdr.WriteUInt32B(0x208, readerCrcs.FullCrc(true)); //original crc hdr.WriteUInt32B(0x210, (uint)(imageSize / 4L)); //ctx.ImageLength hdr.WriteUInt32B(0x218, updateCrc.Value); //Update crc - Only if removed crc.Crcs[0].PatchCrc = Crc.Compute(hdr.Data); crc.Crcs[0].PatchData = hdr.Data; hdr.WriteUInt32B(0x20C, CrcForce.Calculate(crc.FullCrc(true), dstPos, readerCrcs.FullCrc(true), 0x20C, 0)); //magic to force crc crc.Crcs[0].PatchCrc = Crc.Compute(hdr.Data); //update with magic applied pc.WriterCheckPoint3ApplyPatches(crc, false, crc.FullCrc(true), crc.FullCrc(true), this.VerifyIsWrite, "NKit Written"); } catch (Exception ex) { throw pc.SetWriterException(ex, "NkitWriterWii.Write - Convert"); } }
public virtual OutputResults Process(Context ctx, NStream input, Stream output) { OutputResults results = new OutputResults() { Conversion = ctx.ConversionName, VerifyOutputResult = VerifyResult.Unverified, ValidateReadResult = VerifyResult.Unverified }; try { StreamCircularBuffer fs = null; _timer = new Timer { Interval = 250 }; _timer.Elapsed += (s, e) => { if (_timerRunning) { return; //keep processing } try { _timerRunning = true; _log.ProcessingProgress(((IProgress)fs)?.Value ?? 0); } catch { } finally { _timerRunning = false; } }; _timer.Enabled = true; long size; switch (_sizeMode) { case ProcessorSizeMode.Source: size = input.SourceSize; break; case ProcessorSizeMode.Stream: size = input.Length; break; case ProcessorSizeMode.Image: size = input.ImageSize; break; case ProcessorSizeMode.Recover: default: size = input.RecoverySize; break; } Coordinator pc = new Coordinator(ctx.ValidationCrc, Reader, Writer, size); pc.Started += (s, e) => { _timer.Enabled = true; results.AliasJunkId = e.AliasJunkId; }; pc.Completed += (s, e) => { _timer.Enabled = false; if (Writer is HashWriter) { results.OutputMd5 = e.Md5; results.OutputSha1 = e.Sha1; } else { MemorySection hdr = new MemorySection(e.Header ?? input.DiscHeader.Data); results.OutputTitle = hdr.ReadStringToNull(0x20, 0x60); results.OutputDiscNo = hdr.Read8(6); results.OutputDiscVersion = hdr.Read8(7); results.OutputId8 = string.Concat(hdr.ReadString(0, 6), results.OutputDiscNo.ToString("X2"), results.OutputDiscVersion.ToString("X2")); results.ProcessorMessage = e.ResultMessage; results.OutputCrc = e.PatchedCrc; results.IsRecoverable = e.IsRecoverable; if (e.ValidationCrc != 0) { results.ValidationCrc = e.ValidationCrc; results.ValidateReadResult = e.ValidationCrc == e.PatchedCrc ? VerifyResult.VerifySuccess : VerifyResult.VerifyFailed; } if (!(Writer is VerifyWriter)) { results.OutputSize = e.OutputSize; //never store the verify size } else { results.VerifyCrc = e.VerifyCrc; if (e.VerifyIsWrite) //e.ValidationCrc can be set from a previous process run { results.VerifyOutputResult = results.ValidationCrc == results.VerifyCrc ? VerifyResult.VerifySuccess : VerifyResult.VerifyFailed; } } } bool l9 = pc.Patches.Crcs.Any(a => a.Offset > 0xFFFFFFFFL || a.Length > 0xFFFFFFFFL); if (pc.ReaderCrcs != null) { foreach (CrcItem c in pc.ReaderCrcs.Crcs) { _log.LogDebug(string.Format("R-CRC {0} Before:{1} After:{2} L:{3} {4}", c.Offset.ToString(l9 ? "X9" : "X8"), c.Value.ToString("X8"), c.PatchCrc == 0 ? " " : c.PatchCrc.ToString("X8"), c.Length.ToString(l9 ? "X9" : "X8"), SourceFiles.CleanseFileName(c.Name))); } _log.LogDebug(string.Format("ReadCRC {0}Before:{1} After:{2}", l9 ? " " : "", pc.ReaderCrcs.FullCrc(false).ToString("X8"), pc.ReaderCrcs.FullCrc(true).ToString("X8"))); } if (pc.WriterCrcs != null) { foreach (CrcItem c in pc.WriterCrcs.Crcs) { _log.LogDebug(string.Format("W-CRC {0} Before:{1} After:{2} L:{3} {4}", c.Offset.ToString(l9 ? "X9" : "X8"), c.Value.ToString("X8"), c.PatchCrc == 0 ? " " : c.PatchCrc.ToString("X8"), c.Length.ToString(l9 ? "X9" : "X8"), SourceFiles.CleanseFileName(c.Name))); } _log.LogDebug(string.Format("WriteCRC {0}Before:{1} After:{2}", l9 ? " " : "", pc.WriterCrcs.FullCrc(false).ToString("X8"), pc.WriterCrcs.FullCrc(true).ToString("X8"))); } _log.ProcessingComplete(results.OutputSize, results.ProcessorMessage, true); }; try { _log.ProcessingStart(input.SourceSize, Title); using (fs = new StreamCircularBuffer(size, input, null, s => Reader.Read(ctx, input, s, pc))) //read in stream and write to circular buffer { Writer.Write(ctx, fs, output, pc); } } catch { if (pc.Exception != null) { throw pc.Exception; } if (fs.WriterException != null) { throw fs.WriterException; } throw; //writer exception } foreach (CrcItem crc in pc.Patches.Crcs.Where(a => a.PatchData != null || a.PatchFile != null)) { output.Seek(crc.Offset, SeekOrigin.Begin); if (crc.PatchFile == null) { output.Write(crc.PatchData, 0, (int)Math.Min(crc.Length, crc.PatchData.Length)); //PatchData might be larger } else { using (FileStream pf = File.OpenRead(crc.PatchFile)) { pf.Copy(output, pf.Length); ByteStream.Zeros.Copy(output, crc.Length - pf.Length); } } } } catch (Exception ex) { if (_timer != null) { _timer.Enabled = false; } try { _log.ProcessingComplete(results.OutputSize, results.ProcessorMessage, false); // force any log lines to be output - handy for diagnosis } catch { } throw new HandledException(ex, "Failed processing {0} -> {1}", Reader?.GetType()?.Name ?? "<null>", Writer?.GetType()?.Name ?? "<null>"); } finally { if (_timer != null) { _timer.Enabled = false; _timer = null; } } return(results); }
internal int WriteRecoveryPartitionFiller(Stream crcStream, JunkStream junk, long pos, bool isUpdate, bool isNkit, WiiFillerSection fs, NStream target, string tmpFileName, ref string fileName, Crc crc, bool logAsDetail) { long nullBlocks = 0; //check for random junk - only on a handful of launch releases (Rampage, Ant Bully, Grim Adventures, Happy Feet etc) junk.Position = pos; long leadingNullsPos = pos; foreach (WiiFillerSectionItem fi in fs.Sections) { crcStream.Write(fi.Data, 0, (int)fi.Size); for (int i = 0; i < fi.Size; i += 0x8000) { int len = (int)Math.Min(0x8000L, fi.Size - (long)i); bool match = junk.Compare(fi.Data, i, len, junk.Position == leadingNullsPos ? 0x1c : 0) == len; if (match) { nullBlocks++; } else { if (nullBlocks != 0) { if (pos == leadingNullsPos) { ByteStream.Zeros.Copy(target, 0x1cL); junk.Position = pos + 0x1cL; junk.Copy(target, (nullBlocks * 0x8000L) - 0x1cL); } else { junk.Position = pos; junk.Copy(target, nullBlocks * 0x8000L); } pos += nullBlocks * 0x8000L; } nullBlocks = 0; target.Write(fi.Data, i, len); } } } target.Close(); crcStream.Close(); string fName = (fileName += crc.Value.ToString("X8")); bool redump; if (isUpdate) { redump = this.Settings.RedumpUpdateCrcs.Contains(crc.Value); } else { redump = this.Settings.RedumpChannels.FirstOrDefault(a => fName.StartsWith(a.Item1)) != null; } if (isNkit) { Directory.CreateDirectory(Settings.NkitRecoveryFilesPath); } return(storeRecoveryFile(redump, tmpFileName, fileName, Settings.RecoveryFilesPath, isNkit ? Settings.NkitRecoveryFilesPath : Settings.OtherRecoveryFilesPath, logAsDetail)); //rename the file, delete if dupe }
private OutputResults process(string conversion, SourceFile sourceFile, bool renameWithMasks, bool toNkit, NkitFormatType nkitFormat, bool isRecovery, bool fullVerify, bool calcHashes, bool testMode, Func <NStream, IEnumerable <Processor> > passes) { OutputResults results = null; NStream nstream = _nstream; string lastTmp = null; string tmp = null; this.ConvertionName = conversion; try { SourceFile sf = null; long sourceSize = nstream.SourceSize; _context = new Context(); _context.Initialise(this.ConvertionName, sourceFile, _settings, true, isRecovery, nstream.IsGameCube, nstream.Id8, this); List <Processor> processors = passes(nstream).Where(a => a != null).ToList(); if (nkitFormat == NkitFormatType.Gcz) { processors.Add(new Processor(new IsoReader(), new GczWriter(), "To GCZ", this, false, true, ProcessorSizeMode.Stream)); } if (calcHashes) { processors.Add(new Processor(new IsoReader(), new HashWriter(), "Calc Hashes", this, false, true, ProcessorSizeMode.Stream)); } if (fullVerify) { if (!toNkit) { processors.Add(new Processor(new IsoReader(), new VerifyWriter(), "Full Verify", this, false, true, ProcessorSizeMode.Stream)); } else { processors.Add(new Processor(nstream.IsGameCube ? new NkitReaderGc() : (IReader) new NkitReaderWii(), new VerifyWriter(), "NKit Verify", this, true, true, ProcessorSizeMode.Image)); } processors.Last().Writer.RequireVerifyCrc = true; processors.Last().Writer.VerifyIsWrite = true; //read verify } _totalPasses = processors.Count(); if (processors.Count == 0) { return(null); } DateTime dt = DateTime.Now; _completedPasses = 0; Log("PROCESSING" + (testMode ? " [TEST MODE]" : ((_context.Settings.DeleteSource ? " [DELETE SOURCE]" : "")))); Log("-------------------------------------------------------------------------------"); if (_forcedWiiFullNkitRebuild) { LogBlank(); Log(string.Format("Nkit Reencode forced: NkitUpdatePartitionRemoval is {0} and source image has {1} Update Partition", _context.Settings.NkitUpdatePartitionRemoval.ToString(), nstream.IsNkitUpdateRemoved ? "no" : "an")); LogBlank(); } Log(string.Format("{0} [{1}] {2} [MiB:{3,2:#.0}]", friendly(nstream.Title), friendly(nstream.Id), nstream.IsGameCube ? "GameCube" : "Wii", (sourceSize / (double)(1024 * 1024)))); LogBlank(); string passesText = getPassesLine(nstream, processors); Log(passesText); int i = 1; foreach (Processor pro in processors.Where(pro => pro != null)) { LogDebug(string.Format("Pass {0}: {1}", (i++).ToString(), pro.ToString())); } LogBlank(); foreach (Processor pro in processors) { //sort out temp file and open input as nstream each time //raise progress and output messages from processors if (sf != null) { nstream = sf.OpenNStream(!(pro.Writer is HashWriter)); //if hashWriter then read as raw file sf = null; } tmp = null; FileStream tmpFs = null; try { if (pro.HasWriteStream) { tmp = Path.Combine(_context.Settings.TempPath, Path.GetFileName(Path.GetTempFileName())); if (!Directory.Exists(_context.Settings.TempPath)) { Directory.CreateDirectory(_context.Settings.TempPath); } tmpFs = File.Create(tmp, 0x400 * 0x400 * 4, FileOptions.SequentialScan); } _logCache = new List <Tuple <string, LogMessageType> >(); OutputResults nr = pro.Process(_context, nstream, tmpFs ?? Stream.Null); _logCache = null; if (results == null) { results = nr; results.DiscType = nstream.IsGameCube ? DiscType.GameCube : DiscType.Wii; results.InputFileName = sourceFile.AllFiles.Length != 0 ? sourceFile.AllFiles[0] : sourceFile.FilePath; results.InputDiscNo = nstream.DiscNo; results.InputDiscVersion = nstream.Version; results.InputTitle = nstream.Title; results.InputId8 = nstream.Id8; results.InputSize = sourceSize; results.FullSize = nstream.ImageSize; results.Passes = passesText; if (pro.IsVerify) { results.OutputSize = results.InputSize; } } else if (pro.Writer is HashWriter) //hash writer gives no meaningful info back other than md5 and sha1 (the crc is forced when nkit, so ignore) { results.OutputMd5 = nr.OutputMd5; results.OutputSha1 = nr.OutputSha1; } else { if (nr.AliasJunkId != null) { results.AliasJunkId = nr.AliasJunkId; } if (nr.OutputTitle != null) { results.OutputDiscNo = nr.OutputDiscNo; results.OutputDiscVersion = nr.OutputDiscVersion; results.OutputTitle = nr.OutputTitle; } results.OutputId8 = nr.OutputId8; results.OutputCrc = nr.OutputCrc; results.OutputPrePatchCrc = nr.OutputPrePatchCrc; results.FullSize = nstream.ImageSize; if (tmp != null) { results.OutputSize = nr.OutputSize; } if (nr.ValidationCrc != 0 && results.VerifyCrc != 0) { results.VerifyCrc = 0; //blank verify if a new ValidationCrc is set - verification happened when both != 0 } if (nr.VerifyCrc != 0) { results.VerifyCrc = nr.VerifyCrc; } if (nr.ValidationCrc != 0) { results.ValidationCrc = nr.ValidationCrc; } if (nr.ValidateReadResult != VerifyResult.Unverified) { results.ValidateReadResult = nr.ValidateReadResult; } if (nr.VerifyOutputResult != VerifyResult.Unverified) { if (results.ValidateReadResult == VerifyResult.Unverified && nstream.IsNkit) { results.ValidateReadResult = nr.VerifyOutputResult; } results.VerifyOutputResult = nr.VerifyOutputResult; } if (nr.IsRecoverable) { results.IsRecoverable = nr.IsRecoverable; } } } finally { if (tmpFs != null) { tmpFs.Dispose(); } nstream.Close(); } if (lastTmp != null && tmp != null) { File.Delete(lastTmp); } //handle failures well if (results.ValidateReadResult == VerifyResult.VerifyFailed || results.VerifyOutputResult == VerifyResult.VerifyFailed) { lastTmp = tmp; //keep post loop happy break; } if (_completedPasses != _totalPasses) //last item { sf = SourceFiles.OpenFile(tmp ?? lastTmp); } if (tmp != null) { lastTmp = tmp; } } TimeSpan ts = DateTime.Now - dt; results.ProcessingTime = ts; //FAIL if (results.ValidateReadResult == VerifyResult.VerifyFailed || results.VerifyOutputResult == VerifyResult.VerifyFailed) { LogBlank(); Log(string.Format("Verification Failed Crc:{0} - Failed Test Crc:{1}", results.OutputCrc.ToString("X8"), results.ValidationCrc.ToString("X8"))); if (lastTmp != null) //only null when verify only { Log("Deleting Output" + (Settings.OutputLevel != 3 ? "" : " (Skipped as OutputLevel is 3:Debug)")); results.OutputFileName = null; if (Settings.OutputLevel != 3) { File.Delete(lastTmp); } LogBlank(); } } else //SUCCESS { LogBlank(); Log(string.Format("Completed ~ {0}m {1}s [MiB:{2:#.0}]", ((int)ts.TotalMinutes).ToString(), ts.Seconds.ToString(), (results.OutputSize / (double)(1024 * 1024)))); LogBlank(); Log("RESULTS"); Log("-------------------------------------------------------------------------------"); uint finalCrc = results.ValidationCrc != 0 ? results.ValidationCrc : results.OutputCrc; string mask = _context.Settings.MatchFailRenameToMask; results.OutputFileExt = "." + SourceFiles.ExtensionString(false, false, toNkit, nkitFormat == NkitFormatType.Gcz).ToLower(); results.RedumpInfo = _context.Dats.GetRedumpEntry(_context.Settings, _context.Dats, finalCrc); if (results.RedumpInfo.MatchType == MatchType.Redump || results.RedumpInfo.MatchType == MatchType.Custom) { Log(string.Format("{0} [{1} Name]", results.RedumpInfo.MatchName, results.RedumpInfo.MatchType.ToString())); if (results.IsRecoverable) { Log(string.Format("Missing Recovery data is required to correctly restore this image!", results.RedumpInfo.MatchName, results.RedumpInfo.MatchType.ToString())); } mask = results.RedumpInfo.MatchType == MatchType.Custom ? _context.Settings.CustomMatchRenameToMask : _context.Settings.RedumpMatchRenameToMask; } else { Log(string.Format("CRC {0} not in Redump or Custom Dat", finalCrc.ToString("X8"))); } LogBlank(); outputResults(results); if (lastTmp != null) //only null when verify only { if (testMode) { Log("TestMode: Deleting Output"); results.OutputFileName = null; if (File.Exists(lastTmp)) { File.Delete(lastTmp); } } else if (isRecovery && _context.Settings.RecoveryMatchFailDelete && results.RedumpInfo.MatchType == MatchType.MatchFail) { Log("Failed to Recover to Dat Entry: Deleting Output"); results.OutputFileName = null; File.Delete(lastTmp); } else { if (renameWithMasks) { results.OutputFileName = _context.Dats.GetFilename(results, mask); Log("Renaming Output Using Masks"); } else { results.OutputFileName = SourceFiles.GetUniqueName(sourceFile.CreateOutputFilename(results.OutputFileExt)); Log("Renaming Output Based on Source File" + (sourceFile.AllFiles.Count() > 1 ? "s" : "")); } LogBlank(); string path = Path.GetDirectoryName(results.OutputFileName); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } File.Move(lastTmp, results.OutputFileName); Log(string.Format("Output: {0}", Path.GetDirectoryName(results.OutputFileName))); Log(string.Format(" {0}", Path.GetFileName(results.OutputFileName))); //double check test mode just to be sure if (_context.Settings.DeleteSource && !testMode && results.VerifyOutputResult == VerifyResult.VerifySuccess) { LogBlank(); Log("Deleting Source:"); foreach (string s in sourceFile.AllFiles.Length == 0 ? new string[] { sourceFile.FilePath } : sourceFile.AllFiles) { Log(string.Format(" {0}", s)); File.Delete(s); } } } LogBlank(); } } } catch (Exception ex) { try { if (lastTmp == null) { lastTmp = tmp; } if (lastTmp != null) { LogBlank(); Log("Deleting Output" + (Settings.OutputLevel != 3 ? "" : " (Skipped as OutputLevel is 3:Debug)")); if (results != null) { results.OutputFileName = null; } if (Settings.OutputLevel != 3) { File.Delete(lastTmp); } } } catch { } if (_context.Settings.EnableSummaryLog) { if (results == null) { results = new OutputResults(); results.Conversion = this.ConvertionName; results.DiscType = (nstream?.IsGameCube ?? true) ? DiscType.GameCube : DiscType.Wii; results.InputFileName = (sourceFile?.AllFiles?.Length ?? 0) == 0 ? (sourceFile?.FilePath ?? "") : (sourceFile?.AllFiles.FirstOrDefault() ?? ""); results.InputDiscNo = nstream?.DiscNo ?? 0; results.InputDiscVersion = nstream?.Version ?? 0; results.InputTitle = nstream?.Title ?? ""; results.InputId8 = nstream?.Id8 ?? ""; results.InputSize = sourceFile?.Length ?? 0; } results.VerifyOutputResult = VerifyResult.Error; HandledException hex = ex as HandledException; if (hex == null) { hex = new HandledException(ex, "Unhandled Exception"); } results.ErrorMessage = hex.FriendlyErrorMessage; } throw; } finally { if (_context.Settings.EnableSummaryLog) { summaryLog(_context.Settings, results); Log("Summary Log Written" + (results.VerifyOutputResult != VerifyResult.Error ? "" : " as Errored!")); LogBlank(); } } return(results); }