コード例 #1
0
ファイル: srr.cs プロジェクト: zc0nf/pyrescene
        static int Reconstruct(FileInfo srrFileInfo, DirectoryInfo inFolder, DirectoryInfo outFolder, bool savePaths, Dictionary <string, string> hints, bool skipRarCrc, bool autoLocateRenamed)
        {
            string     rarName = null, srcName = null;
            FileStream rarfs = null, srcfs = null;
            bool       rebuildRecovery = false;

            byte[] copyBuff   = new byte[0x10000];
            uint   runningCrc = 0;

            using (RarReader rarReader = new RarReader(srrFileInfo.FullName, RarReadMode.SRR))
            {
                RarBlock block;
                while ((block = rarReader.Read()) != null)
                {
                    if (verbose)
                    {
                        Console.WriteLine("\tBlock Type: 0x{0:x2}", block.RawType);
                        Console.WriteLine("\tBlock Size: {0}", block.RawData.Length);
                    }

                    if (block is SrrHeaderBlock)
                    {
                        // file header block.  the only thing here so far is the name of the app that created the SRR file
                        if ((block.Flags & ~SrrHeaderBlock.SupportedFlagMask) != 0)
                        {
                            ReportUnsupportedFlag();
                        }

                        SrrHeaderBlock headBlock = (SrrHeaderBlock)block;

                        if (verbose)
                        {
                            Console.WriteLine("SRR file created with {0}", headBlock.AppName);
                        }
                    }
                    else if (block is SrrStoredFileBlock)
                    {
                        // There is a file stored within the .srr.  extract it.
                        if ((block.Flags & ~SrrStoredFileBlock.SupportedFlagMask) != 0)
                        {
                            ReportUnsupportedFlag();
                        }

                        SrrStoredFileBlock sb       = (SrrStoredFileBlock)block;
                        string             fileName = savePaths ? sb.FileName : sb.FileName.Substring(sb.FileName.LastIndexOf('/') + 1);
                        FileInfo           fileInfo = new FileInfo(Path.Combine(outFolder.FullName, fileName));
                        if (CheckOverwrite(fileInfo.FullName))
                        {
                            Console.WriteLine("Re-creating stored file: {0}", fileName);
                            fileInfo.Directory.Create();
                            using (FileStream sffs = fileInfo.Create())
                                sffs.Write(block.RawData, sb.FileOffset, (int)sb.FileLength);
                        }
                        else
                        {
                            ReportError("Operation aborted.");
                            return(-1);
                        }
                    }
                    else if (block is SrrRarFileBlock)
                    {
                        // for each SRR block, we need to create a RAR file.  get the stored name and create it.
                        if ((block.Flags & ~SrrRarFileBlock.SupportedFlagMask) != 0)
                        {
                            ReportUnsupportedFlag();
                        }

                        SrrRarFileBlock srrBlock = (SrrRarFileBlock)block;

                        if (rarName != srrBlock.FileName)
                        {
                            // we use flag 0x1 to mark files that have recovery records removed.  all other flags are currently undefined.
                            rebuildRecovery = (block.Flags & (ushort)SrrRarFileBlock.FlagValues.RecoveryBlocksRemoved) != 0;

                            rarName = srrBlock.FileName;
                            if (rarfs != null)
                            {
                                rarfs.Close();
                            }

                            string   fileName = savePaths ? srrBlock.FileName : srrBlock.FileName.Substring(srrBlock.FileName.LastIndexOf('/') + 1);
                            FileInfo fileInfo = new FileInfo(Path.Combine(outFolder.FullName, fileName));
                            if (CheckOverwrite(fileInfo.FullName))
                            {
                                Console.WriteLine("Re-creating RAR file: {0}", fileName);
                                fileInfo.Directory.Create();
                                rarfs = fileInfo.Create();
                            }
                            else
                            {
                                ReportError("Operation aborted.");
                                return(-1);
                            }
                        }
                    }
                    else if (block is RarRecoveryBlock || block is RarOldRecoveryBlock)
                    {
                        // either the recovery block or the newsub block is used for recovery record data.  it consists of two parts: crc's and recovery sectors.
                        //  all file data preceding the recovery record block is protected by the recovery record.  that data is broken into sectors of 512 bytes.
                        //  the crc portion of the recovery block is the 2 low-order bytes of the crc32 value for each sector (2 bytes * protected sector count)
                        //  the recovery sectors are created by breaking the data into slices based on the recovery sector count. (512 bytes * recovery sector count)
                        //  each slice will get one parity sector created by xor-ing the corresponding bytes from all other sectors in the slice.
                        uint  recoverySectors  = 0;
                        ulong protectedSectors = 0;

                        if (block is RarRecoveryBlock)
                        {
                            RarRecoveryBlock subData = (RarRecoveryBlock)block;
                            recoverySectors  = subData.RecoverySectors;
                            protectedSectors = subData.DataSectors;
                        }
                        else
                        {
                            RarOldRecoveryBlock recData = (RarOldRecoveryBlock)block;
                            recoverySectors  = recData.RecoverySectors;
                            protectedSectors = recData.DataSectors;
                        }

                        if (recoverySectors > 0 && rebuildRecovery)
                        {
                            if (verbose)
                            {
                                Console.WriteLine("\t\tCRC entries to rebuild: {0:n0}", protectedSectors);
                                Console.WriteLine("\t\tRecovery sectors to rebuild: {0:n0}", recoverySectors);
                            }

                            byte[]   crc = new byte[protectedSectors * 2];
                            byte[][] rr  = new byte[recoverySectors][];
                            for (int i = 0; i < recoverySectors; i++)
                            {
                                rr[i] = new byte[512];
                            }

                            int  rrSlice       = 0;
                            long currentSector = 0;
                            long rarPos        = rarfs.Position;

                            byte[] sector = new byte[512];
                            rarfs.Position = 0;
                            while (rarfs.Position < rarPos)
                            {
                                // read data 1 sector at a time.  pad the last sector with 0's
                                if (rarPos - rarfs.Position >= 512)
                                {
                                    rarfs.Read(sector, 0, 512);
                                }
                                else
                                {
                                    long pos = rarfs.Position;
                                    rarfs.Read(sector, 0, (int)(rarPos - pos));
                                    for (int i = (int)(rarPos - pos); i < 512; i++)
                                    {
                                        sector[i] = 0;
                                    }
                                }

                                // calculate the crc32 for the sector and store the 2 low-order bytes
                                ushort sectorCrc = (ushort)(Crc32.GetCrc(sector) & 0xffff);
                                crc[currentSector * 2]     = (byte)(sectorCrc & 0xff);
                                crc[currentSector * 2 + 1] = (byte)((sectorCrc >> 8) & 0xff);
                                currentSector++;

                                // update the recovery sector parity data for this slice
                                for (int i = 0; i < 512; i++)
                                {
                                    rr[rrSlice][i] ^= sector[i];
                                }

                                if (++rrSlice % recoverySectors == 0)
                                {
                                    rrSlice = 0;
                                }
                            }

                            // write the backed-up block header, crc data, and recovery sectors
                            rarfs.Write(block.RawData, 0, block.RawData.Length);
                            rarfs.Write(crc, 0, crc.Length);
                            foreach (byte[] ba in rr)
                            {
                                rarfs.Write(ba, 0, ba.Length);
                            }
                        }
                        else
                        {
                            // block is from a previous ReScene version or is not a recovery record.  just copy it
                            rarfs.Write(block.RawData, 0, block.RawData.Length);
                        }
                    }
                    else if (block is RarPackedFileBlock)
                    {
                        // this is the main RAR block we treat differently.  We removed the data when storing it, so we need to get the data back from the extracted file
                        RarPackedFileBlock fileData = (RarPackedFileBlock)block;

                        if (verbose)
                        {
                            Console.WriteLine("\t\tPacked Data Size: {0:n0}", fileData.PackedSize);
                            Console.WriteLine("\t\tFile Name: {0}", fileData.FileName);
                        }

                        // write the block contents from the .srr file
                        rarfs.Write(block.RawData, 0, block.RawData.Length);

                        if (fileData.PackedSize > 0)
                        {
                            // make sure we have the correct extracted file open.  if not, attempt to locate and open it
                            if (srcName != fileData.FileName)
                            {
                                srcName    = fileData.FileName;
                                runningCrc = Crc32.StartValue;
                                if (srcfs != null)
                                {
                                    srcfs.Close();
                                }

                                FileInfo srcInfo = new FileInfo(Path.Combine(inFolder.FullName, srcName));
                                if (hints.ContainsKey(srcName.ToLower()))
                                {
                                    srcInfo = new FileInfo(Path.Combine(inFolder.FullName, hints[srcName.ToLower()]));
                                }

                                if (!srcInfo.Exists)
                                {
                                    ReportError(string.Format("Could not locate data file: {0}", srcInfo.FullName));

                                    if (autoLocateRenamed)
                                    {
                                        foreach (FileInfo altInfo in inFolder.GetFiles(string.Format("*{0}", Path.GetExtension(fileData.FileName))))
                                        {
                                            if ((ulong)altInfo.Length == fileData.UnpackedSize)
                                            {
                                                ReportError(string.Format("Attempting to substitute file: {0}", altInfo.FullName));
                                                srcInfo = altInfo;
                                                break;
                                            }
                                        }
                                    }

                                    if (!srcInfo.Exists)
                                    {
                                        return(4);
                                    }
                                }
                                if ((ulong)srcInfo.Length != fileData.UnpackedSize)
                                {
                                    ReportError(string.Format("Data file is not the correct size: {0}\n\tFound: {1:n0}\n\tExpected: {2:n0}", srcInfo.FullName, srcInfo.Length, fileData.UnpackedSize));
                                    return(5);
                                }

                                srcfs = srcInfo.OpenRead();
                            }

                            // then grab the correct amount of data from the extracted file
                            int  bytesCopied = 0;
                            uint fileCrc     = Crc32.StartValue;
                            while (bytesCopied < (int)fileData.PackedSize)
                            {
                                int bytesToCopy = (int)fileData.PackedSize - bytesCopied;
                                if (bytesToCopy > copyBuff.Length)
                                {
                                    bytesToCopy = copyBuff.Length;
                                }

                                int bytesRead = srcfs.Read(copyBuff, 0, bytesToCopy);
                                rarfs.Write(copyBuff, 0, bytesRead);

                                if (!skipRarCrc)
                                {
                                    runningCrc = Crc32.GetCrc(runningCrc, copyBuff, 0, bytesRead);
                                    fileCrc    = Crc32.GetCrc(fileCrc, copyBuff, 0, bytesRead);
                                }

                                // if the file didn't have as many bytes as we needed, this file record was padded.  add null bytes to correct length
                                if (bytesRead != bytesToCopy)
                                {
                                    Array.Clear(copyBuff, 0, copyBuff.Length);
                                    rarfs.Write(copyBuff, 0, bytesToCopy - bytesRead);
                                }

                                bytesCopied += bytesToCopy;
                            }

                            if (!skipRarCrc)
                            {
                                if ((fileData.Flags & (ushort)RarPackedFileBlock.FlagValues.SplitAfter) != 0 && fileData.FileCrc != ~fileCrc)
                                {
                                    ReportError(string.Format("CRC mismatch in file: {0}", rarName));
                                }
                                else if ((fileData.Flags & (ushort)RarPackedFileBlock.FlagValues.SplitAfter) == 0 && fileData.FileCrc != ~runningCrc)
                                {
                                    ReportError(string.Format("CRC mismatch in file: {0}", fileData.FileName));
                                }
                            }
                        }
                    }
                    else if (block.RawType >= (byte)RarBlockType.RarMin && block.RawType <= (byte)RarBlockType.RarMax || block.RawType == 0x00)
                    {
                        // -> P0W4 cleared RAR archive end block: almost all zeros except for the header length field
                        // copy any other rar blocks to the destination unmodified
                        rarfs.Write(block.RawData, 0, block.RawData.Length);
                    }
                    else
                    {
                        ReportError(string.Format("Warning: Unknown block type ({0:X2}) encountered in SRR file, consisting of {1:n0} bytes.  This block will be skipped.", block.RawType, block.RawData.Length));
                    }
                }
            }

            if (rarfs != null)
            {
                rarfs.Close();
            }

            Console.WriteLine("\nRelease successfully reconstructed.  Please re-check files against the SFV to verify before using.");

            return(0);
        }
コード例 #2
0
ファイル: srr.cs プロジェクト: zc0nf/pyrescene
        static int CreateReconstructionFile(List <FileInfo> inFiles, DirectoryInfo inFolder, List <string> storeFiles, string srrName, bool savePaths)
        {
            using (FileStream srrfs = new FileStream(srrName, FileMode.Create))
            {
                BinaryWriter bw = new BinaryWriter(srrfs, Encoding.ASCII);
                bw.Write(new SrrHeaderBlock(appName).RawData);

                // we store copies of any files included in the storeFiles list in the .srr using a "store block".  any SFV files used are also included.
                foreach (FileInfo fi in inFiles)
                {
                    if (fi.Extension.ToLower() == ".sfv")
                    {
                        storeFiles.Add(fi.FullName);
                    }
                }

                SrrStoredFileBlock storeBlock = null;
                foreach (string fileName in storeFiles)
                {
                    string searchName = fileName;
                    if (!Path.IsPathRooted(searchName))
                    {
                        searchName = Path.Combine(inFolder.FullName, fileName);
                    }

                    DirectoryInfo searchDir = new DirectoryInfo(Path.GetDirectoryName(searchName));
                    if (searchDir.Exists)
                    {
                        foreach (FileInfo storeFile in searchDir.GetFiles(Path.GetFileName(searchName)))
                        {
                            string fName = savePaths ? MakePathRelative(storeFile.FullName, inFolder.FullName) : storeFile.Name;
                            Console.WriteLine("Storing file: {0}", fName);
                            storeBlock = new SrrStoredFileBlock(fName, (int)storeFile.Length);
                            if (savePaths)
                            {
                                storeBlock.Flags |= (ushort)SrrStoredFileBlock.FlagValues.PathsSaved;
                            }
                            using (FileStream storefs = storeFile.OpenRead())
                                storefs.Read(storeBlock.RawData, storeBlock.FileOffset, (int)storeFile.Length);

                            bw.Write(storeBlock.RawData);
                        }
                    }
                }

                List <string> rarFiles = new List <string>();
                foreach (FileInfo inFile in inFiles)
                {
                    if (inFile.Extension.ToLower() == ".sfv")
                    {
                        using (SfvReader sfvReader = new SfvReader(inFile.FullName))
                        {
                            List <string> sfvRarFiles = new List <string>();
                            SfvEntry      sfvEntry;
                            while ((sfvEntry = sfvReader.Read()) != null)
                            {
                                if (RarFileNameComparer.IsRarFile(sfvEntry.FileName))
                                {
                                    sfvRarFiles.Add(Path.Combine(inFile.DirectoryName, sfvEntry.FileName));
                                }
                                else
                                {
                                    ReportError(string.Format("Warning: Non-RAR file referenced in SFV: {0}\n\tThis file cannot be recreated unless it is stored using -s", sfvEntry.FileName));
                                    continue;
                                }
                            }

                            sfvRarFiles.Sort(new RarFileNameComparer());
                            rarFiles.AddRange(sfvRarFiles);
                        }
                    }
                    else
                    {
                        bool     oldNameFormat = false;
                        RarBlock block         = null;
                        using (RarReader rdr = new RarReader(inFile.FullName, RarReadMode.RAR))
                            while ((block = rdr.Read()) != null)
                            {
                                if (block is RarVolumeHeaderBlock && ((block.Flags & (ushort)RarVolumeHeaderBlock.FlagValues.Volume) != 0))
                                {
                                    if ((block.Flags & (ushort)RarVolumeHeaderBlock.FlagValues.FirstVolume) == 0)
                                    {
                                        throw new InvalidDataException("You must start with the first volume from a RAR set");
                                    }

                                    oldNameFormat = (block.Flags & (ushort)RarVolumeHeaderBlock.FlagValues.NewNumbering) == 0;
                                }
                            }

                        string nextFileName = inFile.FullName;
                        while (File.Exists(nextFileName))
                        {
                            rarFiles.Add(Path.Combine(inFile.DirectoryName, nextFileName));
                            nextFileName = RarFileNameFinder.FindNextFileName(nextFileName, oldNameFormat);
                        }
                    }
                }

                foreach (string fileName in rarFiles)
                {
                    if (!File.Exists(fileName))
                    {
                        ReportError(string.Format("Referenced file not found: {0}", fileName));
                        srrfs.Close();
                        File.Delete(srrName);
                        return(2);
                    }

                    string fName = savePaths ? MakePathRelative(fileName, inFolder.FullName) : Path.GetFileName(fileName);
                    Console.WriteLine("Processing file: {0}", fName);

                    SrrRarFileBlock rarBlock = new SrrRarFileBlock(fName);
                    if (savePaths)
                    {
                        rarBlock.Flags |= (ushort)SrrRarFileBlock.FlagValues.PathsSaved;
                    }

                    bw.Write(rarBlock.RawData);
                    using (RarReader rarReader = new RarReader(fileName, RarReadMode.RAR))
                    {
                        RarBlock block;
                        while ((block = rarReader.Read()) != null)
                        {
                            if (verbose)
                            {
                                Console.WriteLine("\tBlock Type: 0x{0:x2}", block.RawType);
                                Console.WriteLine("\tBlock Size: {0}", block.RawData.Length);
                            }

                            if (block is RarPackedFileBlock)
                            {
                                RarPackedFileBlock fileData = (RarPackedFileBlock)block;

                                if (verbose)
                                {
                                    Console.WriteLine("\t\tCompression Type: 0x{0:x2}", fileData.CompressionMethod);
                                    Console.WriteLine("\t\tPacked Data Size: {0:n0}", fileData.PackedSize);
                                    Console.WriteLine("\t\tFile Size: {0:n0}", fileData.UnpackedSize);
                                    Console.WriteLine("\t\tFile Name: {0}", fileData.FileName);
                                }

                                if (fileData.CompressionMethod != 0x30)
                                {
                                    ReportError(string.Format("Archive uses unsupported compression method: {0}", fileName));
                                    srrfs.Close();
                                    File.Delete(srrName);
                                    return(3);
                                }
                            }
                            else if (block is RarRecoveryBlock)
                            {
                                RarRecoveryBlock subData = (RarRecoveryBlock)block;

                                if (verbose & subData.RecoverySectors > 0)
                                {
                                    Console.WriteLine("\t\tRecovery Record Size: {0:n0}", subData.PackedSize);
                                    Console.WriteLine("\t\tRecovery Sectors: {0:n0}", subData.RecoverySectors);
                                    Console.WriteLine("\t\tProtected Sectors: {0:n0}", subData.DataSectors);
                                }
                            }

                            // store the raw data for any blocks found
                            bw.Write(block.RawData);
                        }
                    }
                }
            }

            Console.WriteLine("\nReconstruction file successfully created: {0}", srrName);

            return(0);
        }