Example #1
0
        public Stream OpenFile(CFFILE entry)
        {
            if (decompressed == null)
            {
                if (!DecompressFolder())
                {
                    return(null);
                }
            }

            return(new WindowedStream(decompressed, entry.uoffFolderStart, entry.cbFile));
        }
Example #2
0
        public bool GetEntry(string fileName, ref CFFILE entry, bool caseSensitive = true)
        {
            foreach (var pair in Entries)
            {
                if (caseSensitive)
                {
                    if (fileName == pair.Item2)
                    {
                        entry = pair.Item1;
                        return(true);
                    }
                }
                else
                {
                    if (fileName.ToLower() == pair.Item2.ToLower())
                    {
                        entry = pair.Item1;
                        return(true);
                    }
                }
            }

            return(false);
        }
Example #3
0
        public bool Read()
        {
            reader.BaseStream.Position = 0;
            var magic = reader.ReadUInt16();

            if (magic != 0x5a4d && magic != 0x4d5a)
            {
                Console.WriteLine("Error: recovery isn't proper EXE file!");
                return(false);
            }

            Console.WriteLine("Scanning EXE...");
            cabHeaderPos = new List <long>();

            // Track position/length ourselves instead of needing to call stream accessors
            long position = reader.BaseStream.Position;
            long length   = reader.BaseStream.Length;

            // Fast pattern search via sliding-window, kinda
            // From https://codereview.stackexchange.com/questions/202235/finding-specific-small-byte-arrays-in-large-binary-files
            ulong pattern = ((ulong)0x4643534D).EndianSwap();
            ulong view    = 0;
            long  viewed  = 0;

            while (position + 8 < length)
            {
                view = (view << 8) | reader.ReadByte(); // shift-in next byte
                position++;
                viewed++;
                if (view == pattern && viewed >= 8) // make sure we already got at least 4 bytes
                {
                    cabHeaderPos.Add(position - 8);
                }
            }

            if (cabHeaderPos.Count < 2)
            {
                Console.WriteLine("Error: couldn't find required CAB files inside recovery!");
                return(false);
            }

            string[] csv = null;

            // Read the meta cab in the exe, contains some info about the other cabs
            // Usually the second cab in the file, but we'll try the third cabinet in the file if it exists, and use it if it contains manifest.csv
            // (because second cabinet inside xbox OG SDKs seems to be corrupt or something, with the data actually being in the third one, odd)

            var metaIdx = cabHeaderPos.Count > 2 ? 2 : 1;

            while (cabHeaderPos.Count > 1)
            {
                var thisIdx = metaIdx;
                reader.BaseStream.Position = cabHeaderPos[metaIdx];
                metaIdx = 1;

                // Try reading metacab
                var metaCab = new CabFile(reader.BaseStream);
                if (!metaCab.Read())
                {
                    continue;
                }

                // Read the manifest file...
                var manifest = new CFFILE();
                if (!metaCab.GetEntry("manifest.csv", ref manifest, false))
                {
                    continue;
                }

                // Remove the meta cab from cab header list...
                cabHeaderPos.RemoveAt(thisIdx);

                Stream manifestStream = null;
                try
                {
                    manifestStream = metaCab.OpenFile(manifest);
                    if (manifestStream == null)
                    {
                        continue;
                    }
                }
                catch
                {
                    continue;
                }

                using (var reader2 = new BinaryReader(manifestStream))
                {
                    byte[] data = reader2.ReadBytes((int)reader2.BaseStream.Length);
                    var    str  = Encoding.ASCII.GetString(data);
                    csv = str.Replace("\r\n", "\n").Split(new char[] { '\n' });
                }
                break;
            }

            if (csv == null)
            {
                Console.WriteLine("Error: failed to read manifest.csv from meta-cab section!");
                return(false);
            }

            Variants = new List <string>();
            Entries  = new List <ManifestEntry>();
            foreach (var line in csv)
            {
                if (string.IsNullOrEmpty(line))
                {
                    continue;
                }

                var parts = new List <string>(line.Split(new char[] { ',' }));
                if (parts.Count < 6)
                {
                    continue;
                }

                // Older SDKs don't have a variant section, so we'll insert one
                if (parts[1] == "file" || parts[1] == "copy" || parts[1] == "sharedfile" || parts[1] == "backupsharedfile" || parts[1] == "singlefile")
                {
                    parts.Insert(1, "");
                }

                if (parts[2] != "file" && parts[2] != "copy" && parts[2] != "sharedfile" && parts[2] != "backupsharedfile" && parts[2] != "singlefile")
                {
                    continue;
                }

                var entry = new ManifestEntry();
                entry.LangID       = parts[0];
                entry.Variant      = parts[1];
                entry.Action       = parts[2];
                entry.BasePath     = parts[3];
                entry.FilePath     = parts[4];
                entry.CopyDestPath = parts[5];

                if (string.IsNullOrEmpty(entry.Variant))
                {
                    entry.Variant = "_All";
                }

                Entries.Add(entry);

                if (!Variants.Contains(entry.Variant))
                {
                    Variants.Add(entry.Variant);
                }
            }

            Console.WriteLine();
            Console.WriteLine($"EXE contents:");
            Console.WriteLine($"{Entries.Count} file{(Entries.Count == 1 ? "" : "s")}");
            Console.WriteLine($"{Variants.Count} variant{(Variants.Count == 1 ? "" : "s")}:");
            foreach (var variant in Variants)
            {
                int numFiles = 0;
                foreach (var entry in Entries)
                {
                    if (entry.Variant == variant)
                    {
                        numFiles++;
                    }
                }

                Console.WriteLine($"  - {variant} ({numFiles} file{(numFiles == 1 ? "" : "s")})");
            }

            Console.WriteLine();

            // TODO: devices (need to read settings.ini from metaCab)

            return(true);
        }