Exemplo n.º 1
0
        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;
        }
Exemplo n.º 2
0
        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();
        }
Exemplo n.º 3
0
        private ExtractResult ExtractFilesWiiNkit(Func <ExtractedFile, bool> filter, Action <Stream, ExtractedFile> extract)
        {
            WiiDiscHeaderSection hdr    = (WiiDiscHeaderSection)Header;
            ExtractedFile        exFile = new ExtractedFile(IsGameCube ? DiscType.GameCube : DiscType.Wii,
                                                            NStream.Id8, null, 0, hdr.Size, "", "hdr.bin", ExtractedFileType.WiiDiscItem);
            long srcPos = hdr.Size;

            if (filter(exFile))
            {
                using (MemoryStream ms = new MemoryStream(hdr.Data))
                {
                    extract(ms, exFile);
                }
            }

            foreach (WiiPartitionInfo part in hdr.Partitions) //already sorted
            {
                if (NStream.Position < part.DiscOffset)
                {
                    NStream.Copy(Stream.Null, part.DiscOffset - NStream.Position);
                }

                long          prtPos     = NStream.Position;
                MemorySection prtHdr     = MemorySection.Read(NStream, 0x20000);
                MemorySection prtDataHdr = MemorySection.Read(NStream, 0x440);
                string        prtId      = prtDataHdr.ReadString(0, 4);
                if (prtId != "\0\0\0\0")
                {
                    srcPos += prtHdr.Size + prtDataHdr.Size;
                    exFile  = new ExtractedFile(IsGameCube ? DiscType.GameCube : DiscType.Wii,
                                                NStream.Id8, null, prtPos, prtHdr.Size, "", prtId + "hdr.bin", ExtractedFileType.WiiDiscItem);
                    if (filter(exFile))
                    {
                        using (MemoryStream ms = new MemoryStream(prtHdr.Data))
                        {
                            extract(ms, exFile);
                        }
                    }

                    extractFiles(prtId, prtDataHdr, NStream, filter, extract);
                }
            }
            return(createExtractResult((Region)Header.ReadUInt32B(0x4e000), null));
        }
Exemplo n.º 4
0
        private void patchGroups(NkitPartitionPatchInfo patchInfo, WiiDiscHeaderSection discHeader, long crcPatchOffset, byte[] crcPatchData)
        {
            long partDataOffset = patchInfo.DiscOffset + patchInfo.PartitionHeader.Size;
            int  groupIdx       = (int)((crcPatchOffset - partDataOffset) / WiiPartitionSection.GroupSize);

            byte[] data = new byte[WiiPartitionSection.GroupSize];

            WiiPartitionHeaderSection wh = new WiiPartitionHeaderSection(discHeader, null, partDataOffset, patchInfo.PartitionHeader.Data, patchInfo.PartitionHeader.Size)
            {
                ScrubManager = patchInfo.ScrubManager
            };

            wh.Initialise(true, patchInfo.PartitionDataHeader.ReadString(0, 4));
#if DECRYPT
            WiiPartitionGroupSection wp = new WiiPartitionGroupSection(discHeader, wh, ph.Data, , ph.Data.Length, false);
#else
            WiiPartitionGroupSection wp = new WiiPartitionGroupSection(discHeader, wh, data, partDataOffset, Math.Min(WiiPartitionSection.GroupSize, crcPatchData.Length), true);
#endif

            if (patchInfo.Fst != null)
            {
                using (MemoryStream fstStream = new MemoryStream(patchInfo.Fst.Data))
                {
                    fstPatch(patchInfo, wp, crcPatchOffset, crcPatchData, fstStream);
                }
            }

            MemorySection d = new MemorySection(crcPatchData);

            for (long i = 0; i < crcPatchData.Length; i += WiiPartitionSection.GroupSize)
            {
                Array.Copy(d.Data, (int)i, data, 0, Math.Min(WiiPartitionSection.GroupSize, d.Size - i));
                wp.Populate(groupIdx++, data, crcPatchOffset + i, Math.Min(WiiPartitionSection.GroupSize, d.Size - i));
                wp.ForceHashes(null);
                if (patchInfo.HashGroups.ContainsKey(wp.DiscOffset))
                {
                    hashPatchGroup(patchInfo, wp);
                    d.Write((int)i, wp.Encrypted, (int)wp.Size);
                }
            }
        }
Exemplo n.º 5
0
 internal WiiPartitionGroupSection(WiiDiscHeaderSection hdr, WiiPartitionHeaderSection partHdr, byte[] data, long discOffset, long size, bool encrypted) : this(null, hdr, partHdr, data, discOffset, size, encrypted)
 {
 }
Exemplo n.º 6
0
        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
            }
        }
Exemplo n.º 7
0
        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");
            }
        }
Exemplo n.º 8
0
        /// <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()));
        }
Exemplo n.º 9
0
        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));
        }
Exemplo n.º 10
0
        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));
            }
        }
Exemplo n.º 11
0
        /// <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));
            }
        }
Exemplo n.º 12
0
        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
            }
        }
Exemplo n.º 13
0
        public void Read(Context ctx, NStream inStream, Stream outStream, Coordinator pc)
        {
            try
            {
                WiiDiscHeaderSection hdr = (WiiDiscHeaderSection)inStream.DiscHeader;
                string idVer             = hdr.ReadString(0x200, 8);
                if (idVer != "NKIT v01")
                {
                    throw new Exception(string.Format("{0} not supported by this version", idVer));
                }
                bool          isNkit              = idVer.StartsWith("NKIT");
                uint          nkitCrc             = hdr.ReadUInt32B(0x208);
                long          imageSize           = hdr.ReadUInt32B(0x210) * 4L;
                string        junkId              = hdr.ReadString(0x214, 4);
                uint          updatePartitionCrc  = hdr.ReadUInt32B(0x218);
                MemorySection updateRemovedFiller = null;

                long discOffset = 0;
                List <NkitPartitionPatchInfo> patchInfos = new List <NkitPartitionPatchInfo>();
                discOffset += hdr.Size;
                string        lastPartitionId   = null;
                PartitionType lastPartitionType = PartitionType.Other;
                NCrc          crc           = new NCrc();
                long          dstPos        = 0;
                long          srcPos        = hdr.Size;
                ScrubManager  scrubFiller   = new ScrubManager(null);
                bool          isRecoverable = false;

                using (NDisc disc = new NDisc(_log, inStream))
                {
                    hdr.WriteUInt32B(0x200, 0);
                    hdr.WriteUInt32B(0x204, 0);
                    hdr.WriteUInt32B(0x208, 0);
                    hdr.WriteUInt32B(0x20C, 0);
                    hdr.WriteUInt32B(0x210, 0);
                    hdr.WriteUInt32B(0x214, 0);
                    hdr.WriteUInt32B(0x218, 0);

                    hdr.Write8(0x60, 0);
                    hdr.Write8(0x61, 0);

                    CryptoStream crcStream = new CryptoStream(outStream, crc, CryptoStreamMode.Write); //wrap to calculate crc
                    crcStream.Write(hdr.Data, 0, hdr.Data.Length);                                     //write the header
                    pc.ReaderCheckPoint1PreWrite(null, nkitCrc);                                       //size that will be output from this read
                    dstPos += hdr.Size;

                    crc.Snapshot("Disc Header");

                    foreach (WiiPartitionInfo part in hdr.Partitions)      //already sorted
                    {
                        if (updatePartitionCrc != 0 && srcPos == hdr.Size) //write update partition out
                        {
                            updateRemovedFiller = MemorySection.Read(inStream, hdr.Partitions.First().DiscOffset - srcPos);
                            srcPos += updateRemovedFiller.Size;
                            WiiPartitionInfo firstPart      = WiiDiscHeaderSection.CreatePartitionInfos(updateRemovedFiller, 0)?.FirstOrDefault(a => a.Type != PartitionType.Update);
                            string           updateFileName = RecoveryData.GetUpdatePartition(ctx.Settings, updatePartitionCrc);

                            if (updateFileName != null)
                            {
                                using (FileStream pf = File.OpenRead(updateFileName))
                                {
                                    pf.Copy(crcStream, pf.Length);
                                    dstPos += pf.Length;
                                }
                            }
                            else
                            {
                                string msg = string.Format("!! Update partition *_{0} missing - Adding filler. It may be Recoverable", updatePartitionCrc.ToString("X8"));
                                isRecoverable = true;
                                _log?.LogDetail(msg);
                                //throw pc.SetReaderException(new HandledException("Failed to convert: " + msg));
                            }
                            ByteStream.Zeros.Copy(crcStream, firstPart.DiscOffset - dstPos); //fill full gap
                            dstPos += firstPart.DiscOffset - dstPos;
                        }

                        NkitPartitionPatchInfo patchInfo = new NkitPartitionPatchInfo()
                        {
                            HashGroups = new Dictionary <long, MemorySection>()
                        };
                        patchInfos.Add(patchInfo);

                        if (part.DiscOffset > srcPos)
                        {
                            dstPos += writeFiller(ref srcPos, dstPos, dstPos + 0x1cL, inStream, crcStream, new JunkStream(lastPartitionType != PartitionType.Data ? hdr.ReadString(0, 4) : lastPartitionId, hdr.Read8(6), lastPartitionType == PartitionType.Update ? 0 : imageSize), scrubFiller);
                            inStream.Copy(ByteStream.Zeros, part.DiscOffset - srcPos); //padded to 0x8000
                            srcPos += part.DiscOffset - srcPos;
                        }

                        part.DiscOffset           = dstPos; //restore the original position
                        patchInfo.DiscOffset      = dstPos;
                        patchInfo.PartitionHeader = MemorySection.Read(inStream, 0x20000);
                        srcPos += patchInfo.PartitionHeader.Size;
                        long    size     = patchInfo.PartitionHeader.ReadUInt32B(0x2bc) * 4L;
                        LongRef origSize = new LongRef()
                        {
                            Value = 0
                        };
                        WiiPartitionGroupSection  wp = null;
                        WiiPartitionHeaderSection wh = new WiiPartitionHeaderSection(hdr, null, part.DiscOffset, patchInfo.PartitionHeader.Data, patchInfo.PartitionHeader.Data.Length);
                        MemorySection             ph = new MemorySection(new byte[0x8000 * 64]);
                        long         remaining       = long.MaxValue;//set after first block read
                        int          groupIndex      = 0;
                        WiiHashStore hashes          = new WiiHashStore();
                        patchInfo.ScrubManager = wh.ScrubManager;
                        bool patchBlock = false;
                        StreamCircularBuffer prtStream = null;

                        try
                        {
                            using (prtStream = new StreamCircularBuffer(0, null, null, output => srcPos += partitionStreamWrite(origSize, inStream, output, size, ctx.Dats, patchInfo, hashes, pc)))
                            {
                                int          gs          = 0;
                                int          ge          = 0;
                                int          i           = 0;
                                MemoryStream patchBlocks = null;

                                while (remaining > 0)
                                {
                                    int blocks = (int)Math.Min(64L, remaining / 0x7c00);
                                    for (int b = 0; b < blocks; b++)
                                    {
                                        prtStream.Read(ph.Data, (b * 0x8000) + 0x400, 0x7c00); //load aligned with no hashes

                                        if (remaining == long.MaxValue)                        //first loop
                                        {
                                            remaining = origSize.Value;

                                            if (ph.ReadString(0x400 + 0, 4) == "\0\0\0\0")
                                            {
                                                gs              = -1;
                                                ge              = -1;
                                                blocks          = (int)Math.Min(64L, remaining / 0x7c00);
                                                lastPartitionId = ph.ReadString(0x400 + 0, 4);
                                                patchInfo.PartitionHeader.WriteUInt32B(0x2bc, (uint)(NStream.DataToHashedLen(origSize.Value) / 4)); //restore real size
                                                crcStream.Write(patchInfo.PartitionHeader.Data, 0, patchInfo.PartitionHeader.Data.Length);
                                                dstPos += patchInfo.PartitionHeader.Size;
                                            }
                                            else
                                            {
                                                gs = (int)((ph.ReadUInt32B(0x400 + 0x424) * 4L) / (0x7c00L * 64));
                                                ge = (int)(((ph.ReadUInt32B(0x400 + 0x424) * 4L) + (ph.ReadUInt32B(0x400 + 0x428) * 4L)) / (0x7c00L * 64));
                                                if ((int)((part.DiscOffset + (ph.ReadUInt32B(0x400 + 0x428) * 4L)) % (0x7c00L * 64)) == 0)
                                                {
                                                    ge--; //don't load the next group if the data will end on the last byte
                                                }
                                                blocks          = (int)Math.Min(64L, remaining / 0x7c00);
                                                lastPartitionId = ph.ReadString(0x400 + 0, 4);

                                                patchInfo.PartitionHeader.WriteUInt32B(0x2bc, ph.ReadUInt32B(0x400 + 0x210)); //restore real size
                                                crcStream.Write(patchInfo.PartitionHeader.Data, 0, patchInfo.PartitionHeader.Data.Length);
                                                dstPos += patchInfo.PartitionHeader.Size;

                                                ph.WriteUInt32B(0x400 + 0x200, 0);
                                                ph.WriteUInt32B(0x400 + 0x204, 0);
                                                ph.WriteUInt32B(0x400 + 0x208, 0);
                                                ph.WriteUInt32B(0x400 + 0x20C, 0);
                                                ph.WriteUInt32B(0x400 + 0x210, 0);
                                                ph.WriteUInt32B(0x400 + 0x214, 0);
                                                ph.WriteUInt32B(0x400 + 0x218, 0);
                                            }
                                            wp = new WiiPartitionGroupSection(hdr, wh, ph.Data, part.DiscOffset, blocks * 0x8000, false);
                                            wh.Initialise(wp, origSize.Value);
                                        }
                                    }

                                    if (blocks < 64)
                                    {
                                        Array.Clear(ph.Data, blocks * 0x8000, ph.Data.Length - (blocks * 0x8000)); //clear remaining blocks
                                    }
                                    wp.Populate(groupIndex, ph.Data, dstPos, blocks * 0x8000);

                                    int scrubbed = 0;
                                    for (int bi = 0; bi < blocks; bi++)
                                    {
                                        wp.MarkBlockDirty(bi);
                                        byte byt;
                                        if (patchInfo.ScrubManager.IsBlockScrubbedScanMode(wp.Offset + (bi * 0x8000), out byt))
                                        {
                                            wp.SetScrubbed(bi, byt);
                                            scrubbed++;
                                        }
                                    }
                                    bool isFstBlocks = i >= gs && i <= ge;
                                    bool reqHashes   = hashes.IsPreserved(wp.Offset);                                                                                            //test with 0 partition based offset

                                    repairBlocks(wp, scrubbed, blocks, false, isFstBlocks);                                                                                      //only test if the hashes aren't preserved (only preserved for scrubbed/customs)

                                    if (reqHashes)                                                                                                                               //store with disc based offset
                                    {
                                        patchInfo.HashGroups.Add(wp.Offset + part.DiscOffset + patchInfo.PartitionHeader.Size, new MemorySection((byte[])wp.Decrypted.Clone())); //fetch the stored hashed that couldn't be recreated
                                    }
                                    groupIndex++;
                                    bool inFstArea = i >= gs && i <= ge;

                                    if (!patchBlock && (gs == i || reqHashes))
                                    {
                                        patchBlocks = new MemoryStream();
                                        crc.Snapshot(lastPartitionId + " Data");
                                        patchBlock = true;
                                    }
                                    else if (patchBlock && !inFstArea && !reqHashes)
                                    {
                                        crc.Snapshot(lastPartitionId + " Patch");
                                        crc.Crcs.Last().PatchData = patchBlocks.ToArray();
                                        patchBlocks.Dispose();
                                        patchBlock = false;
                                    }
#if DECRYPT
                                    outStream.Write(wp.Decrypted, 0, blocks * 0x8000);
                                    if (i >= gs && i <= ge)
                                    {
                                        fstBlocks.Write(wp.Decrypted, 0, blocks * 0x8000);
                                    }
#else
                                    crcStream.Write(wp.Encrypted, 0, blocks * 0x8000);
                                    if (patchBlock)
                                    {
                                        patchBlocks.Write(wp.Encrypted, 0, blocks * 0x8000);
                                    }
#endif

                                    remaining -= (blocks * 0x7c00);
                                    dstPos    += (blocks * 0x8000);
                                    i++;
                                }
                                if (patchBlock)
                                {
                                    crc.Snapshot(lastPartitionId + " Patch");
                                    crc.Crcs.Last().PatchData = patchBlocks.ToArray();
                                    patchBlocks.Dispose();
                                }
                            }
                            if (origSize.Value != prtStream.WriterPosition)
                            {
                                throw pc.SetReaderException(new HandledException("Partition read did not write the full amount to the circular buffer before completing"));
                            }
                        }
                        catch (Exception ex)
                        {
                            if (prtStream?.WriterException != null)
                            {
                                throw pc.SetReaderException(prtStream.WriterException, "Failed reading filesystem");
                            }
                            throw pc.SetReaderException(ex, "Failed converting the filesystem");;  //writer exception
                        }

                        srcPos += hashes.ReadPatchData(part.DiscOffset + patchInfo.PartitionHeader.Size, patchInfo.HashGroups, inStream);

                        //read hash patches
                        lastPartitionType = part.Type;
                    }

                    if (srcPos < inStream.Length)
                    {
                        JunkStream partJunk = new JunkStream(lastPartitionType != PartitionType.Data ? hdr.ReadString(0, 4) : lastPartitionId, hdr.Read8(6), lastPartitionType == PartitionType.Update ? 0 : imageSize);
                        dstPos += writeFiller(ref srcPos, dstPos, lastPartitionType == PartitionType.Update ? imageSize : dstPos + 0x1cL, inStream, crcStream, partJunk, scrubFiller);
                    }
                }

                crc.Snapshot("End");

                if (updatePartitionCrc != 0)
                {
                    hdr.Write((int)WiiDiscHeaderSection.PartitionTableOffset, updateRemovedFiller.Data, 0, (int)WiiDiscHeaderSection.PartitionTableLength); //restore the exact partition table if update was removed
                }
                else
                {
                    hdr.UpdateOffsets(); //just update the table with the new offsets
                }
                crc.Crcs[0].PatchData = hdr.Data;

                foreach (CrcItem ci in crc.Crcs.Where(a => a.PatchData != null))
                {
                    NkitPartitionPatchInfo patchInfo = patchInfos.FirstOrDefault(a => ci.Offset >= a.DiscOffset + a.PartitionHeader.Size && ci.Offset < a.DiscOffset + a.PartitionHeader.Size + a.Size);
                    if (patchInfo != null)
                    {
                        patchGroups(patchInfo, hdr, ci.Offset, ci.PatchData);
                    }
                    ci.PatchCrc = Crc.Compute(ci.PatchData);
                }

                if (imageSize != dstPos)
                {
                    throw pc.SetReaderException(new HandledException("Nkit image read output {0} bytes not the expected {1}!", dstPos.ToString(), imageSize.ToString()));
                }

                pc.ReaderCheckPoint2Complete(crc, isRecoverable, nkitCrc, crc.FullCrc(true), this.VerifyIsWrite, hdr.Data, nkitCrc == crc.FullCrc(true) ? "NKit Valid" : "NKit Invalid");
                pc.ReaderCheckPoint3Complete();
            }
            catch (Exception ex)
            {
                throw pc.SetReaderException(ex, "NkitReaderWii.Read - Read and convert");
            }
        }
Exemplo n.º 14
0
        internal WiiPartitionHeaderSection(WiiDiscHeaderSection header, NStream stream, long discOffset, byte[] data, long size) : base(stream, discOffset, data, size)
        {
            _hdr        = header;
            _fileSystem = null;
            Aes         = Aes.Create();
            _sha1       = SHA1.Create();

            Aes.Padding       = PaddingMode.None;
            _dataOffset       = this.ReadUInt32B(0x2b8) * 4L;
            PartitionSize     = this.ReadUInt32B(0x2bc) * 4L;
            PartitionDataSize = NStream.HashedLenToData(PartitionSize);

            int h3Offset  = (int)this.ReadUInt32B(0x2b4) * 4;
            int tmdOffset = (int)(this.ReadUInt32B(0x2a8) * 4);

            if (h3Offset != 0)
            {
                H3Table = this.Read(h3Offset, 0x18000);
            }
            if (tmdOffset != 0)
            {
                ContentSha1 = this.Read(tmdOffset + 0x1e4 + 0x10, 20);
            }

            // Determine the common key to use.
            string issuer = Encoding.ASCII.GetString(this.Read(0x140, 64)).TrimEnd('\0');

            IsRvt    = issuer == "Root-CA00000002-XS00000006"; //Use the RVT-R key.
            IsKorean = !IsRvt && this.Read8(0x1f1) == 1;       //Use the Korean Key
            IsRvtR   = !(IsRvtH = IsRvt && PartitionSize == 0);
            if (IsRvtH)
            {
                return; //notsupported
            }
            int i = IsRvt ? 0 : IsKorean ? 1 : 2;

            byte[] lame = Convert.FromBase64String(_lame);
            byte[] l    = new byte[lame.Length / 3];
            for (int j = 0; j < l.Length; i += 3)
            {
                l[j++] = lame[i];
            }
            Aes.Key = l;

            byte[] titleKey = this.Read(0x1bf, 16);
            byte[] iv       = this.Read(0x1dc, 16);
            Array.Clear(iv, 8, 8);
            Aes.IV = iv;

            using (ICryptoTransform cryptor = Aes.CreateDecryptor())
                cryptor.TransformBlock(titleKey, 0, 16, titleKey, 0);

            this.Key = titleKey;
            Aes.Key  = this.Key;

            //decrypt scrubbed values. This is to allow the comparison of scrubbed partition data in the decrypted layer
            DecryptedScrubbed00 = new byte[16];
            DecryptedScrubbedFF = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

            Aes.IV = (byte[])DecryptedScrubbedFF.Clone(); //if properly scrubbed then the KEY is FFs too
            using (ICryptoTransform cryptor = Aes.CreateDecryptor())
                cryptor.TransformBlock(DecryptedScrubbedFF, 0, 16, DecryptedScrubbedFF, 0);

            Aes.IV = new byte[16];
            using (ICryptoTransform cryptor = Aes.CreateDecryptor())
                cryptor.TransformBlock(DecryptedScrubbed00, 0, 16, DecryptedScrubbed00, 0);

            _scrubManager = new ScrubManager(this);
        }