/// <summary>
        /// Parses a Sonic Heroes/Shadow ONE Archive from the supplied byte array and returns
        /// you an instance of Archive.
        /// </summary>
        /// <returns></returns>
        public static unsafe Archive FromONEFile(ref byte[] oneArchive)
        {
            // Know if we're dealing with Shadow050 or Shadow060
            ONEArchiveType archiveType = ONEArchiveTester.GetArchiveType(ref oneArchive);

            if (archiveType == ONEArchiveType.Heroes)
            {
                return(new ONEArchive(ref oneArchive).GetArchive());
            }
            else
            {
                return(new ONEShadowArchive(ref oneArchive).GetArchive());
            }
        }
Exemple #2
0
        /// <summary>
        /// Used to conditionally display a warning on opening Shadow The Hedgehog .ONE files pleading the user
        /// to retain the file order.
        /// </summary>
        /// <param name="oneArchive">Byte array containing a .ONE File.</param>
        private void CheckShadowWarning(ref byte[] oneArchive)
        {
            // Know if we're dealing with Shadow050 or Shadow060
            ONEArchiveType archiveType = ONEArchiveTester.GetArchiveType(ref oneArchive);

            if (archiveType == ONEArchiveType.Shadow050 || archiveType == ONEArchiveType.Shadow060 && _openedShadowArchive == false)
            {
                if (Properties.Settings.Default.HideWarnings == false)
                {
                    _openedShadowArchive = true;
                    MessageBox.Show("Note: You are opening a Shadow The Hedgehog Archive.\n\n" +
                                    "For some of the .ONE files (such as shadow.one), Shadow The Hedgehog seems to expect a strict file order.\n\n" +
                                    "It is highly recommended you either use the Replace button or reimport the files in the same order as the original when creating new archives and reimporting.");
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Tries to check the type of .ONE file that the supplied byte array is
        /// and sets the checkbox hint beside the save buttons appropriately.
        /// </summary>
        /// <param name="oneFile"></param>
        private void SetCheckboxHint(ref byte[] oneFile)
        {
            // Update toolstrip save button hint.
            var archiveType = ONEArchiveTester.GetArchiveType(ref oneFile);

            saveShadow050ToolStripMenuItem.Checked = false;
            saveShadow060ToolStripMenuItem.Checked = false;
            saveToolStripMenuItem.Checked          = false;

            switch (archiveType)
            {
            case ONEArchiveType.Heroes:
                saveToolStripMenuItem.Checked = true;
                break;

            case ONEArchiveType.Shadow050:
                saveShadow050ToolStripMenuItem.Checked = true;
                break;

            case ONEArchiveType.Shadow060:
                saveShadow060ToolStripMenuItem.Checked = true;
                break;
            }
        }
        /// <summary>
        /// Parses a Shadow The Hedgehog ONE file from a byte array and returns a ONE file structure back.
        /// </summary>
        /// <returns>The structure of a ONE Archive.</returns>
        // ReSharper disable once InconsistentNaming
        public static ONEShadowArchive ParseONEFile(ref byte[] file)
        {
            // Know if we're dealing with Shadow050 or Shadow060
            ONEArchiveType archiveType = ONEArchiveTester.GetArchiveType(ref file);

            // Do not accept Heroes archives.
            if (archiveType == ONEArchiveType.Heroes)
            {
                throw new ArgumentException("The supplied .ONE file does not appear to be a Shadow The Hedgehog .ONE file");
            }

            // Instantiate either a Shadow 050 or Shadow 060 archive.
            ONEShadowArchive oneShadowArchive = new ONEShadowArchive();

            // Pointer for our file.
            int pointer = 0;

            oneShadowArchive.FileHeader = StructUtilities.ArrayToStructureUnsafe <ONEHeader>(ref file, pointer, ref pointer);
            oneShadowArchive.OneVersion = StructUtilities.ArrayToStructureUnsafe <ONEFileVersion>(ref file, pointer, ref pointer);
            oneShadowArchive.FileCount  = StructUtilities.ArrayToStructureUnsafe <int>(ref file, pointer, ref pointer);
            oneShadowArchive.OnePadding = StructUtilities.ArrayToStructureUnsafe <ONEPadding>(ref file, pointer, ref pointer);

            // Populate the file list.
            oneShadowArchive.Files = new List <IFileEntry>();

            // Populate the individual files.
            // Here we will filter out our dummies by
            for (int x = 0; x < oneShadowArchive.FileCount + 2; x++)
            {
                if (archiveType == ONEArchiveType.Shadow050)
                {
                    ONE50FileEntry entry = StructUtilities.ArrayToStructureUnsafe <ONE50FileEntry>(ref file, pointer, ref pointer);

                    if (entry.EndOfBlock == 1)
                    {
                        oneShadowArchive.Files.Add(entry);
                    }
                }
                else
                {
                    ONE60FileEntry entry = StructUtilities.ArrayToStructureUnsafe <ONE60FileEntry>(ref file, pointer, ref pointer);

                    if (entry.EndOfBlock == 1)
                    {
                        oneShadowArchive.Files.Add(entry);
                    }
                }
            }

            // Get individual file data.
            oneShadowArchive.FileData = new List <byte[]>(oneShadowArchive.FileCount);

            /*
             *  From observation we can deduce that the first 0xC length header present at the start of the file
             *  is not technically part of the .ONE Archive structure itself for any of the .ONE variations but is
             *  instead, metadata preprended to each of the files once they have been generated. The first always empty
             *  integer is probably simply acting as a reserved value.
             *
             *  Thus all of the offsets present inside the ONE Files are actually incorrect as they are read here due to this
             *  and offset from the true location inside the actual file by the length of the header in question.
             *
             *  We must take this into consideration when obtaining the length of the last file and the offset of each of the individual files.
             */
            int headerSize   = Marshal.SizeOf <ONEHeader>();
            int trueFileSize = file.Length - headerSize;

            for (int x = 0; x < oneShadowArchive.Files.Count; x++)
            {
                // Define our locals
                int    length;
                int    offset;
                byte[] compressedData;

                // Determine our locals.
                if (x != oneShadowArchive.Files.Count - 1)
                {
                    length = oneShadowArchive.Files[x + 1].FileOffset - oneShadowArchive.Files[x].FileOffset;
                }
                else
                {
                    length = trueFileSize - oneShadowArchive.Files[x].FileOffset;
                }                                                                      // Last file needs to count from the end of file.

                // Set our offset.
                offset = oneShadowArchive.Files[x].FileOffset + headerSize;

                // Create managed byte array and copy into it.
                compressedData = new byte[length];
                Array.Copy(file, offset, compressedData, 0, length);

                // We're done here.
                oneShadowArchive.FileData.Add(compressedData);
            }

            return(oneShadowArchive);
        }