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); }
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 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"); } }
private long partitionWrite(Stream inStream, NCrc crc, Stream target, WiiPartitionSection pHdr, Context ctx, Coordinator pc, NkitInfo imageInfo, ScrubManager scrub, WiiHashStore hashes, long fstFileAlignment) { #if DEHASH return(pHdr.NewPartitionDataLength = inStream.Copy(target, pHdr.PartitionDataLength, null)); #elif DECRYPT inStream.Copy(target, pHdr.PartitionLength, null); return(pHdr.NewPartitionDataLength = pHdr.PartitionDataLength); #endif MemorySection hdr = MemorySection.Read(inStream, 0x440); //ProgressResult result = ctx.Result; long mlt = 4L; //GC 1L long logicalSize = pHdr.PartitionDataLength; //GC: ctx.ImageLength; long physicalSize = pHdr.PartitionDataLength; //GC: result.ImageInfo.IsoSize; string junkId = hdr.ReadString(0, 4); //bool write = true; List <string> addedFiles = new List <string>(); long srcPos; long dstPos = 0; JunkStream js = new JunkStream(junkId, hdr.Read8(6), logicalSize); try { if (junkId == "\0\0\0\0") { srcPos = hdr.Size; imageInfo.BytesData = srcPos; _log?.LogDetail("Null Partition ID, preserving partition as raw"); ConvertFile cf = new ConvertFile(logicalSize - hdr.Size, true) //Size isn't important for writing //result.ImageInfo.IsoSize { FstFile = new FstFile(null) { DataOffset = hdr.Size, Offset = hdr.Size, Length = 0 }, }; long nullsPos = 0; target.Write(hdr.Data, 0, (int)hdr.Size); //0x400 target.Write(pHdr.Header.Data, 0x2bc, 4); //copy the original partition length dstPos += 0x440 + 4 + NkitFormat.ProcessGap(ref nullsPos, cf, ref srcPos, inStream, js, true, scrub, target, _log); } else { hdr.WriteString(0x200, 8, "NKIT v01"); hdr.WriteUInt32B(0x210, (uint)(pHdr.PartitionLength / mlt)); MemorySection fst; List <JunkDiff> junkDiffs = new List <JunkDiff>(); long mainDolAddr = hdr.ReadUInt32B(0x420) * mlt; //############################################################################ //# READ DISC START target.Write(hdr.Data, 0, (int)hdr.Size); inStream.Copy(target, (hdr.ReadUInt32B(0x424) * mlt) - hdr.Size); //read fst with 4 byte boundary fst = MemorySection.Read(inStream, (hdr.ReadUInt32B(0x428) * mlt) + (((hdr.ReadUInt32B(0x428) * mlt) % 4 == 0 ? 0 : 4 - ((hdr.ReadUInt32B(0x428) * mlt) % 4)))); crc.Snapshot(junkId + " PrtHdr"); target.Write(fst.Data, 0, (int)fst.Size); crc.Snapshot(junkId + " Fst"); target.Write(hashes.FlagsToByteArray(), 0, (int)hashes.FlagsLength); crc.Snapshot(junkId + " HashFlags"); srcPos = (hdr.ReadUInt32B(0x424) * mlt) + fst.Size; long nullsPos = srcPos + 0x1c; dstPos = srcPos + hashes.FlagsLength; //create as late as possible in case id is swaped - Dairantou Smash Brothers DX (Japan) (Taikenban), Star Wars - Rogue Squadron II (Japan) (Jitsuen-you Sample) string error; List <ConvertFile> conFiles = NkitFormat.GetConvertFstFiles(inStream, physicalSize, hdr, fst, false, fstFileAlignment, out error); imageInfo.BytesData = srcPos; if (conFiles == null) { if (error != null) { _log?.LogDetail(error); } ConvertFile cf = new ConvertFile(pHdr.PartitionDataLength - srcPos, true) //Size isn't important for writing //result.ImageInfo.IsoSize { FstFile = new FstFile(null) { DataOffset = hdr.ReadUInt32B(0x424), Offset = hdr.ReadUInt32B(0x424), Length = (int)fst.Size }, }; dstPos += NkitFormat.ProcessGap(ref nullsPos, cf, ref srcPos, inStream, js, true, scrub, target, _log); } else { //############################################################################ //# WRITE THE FILESYSTEM List <ConvertFile> missing; NkitFormat.NkitWriteFileSystem(ctx, imageInfo, mlt, inStream, ref srcPos, ref dstPos, hdr, fst, ref mainDolAddr, target, nullsPos, js, conFiles, out missing, scrub, pHdr.PartitionDataLength, _log); dstPos += hashes.HashesToStream(target); if (missing.Count != 0) { _log?.LogDetail(string.Format("{0} Junk File{1} Removed (Files listed in the FST, but not in the image)", missing.Count.ToString(), missing.Count == 1 ? "" : "s")); foreach (ConvertFile cf in missing) { _log?.LogDebug(string.Format("File content is Junk {0}: {1} - Size: {2}", cf.FstFile.DataOffset.ToString("X8"), cf.FstFile.Name, cf.FstFile.Length)); } } crc.Crcs[crc.Crcs.Length - 1].PatchData = hashes.FlagsToByteArray(); crc.Crcs[crc.Crcs.Length - 1].PatchCrc = Crc.Compute(crc.Crcs[crc.Crcs.Length - 2].PatchData); crc.Crcs[crc.Crcs.Length - 2].PatchData = fst.Data; crc.Crcs[crc.Crcs.Length - 2].PatchCrc = Crc.Compute(fst.Data); } } } catch (Exception ex) { throw new HandledException(ex, "NkitWriterGc.Write - Convert"); } return(dstPos); }
/// <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 ExtractResult ExtractFilesWii(Func <ExtractedFile, bool> filter, Action <Stream, ExtractedFile> extract) { List <ExtractResult> result = new List <ExtractResult>(); List <WiiPartitionInfo> toExtract = new List <WiiPartitionInfo>(); WiiDiscHeaderSection hdr = null; bool seekMode = !NStream.IsIsoDec; //experimental as iso.dec may not be able to seek to group start, archives will read ahead not seek ExtractedFile exFile; foreach (IWiiDiscSection s in this.EnumerateSectionsFix(false, true, false)) { if (s is WiiDiscHeaderSection) { hdr = (WiiDiscHeaderSection)s; exFile = new ExtractedFile(this.IsGameCube ? DiscType.GameCube : DiscType.Wii, this.NStream.Id8, null, 0, hdr.Size, "", "hdr.bin", ExtractedFileType.WiiDiscItem); if (filter(exFile)) { using (MemoryStream ms = new MemoryStream(hdr.Data)) extract(ms, exFile); } } else if (s is WiiPartitionSection) { WiiPartitionSection ps = (WiiPartitionSection)s; if (ps.Header.Id != "\0\0\0\0") { exFile = new ExtractedFile(this.IsGameCube ? DiscType.GameCube : DiscType.Wii, this.NStream.Id8, null, ps.Header.DiscOffset, ps.Header.Size, "", ps.Header.Id + "hdr.bin", ExtractedFileType.WiiDiscItem); if (filter(exFile)) { using (MemoryStream ms = new MemoryStream(ps.Header.Data)) extract(ms, exFile); } } using (StreamCircularBuffer decrypted = new StreamCircularBuffer(ps.PartitionDataLength, null, null, output => { foreach (WiiPartitionGroupSection pg in ps.Sections) { for (int i = 0; i < pg.Size / 0x8000; i++) { output.Write(pg.Decrypted, (i * 0x8000) + 0x400, 0x7c00); } } })) { if (ps.Header.Id == "\0\0\0\0") { decrypted.Copy(Stream.Null, ps.PartitionDataLength); } else { extractFiles(ps.Id, new MemorySection(ps.Header.Data), decrypted, filter, extract); } } } else if (s is WiiFillerSection) { } } return(createExtractResult((Region)hdr.ReadUInt32B(0x4e000), null)); }
public IEnumerable <IWiiDiscSection> EnumerateSections(long imageSize) { bool generateUpdateFiller = false; bool generateOtherFiller = false; bool forceFillerJunk = false; long discOffset = 0; WiiDiscHeaderSection hdr = (WiiDiscHeaderSection)NStream.DiscHeader; //this.Header = (BaseSection)hdr; yield return(hdr); discOffset += hdr.Size; string lastId = null; long lastDiscOffset = 0; long updateGapPadding = 0; foreach (WiiPartitionInfo part in hdr.Partitions) //already sorted { WiiPartitionPlaceHolder placeholder = part as WiiPartitionPlaceHolder; //do we have a gap if (lastId != null || part.DiscOffset - discOffset != 0) //null if last was header { if (part.DiscOffset < discOffset) { throw new HandledException("Partition alignment error, this could be a bug when adding missing partitions"); } WiiFillerSection gap = new WiiFillerSection(NStream, part.Type == PartitionType.Update, discOffset, part.DiscOffset - discOffset, updateGapPadding, null, generateUpdateFiller, generateOtherFiller, forceFillerJunk); yield return(gap); discOffset += gap.Size; ensurePosition(NStream, discOffset - updateGapPadding); } WiiPartitionSection partSec; if (placeholder != null) { if (placeholder.Filename != null) { if (generateOtherFiller && NStream.Position + updateGapPadding > hdr.Partitions.Max(a => a is WiiPartitionPlaceHolder ? 0 : a.DiscOffset)) { NStream.Complete(); //Placeholders from now, no stream reading required } partSec = new WiiPartitionSection(NStream, (WiiDiscHeaderSection)NStream.DiscHeader, placeholder.Stream, discOffset); ensurePosition(NStream, discOffset + partSec.Size - updateGapPadding); //used to be a seek - _stream.Seek(partSec.Size, SeekOrigin.Current); //stream will seek forward } else { continue; //force filler } } else //should not get called when _stream is null { partSec = new WiiPartitionSection(NStream, (WiiDiscHeaderSection)NStream.DiscHeader, this.NStream, discOffset); } yield return(partSec); ensurePosition(NStream, discOffset + partSec.Size - updateGapPadding); if (generateOtherFiller && NStream.Position + updateGapPadding > hdr.Partitions.Max(a => a is WiiPartitionPlaceHolder ? 0 : a.DiscOffset)) { NStream.Complete(); //Placeholders from now, no stream reading required } if (placeholder != null && placeholder.Filename != null) { placeholder.Dispose(); } lastId = partSec.Id; lastDiscOffset = partSec.DiscOffset + updateGapPadding; discOffset += partSec.Size; } if (lastId != null) { yield return(new WiiFillerSection(NStream, false, discOffset, (imageSize == 0 ? NStream.Length : imageSize) - discOffset, 0, null, generateUpdateFiller, generateOtherFiller, forceFillerJunk)); } }
/// <summary> /// Forward read of the full iso /// </summary> /// <param name="generateUpdateFiller">True: blank the update filler, False: copy it to catch the unknown extra data sin some images</param> /// <param name="generateOtherFiller">True: skip reading other filler (junk) and generate it, False: read filler sections from the source</param> /// <param name="forceFillerJunk">True: Generate the junk even if either of the above is false for comparison purposes</param> /// <returns></returns> public IEnumerable <IWiiDiscSection> EnumerateSectionsFix(bool generateUpdateFiller, bool generateOtherFiller, bool forceFillerJunk) { long discOffset = 0; WiiDiscHeaderSection hdr = (WiiDiscHeaderSection)NStream.DiscHeader; //this.Header = (BaseSection)hdr; yield return(hdr); discOffset += hdr.Size; string lastId = null; long lastDiscOffset = 0; long updateGapPadding = 0; foreach (WiiPartitionInfo part in hdr.Partitions) //already sorted { WiiPartitionPlaceHolder placeholder = part as WiiPartitionPlaceHolder; //do we have a gap if (lastId != null || part.DiscOffset - discOffset != 0) //null if last was header { if (lastDiscOffset <= 0x50000L && part.DiscOffset > part.SrcDiscOffset && updateGapPadding == 0) //only once { updateGapPadding = part.DiscOffset - part.SrcDiscOffset; Log?.LogDetail(string.Format("Moving Data Partition from {0} to {1}", part.SrcDiscOffset.ToString("X8"), part.DiscOffset.ToString("X8"))); } if (part.DiscOffset < discOffset) { throw new HandledException("Partition alignment error, this could be a bug when adding missing partitions"); } WiiFillerSection gap = new WiiFillerSection(NStream, discOffset < 0xF800000L, discOffset, part.DiscOffset - discOffset, updateGapPadding, null, generateUpdateFiller, generateOtherFiller, forceFillerJunk); yield return(gap); discOffset += gap.Size; ensurePosition(NStream, discOffset - updateGapPadding); } WiiPartitionSection partSec; if (placeholder != null) { if (placeholder.Filename != null) { if (generateOtherFiller && NStream.Position + updateGapPadding > hdr.Partitions.Max(a => a is WiiPartitionPlaceHolder ? 0 : a.DiscOffset)) { NStream.Complete(); //Placeholders from now, no stream reading required } partSec = new WiiPartitionSection(NStream, (WiiDiscHeaderSection)NStream.DiscHeader, placeholder.Stream, discOffset); ensurePosition(NStream, discOffset + partSec.Size - updateGapPadding); //used to be a seek - _stream.Seek(partSec.Size, SeekOrigin.Current); //stream will seek forward } else { continue; //force filler } } else //should not get called when _stream is null { partSec = new WiiPartitionSection(NStream, (WiiDiscHeaderSection)NStream.DiscHeader, this.NStream, discOffset); } //correct the stream length - required for 1 dual layer than when shrank is seen as single layer if (partSec.DiscOffset + partSec.PartitionLength > NStream.Length) { NStream.SetLength(partSec.DiscOffset + partSec.PartitionLength); } yield return(partSec); ensurePosition(NStream, discOffset + partSec.Size - updateGapPadding); if (generateOtherFiller && NStream.Position + updateGapPadding > hdr.Partitions.Max(a => a is WiiPartitionPlaceHolder ? 0 : a.DiscOffset)) { NStream.Complete(); //Placeholders from now, no stream reading required } if (placeholder != null && placeholder.Filename != null) { placeholder.Dispose(); } lastId = partSec.Id; lastDiscOffset = partSec.DiscOffset + updateGapPadding; discOffset += partSec.Size; } if (lastId != null) { yield return(new WiiFillerSection(NStream, false, discOffset, NStream.RecoverySize - discOffset, 0, lastDiscOffset == 0xF800000 && lastId == "RELS" ? lastId : null, generateUpdateFiller, generateOtherFiller, forceFillerJunk)); } }
public void Read(Context ctx, NStream inStream, Stream outStream, Coordinator pc) { if (!Settings.ConfigFileFound) { _log?.Log("!! No config file found - This is required to restore and validate images"); } bool truncatedRvtr = false; bool write = !(outStream is ByteStream || outStream == Stream.Null); //ProgressResult result = ctx.Result; byte[] origHeader = null; byte[] dataHeader = new byte[256]; uint headerCrc = 0; uint dataHeaderCrc = 0; //bool fstMissingWithH3Error = false; NCrc crc = new NCrc(); CryptoStream target = null; int h3Errors = 0; List <string> requiredUpdateFiles = new List <string>(); try { //long progress = 0; string lastPartitionId = ""; bool generateUpdateFiller = false; bool generateOtherFiller = true; bool forceFillerJunk = false; NDisc disc = new NDisc(_log, inStream); foreach (IWiiDiscSection s in disc.EnumerateSectionsFix(generateUpdateFiller, generateOtherFiller, forceFillerJunk)) { if (s is WiiDiscHeaderSection) { _hdr = (WiiDiscHeaderSection)s; origHeader = (byte[])_hdr.Data.Clone(); target = new CryptoStream(outStream, crc, CryptoStreamMode.Write); target.Write(_hdr.Data, 0, _hdr.Data.Length); //write the header applyPartitionTableFixes(inStream, requiredUpdateFiles, ctx.Settings, ctx.Recovery); headerCrc = Crc.Compute(_hdr.Data); crc.Snapshot("Header"); pc.ReaderCheckPoint1PreWrite(null, 0); //size that we will output from this read } else if (s is WiiPartitionSection) { WiiPartitionSection ps = (WiiPartitionSection)s; if (!truncatedRvtr && ps.Header.IsRvtR && inStream.RecoverySize == NStream.FullSizeWii5) { _log.LogDetail(string.Format("Truncated RVT-R image detected. Pad it with 00 to {0} bytes for NKit to handle it properly", NStream.FullSizeWiiRvtr.ToString())); truncatedRvtr = true; } if (applyFixes(ps.Header, inStream)) { _hdr.UpdateRepair(); headerCrc = Crc.Compute(_hdr.Data); //recalculate } if (ps.Header.Type == PartitionType.Data) { if (applyDataPartitionFixes(ps.Header)) { headerCrc = Crc.Compute(_hdr.Data); //recalculate } Array.Copy(ps.Header.Data, dataHeader, dataHeader.Length); } target.Write(ps.Header.Data, 0, (int)ps.Header.Size); foreach (WiiPartitionGroupSection pg in ps.Sections) { if (ps.Header.Type == PartitionType.Data && pg.DiscOffset == ps.Header.DiscOffset + ps.Header.Size) { Array.Copy(pg.Decrypted, 0x400, dataHeader, 0, dataHeader.Length); //copy out datapartition header (256 bytes) } pg.Unscrub(ctx.Recovery.JunkPatches); h3Errors += pg.H3Errors; target.Write(pg.Encrypted, 0, (int)pg.Size); } if (h3Errors != 0) { _log?.LogDetail(string.Format("{0} unrecoverable group errors, this image will now be corrupted due to failed unscrubbing attempts!", h3Errors.ToString())); } lastPartitionId = ps.Id; } 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(((WiiFillerSection)s).DiscOffset == 0x50000 ? "[Update Filler]" : lastPartitionId); } } } catch (Exception ex) { throw pc.SetReaderException(ex, "RestoreReaderWii.Restore - Read and repair"); } try { Tuple <string, string, string, uint>[] allParts = ctx.Recovery.WiiUPartsData.Union(ctx.Recovery.WiiUOtherPartsData).ToArray(); uint[] uniqueCrcs = allParts.Select(a => a.Item4).Union(ctx.Settings.RedumpUpdateCrcs.Where(a => !allParts.Any(b => a == b.Item4))).ToArray(); //create a data header based on the modified header byte[] dataHdr = dataHeader; dataHeader = (byte[])_hdr.Data.Clone(); Array.Copy(dataHdr, dataHeader, dataHdr.Length); Array.Clear(dataHeader, 0x60, 2); dataHeaderCrc = Crc.Compute(dataHeader); bool isCustom = false; bool updatePartitionMissing = false; SortedList <uint, bool> checkCrcs = new SortedList <uint, bool>(); foreach (RedumpEntry r in ctx.Dats.RedumpData) { checkCrcs.Add(r.Crc, true); } foreach (RedumpEntry r in ctx.Dats.CustomData.Where(a => !checkCrcs.ContainsKey(a.Crc))) { checkCrcs.Add(r.Crc, false); } HeaderBruteForcer crcMtch = new HeaderBruteForcer(uniqueCrcs, checkCrcs, ctx.Settings.RedumpRegionData, _hdr.Data, dataHeader); BruteForceCrcResult bfMatch = crcMtch.Match(crc.Crcs); string updateFilename = allParts?.FirstOrDefault(a => a.Item4 == (bfMatch.UpdateChanged ? bfMatch.UpdateCrc : crc.Crcs[1].Value))?.Item1; updatePartitionMissing = bfMatch.UpdateChanged && !allParts.Any(a => a.Item4 == bfMatch.UpdateCrc); //matched, but update crc not an update partition if (bfMatch.HeaderChanged) { crc.Crcs[0].PatchCrc = bfMatch.HeaderCrc; crc.Crcs[0].PatchData = bfMatch.Header; if (bfMatch.RegionChanged) { _log.LogDetail(bfMatch.Region != bfMatch.OriginalRegion ? string.Format("Region changed from {0} to {1}", ((Region)bfMatch.OriginalRegion).ToString(), ((Region)bfMatch.Region).ToString()) : string.Format("Region age ratings changed for {0} region", ((Region)bfMatch.Region).ToString())); } } bool isRecoverable = false; if (bfMatch.UpdateChanged) { if (!updatePartitionMissing) { _log?.LogDetail(string.Format("Matched recovery update partition: {0}", updateFilename ?? "")); crc.Crcs[1].Name = updateFilename ?? string.Format("[UNKNOWN {0}]", bfMatch.UpdateCrc.ToString("X8")); crc.Crcs[1].PatchCrc = bfMatch.UpdateCrc; crc.Crcs[1].PatchFile = Path.Combine(ctx.Recovery.WiiUPartsData.Any(a => a.Item4 == bfMatch.UpdateCrc) ? ctx.Settings.RecoveryFilesPath : ctx.Settings.OtherRecoveryFilesPath, updateFilename); } else { _log?.LogDetail(string.Format("Missing update recovery partition file *_{0}", bfMatch.UpdateCrc.ToString("X8"))); crc.Crcs[1].Name = "Missing Recovery Partition File"; crc.Crcs[1].PatchCrc = bfMatch.UpdateCrc; crc.Crcs[1].PatchFile = null; isRecoverable = true; } } else if (!string.IsNullOrEmpty(updateFilename)) { crc.Crcs[1].Name += string.Format(" [Matches {0}{1}]", updateFilename, isRecoverable ? " (Recoverable)" : ""); } string resultMsg = "MatchFail"; if (bfMatch.MatchedCrc != 0) { if (updatePartitionMissing) { resultMsg = string.Format("Match {0} (Recoverable: missing update partition {1})", isCustom ? "Custom" : "Redump", bfMatch.UpdateCrc.ToString("X8")); } else { resultMsg = string.Format("Match {0}", isCustom ? "Custom" : "Redump"); } } pc.ReaderCheckPoint2Complete(crc, isRecoverable, crc.FullCrc(true), crc.FullCrc(true), VerifyIsWrite, bfMatch?.Header ?? _hdr.Data, resultMsg); pc.ReaderCheckPoint3Complete(); } catch (Exception ex) { throw pc.SetReaderException(ex, "RestoreReaderWii.Read - Read and repair"); //don't let the writer lock } }