示例#1
0
        private int GetDirStructureRecursive(int curIndex, List <FSTEntry> FST, FSTEntry parentFST, VirtualFilesystemDirectory parentDir, EndianBinaryReader image)
        {
            FSTEntry curEntry = FST[curIndex];

            if (curEntry.Type == FSTNodeType.Directory)
            {
                VirtualFilesystemDirectory dir = new VirtualFilesystemDirectory(curEntry.RelativeFileName);

                Console.WriteLine("Created directory: " + dir.Name);

                while (curIndex < curEntry.FileSizeNextDirIndex - 1)
                {
                    curIndex = GetDirStructureRecursive(curIndex + 1, FST, curEntry, dir, image);
                }

                parentDir.Children.Add(dir);

                Console.WriteLine("Leaving directory: " + dir.Name);

                return(curIndex);
            }
            else
            {
                VirtualFilesystemFile file = GetFileData(curEntry, image);
                parentDir.Children.Add(file);

                return(curIndex);
            }
        }
示例#2
0
        private void PathExport(string path)
        {
            VirtualFilesystemDirectory rootDir = new VirtualFilesystemDirectory("archive");

            // Write text
            MemoryStream       bmgFile   = new MemoryStream();
            EndianBinaryWriter bmgWriter = new EndianBinaryWriter(bmgFile, Endian.Big);

            LoadedTextFile.Export(bmgWriter);
            VirtualFilesystemFile bmg = new VirtualFilesystemFile("zel_00", ".bmg", new VirtualFileContents(bmgFile.ToArray()));

            rootDir.Children.Add(bmg);

            // Write color
            MemoryStream       bmcFile   = new MemoryStream();
            EndianBinaryWriter bmcWriter = new EndianBinaryWriter(bmcFile, Endian.Big);

            LoadedColorFile.Export(bmcWriter);
            VirtualFilesystemFile bmc = new VirtualFilesystemFile("color", ".bmc", new VirtualFileContents(bmcFile.ToArray()));

            rootDir.Children.Add(bmc);

            if (m_isCompressed)
            {
                WArchiveTools.ArchiveUtilities.WriteArchive(path, rootDir, WArchiveTools.ArchiveCompression.Yaz0);
            }
            else
            {
                WArchiveTools.ArchiveUtilities.WriteArchive(path, rootDir);
            }
        }
示例#3
0
        private void PathExport(string path)
        {
            /*
             * VirtualFilesystemDirectory rootDir = new VirtualFilesystemDirectory("archive");
             *
             * // Write text
             * MemoryStream bmgFile = new MemoryStream();
             * EndianBinaryWriter bmgWriter = new EndianBinaryWriter(bmgFile, Endian.Big);
             * LoadedTextFile.Export(bmgWriter);
             * VirtualFilesystemFile bmg = new VirtualFilesystemFile("zel_00", ".bmg", new VirtualFileContents(bmgFile.ToArray()));
             * rootDir.Children.Add(bmg);
             *
             * // Write color
             * MemoryStream bmcFile = new MemoryStream();
             * EndianBinaryWriter bmcWriter = new EndianBinaryWriter(bmcFile, Endian.Big);
             * LoadedColorFile.Export(bmcWriter);
             * VirtualFilesystemFile bmc = new VirtualFilesystemFile("color", ".bmc", new VirtualFileContents(bmcFile.ToArray()));
             * rootDir.Children.Add(bmc);
             */

            VirtualFilesystemDirectory rootDir = new VirtualFilesystemDirectory("archive");

            foreach (VirtualFilesystemNode node in m_loadedDirRoot.Children)
            {
                if (node is VirtualFilesystemFile)
                {
                    VirtualFilesystemFile oldFile       = node as VirtualFilesystemFile;
                    MemoryStream          newFileData   = new MemoryStream();
                    EndianBinaryWriter    newFileWriter = new EndianBinaryWriter(newFileData, Endian.Big);

                    if (oldFile.Extension == ".bmg")
                    {
                        LoadedTextFile.Export(newFileWriter);
                    }
                    else if (oldFile.Extension == ".bmc")
                    {
                        LoadedColorFile.Export(newFileWriter);
                    }
                    else
                    {
                        rootDir.Children.Add(oldFile);
                        continue;
                    }

                    VirtualFilesystemFile newFile = new VirtualFilesystemFile(oldFile.Name, oldFile.Extension, newFileData.ToArray());
                    rootDir.Children.Add(newFile);
                }
            }

            if (m_isCompressed)
            {
                WArchiveTools.ArchiveUtilities.WriteArchive(path, rootDir, WArchiveTools.ArchiveCompression.Yaz0);
            }
            else
            {
                WArchiveTools.ArchiveUtilities.WriteArchive(path, rootDir);
            }
        }
示例#4
0
        private void DoOutputPrep(VirtualFilesystemNode vfsNode, List <FSTEntry> outputFST, List <byte> fstNameBank, EndianBinaryWriter writer, int curParentDirIndex)
        {
            FSTEntry fstEnt = new FSTEntry();

            if (vfsNode.Type == NodeType.Directory)
            {
                VirtualFilesystemDirectory dir = (VirtualFilesystemDirectory)vfsNode;

                fstEnt.Type           = FSTNodeType.Directory;
                fstEnt.FileNameOffset = fstNameBank.Count();

                fstNameBank.AddRange(Encoding.ASCII.GetBytes(dir.Name.ToCharArray()));
                fstNameBank.Add(0);

                fstEnt.FileOffsetParentDir = curParentDirIndex;
                curParentDirIndex          = outputFST.Count();

                int thisDirIndex = curParentDirIndex;

                outputFST.Add(fstEnt); //Placeholder for this dir

                foreach (VirtualFilesystemNode child in dir.Children)
                {
                    DoOutputPrep(child, outputFST, fstNameBank, writer, curParentDirIndex);
                }

                int dirEndIndex = outputFST.Count();
                fstEnt.FileSizeNextDirIndex = (dirEndIndex - thisDirIndex) + thisDirIndex;
                outputFST[thisDirIndex]     = fstEnt; //Add the actual entry after giving it the rest of the info
            }
            else
            {
                VirtualFilesystemFile file = (VirtualFilesystemFile)vfsNode;

                fstEnt.Type = FSTNodeType.File;
                fstEnt.FileSizeNextDirIndex = file.Data.Length;
                fstEnt.FileNameOffset       = fstNameBank.Count();

                fstNameBank.AddRange(Encoding.ASCII.GetBytes(file.Name.ToCharArray()));
                fstNameBank.AddRange(Encoding.ASCII.GetBytes(file.Extension.ToCharArray()));
                fstNameBank.Add((byte)0);

                writer.BaseStream.Position = (int)writer.BaseStream.Position + (32 - ((int)writer.BaseStream.Position % 32)) % 32;

                fstEnt.FileOffsetParentDir = (int)writer.BaseStream.Position;

                writer.Write(file.Data);

                for (int i = 0; i < ((32 - (file.Data.Length - 32)) % 32); i++)
                {
                    writer.Write((byte)0);
                }

                //Console.WriteLine("Wrote file: " + file.Name);

                outputFST.Add(fstEnt);
            }
        }
示例#5
0
        private void CreateFileFromArc(VirtualFilesystemFile file, string rootPath)
        {
            string filePath = $"{rootPath}\\{file.NameWithExtension}";

            using (FileStream stream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            {
                stream.Write(file.Data, 0, file.Data.Length);
            }
        }
示例#6
0
        private void Open()
        {
            OpenFileDialog m_openFile = new OpenFileDialog();

            m_openFile.FileName   = "bmgres";
            m_openFile.DefaultExt = ".arc";
            m_openFile.Filter     = "arc files (*.arc)|*.arc";

            if (m_openFile.ShowDialog() == true)
            {
                VirtualFilesystemDirectory sourceDir = WArchiveTools.ArchiveUtilities.LoadArchive(m_openFile.FileName);

                foreach (VirtualFilesystemNode node in sourceDir.Children)
                {
                    EndianBinaryReader reader;

                    if (node.Type == NodeType.File)
                    {
                        VirtualFilesystemFile file = node as VirtualFilesystemFile;

                        if (file.Extension == ".bmg")
                        {
                            if (IsDataLoaded)
                            {
                                CloseCurrentFile();
                            }

                            reader = new EndianBinaryReader(file.Data, Endian.Big);

                            LoadedTextFile = new BMG(reader);

                            m_listboxSelectedIndex = 0;
                            m_loadedFileName       = m_openFile.FileName;
                            WindowTitle            = m_loadedFileName;

                            ColViewSource        = new CollectionViewSource();
                            ColViewSource.Source = LoadedTextFile.MessageList;
                            SelectedMessage      = LoadedTextFile.MessageList[0];
                            IsDataLoaded         = true;

                            m_loadedDirRoot = sourceDir;
                        }

                        else if (file.Extension == ".bmc")
                        {
                            VirtualFilesystemFile bmcFile = node as VirtualFilesystemFile;

                            reader = new EndianBinaryReader(bmcFile.Data, Endian.Big);

                            LoadedColorFile = new BMC(reader);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Returns the file at the given path, if it exists. If the file does not exist, it will return null.
        /// </summary>
        /// <param name="root">ISO to search</param>
        /// <param name="filePath">File path to search for</param>
        /// <returns></returns>
        public static VirtualFilesystemFile FindFile(VirtualFilesystemDirectory root, string filePath)
        {
            VirtualFilesystemFile      result = null;
            VirtualFilesystemDirectory curDir = root;

            List <string> dividedPath = new List <string>(filePath.ToLower().Split('\\'));
            int           pathIndex   = 0;

            // For each element of the filepath, we'll look at the current directory's children,
            // starting with the root, and see if the name matches.
            // If we find a file, we'll check to see if its name and extension match the current
            // filepath element. If it does, we can break the loop and leave.
            // If not, we check if it's a directory instead, and if it is, we set curDir to it.
            while (pathIndex < dividedPath.Count)
            {
                for (int i = 0; i < curDir.Children.Count; i++)
                {
                    if (curDir.Children[i].Type == NodeType.File)
                    {
                        VirtualFilesystemFile cand   = curDir.Children[i] as VirtualFilesystemFile;
                        string fileNameWithExtension = cand.Name.ToLower() + cand.Extension.ToLower();

                        if (fileNameWithExtension == dividedPath[pathIndex])
                        {
                            result = cand;
                            break;
                        }
                    }
                    else if (curDir.Children[i].Name.ToLower() == dividedPath[pathIndex])
                    {
                        if (curDir.Children[i].Type == NodeType.Directory)
                        {
                            curDir = curDir.Children[i] as VirtualFilesystemDirectory;
                        }
                        break;
                    }
                }

                pathIndex++;
            }

            return(result);
        }
示例#8
0
        private void DumpDirsRecursive(VirtualFilesystemNode vfsObj, string root)
        {
            if (vfsObj.Type == NodeType.Directory)
            {
                VirtualFilesystemDirectory dir = (VirtualFilesystemDirectory)vfsObj;

                string testRoot = root + @"\" + dir.Name;

                Directory.CreateDirectory(testRoot);

                foreach (VirtualFilesystemNode child in dir.Children)
                {
                    if (child.Type == NodeType.Directory)
                    {
                        Directory.CreateDirectory(testRoot + @"\" + child.Name);

                        Console.WriteLine("Wrote directory: " + testRoot + @"\" + child.Name);

                        DumpDirsRecursive(child, testRoot);
                    }

                    else
                    {
                        DumpDirsRecursive(child, testRoot);
                    }
                }
            }

            else
            {
                VirtualFilesystemFile file = (VirtualFilesystemFile)vfsObj;

                using (FileStream stream = new FileStream(root + @"\" + file.Name + file.Extension, FileMode.Create))
                {
                    EndianBinaryWriter writer = new EndianBinaryWriter(stream, Endian.Big);

                    writer.Write(file.Data);

                    Console.WriteLine("Wrote file: " + root + @"\" + file.Name + file.Extension);
                }
            }
        }
        public static J3D LoadModelFromVFS(VirtualFilesystemDirectory fs, string path)
        {
            TSharedRef <J3D> existRef = null;//m_j3dList.Find(x => string.Compare(x.FilePath, arc_and_file_path, StringComparison.InvariantCultureIgnoreCase) == 0);

            J3D model = new J3D(path);

            VirtualFilesystemFile file = fs.GetFileAtPath(path);

            using (EndianBinaryReader reader = new EndianBinaryReader(file.Data, Endian.Big))
                model.LoadFromStream(reader);

            existRef          = new TSharedRef <J3D>();
            existRef.FilePath = fs.Name + '/' + path;
            existRef.Asset    = model;
            existRef.ReferenceCount++;

            m_j3dList.Add(existRef);

            return(model);
        }
示例#10
0
        private int GetFSTSkipValue(int curValue, VirtualFilesystemNode node)
        {
            if (node.Type == NodeType.Directory)
            {
                VirtualFilesystemDirectory dir = (VirtualFilesystemDirectory)node;
                curValue += (12 + dir.Name.Length + 1);

                foreach (VirtualFilesystemNode child in dir.Children)
                {
                    curValue = GetFSTSkipValue(curValue, child);
                }
            }
            else
            {
                VirtualFilesystemFile file = (VirtualFilesystemFile)node;
                curValue += (int)(12 + file.Name.Length + file.Extension.Length + 1);
            }

            return(curValue);
        }
示例#11
0
        private bool TryLoadMessageArchive()
        {
            string bmgres_path = Path.Combine(WSettingsManager.GetSettings().RootDirectoryPath, "files", "res", "Msg", "bmgres.arc");

            if (!File.Exists(bmgres_path))
            {
                return(false);
            }

            m_MessageArchive = ArchiveUtilities.LoadArchive(bmgres_path);

            VirtualFilesystemFile text_bank = m_MessageArchive.GetFileAtPath("zel_00.bmg");

            using (MemoryStream strm = new MemoryStream(text_bank.Data))
            {
                EndianBinaryReader reader = new EndianBinaryReader(strm, Endian.Big);
                LoadMessageData(reader);
            }

            return(true);
        }
示例#12
0
        private VirtualFilesystemFile GetFileData(FSTEntry fstData, EndianBinaryReader image)
        {
            string[] fileNameAndExtension = fstData.RelativeFileName.Split('.');
            image.BaseStream.Position = fstData.FileOffsetParentDir;
            byte[] data = image.ReadBytes((int)fstData.FileSizeNextDirIndex);

            VirtualFilesystemFile file;

            if (fileNameAndExtension.Length != 2)
            {
                file = new VirtualFilesystemFile(fileNameAndExtension[0], "", data);
            }

            else
            {
                file = new VirtualFilesystemFile(fileNameAndExtension[0], "." + fileNameAndExtension[1], data);
            }

            Console.WriteLine("Created file: " + file.Name);

            return(file);
        }
示例#13
0
        public override VirtualFilesystemDirectory ExportToVFS()
        {
            VirtualFilesystemDirectory new_dir = SourceDirectory;

            new_dir.Name = Name;

            VirtualFilesystemFile dzr_file = SourceDirectory.GetFileAtPath("dzr/room.dzr");

            using (MemoryStream mem = new MemoryStream())
            {
                using (EndianBinaryWriter writer = new EndianBinaryWriter(mem, Endian.Big))
                {
                    SceneDataExporter exporter = new SceneDataExporter();
                    exporter.ExportToStream(writer, this);

                    writer.Flush();

                    dzr_file.Data = mem.ToArray();
                }
            }

            return(new_dir);
        }
示例#14
0
        public override VirtualFilesystemDirectory ExportToVFS()
        {
            VirtualFilesystemDirectory new_dir = SourceDirectory;

            new_dir.Name = Name;

            VirtualFilesystemFile dzs_file = SourceDirectory.GetFileAtPath("dzs/stage.dzs");

            using (MemoryStream mem = new MemoryStream())
            {
                using (EndianBinaryWriter writer = new EndianBinaryWriter(mem, Endian.Big))
                {
                    SceneDataExporter exporter = new SceneDataExporter();
                    exporter.ExportToStream(writer, this);

                    writer.Flush();

                    dzs_file.Data = mem.ToArray();
                }
            }

            List <J3DNode> meshes = GetChildrenOfType <J3DNode>();

            for (int i = 0; i < meshes.Count; i++)
            {
                string modelExt = meshes[i].Model.StudioType == "bdl4" ? "bdl" : "bmd";

                VirtualFilesystemFile modelFile = SourceDirectory.GetFileAtPath($"{ modelExt }/{ meshes[i].Name }.{ modelExt }");
                byte[] data = File.ReadAllBytes(meshes[i].Filename);

                if (modelFile != null)
                {
                    modelFile.Data = data;
                }
                else
                {
                    VirtualFilesystemDirectory modelDir = null;

                    foreach (VirtualFilesystemNode n in new_dir.Children)
                    {
                        if (n.Name == modelExt)
                        {
                            modelDir = n as VirtualFilesystemDirectory;
                            break;
                        }
                    }

                    if (modelDir == null)
                    {
                        modelDir = new VirtualFilesystemDirectory(modelExt);
                        new_dir.Children.Add(modelDir);
                    }

                    modelDir.Children.Add(new VirtualFilesystemFile(meshes[i].Name, $".{ modelExt }", data));
                }
            }

            VirtualFilesystemFile dat_file  = SourceDirectory.GetFileAtPath("dat/event_list.dat");
            WEventList            eventlist = GetChildrenOfType <WEventList>()[0];

            using (MemoryStream ev_strm = new MemoryStream())
            {
                using (EndianBinaryWriter writer = new EndianBinaryWriter(ev_strm, Endian.Big))
                {
                    eventlist.ExportToStream(writer);
                    writer.Flush();

                    dat_file.Data = ev_strm.ToArray();
                }
            }

            return(new_dir);
        }
示例#15
0
        private void Open()
        {
            OpenFileDialog m_openFile = new OpenFileDialog();

            m_openFile.FileName   = "bmgres";
            m_openFile.DefaultExt = ".arc";
            m_openFile.Filter     = "arc files (*.arc)|*.arc";

            if (m_openFile.ShowDialog() == true)
            {
                VirtualFilesystemDirectory sourceDir = WArchiveTools.ArchiveUtilities.LoadArchive(m_openFile.FileName);

                foreach (VirtualFilesystemNode node in sourceDir.Children)
                {
                    EndianBinaryReader reader;

                    if (node.Name == "zel_00")
                    {
                        VirtualFilesystemFile bmgFile = node as VirtualFilesystemFile;

                        reader = new EndianBinaryReader(bmgFile.File.GetData(), Endian.Big);

                        LoadedTextFile = new BmgTextFile(reader);

                        m_listboxSelectedIndex = 0;

                        IsSearchByText = true;

                        m_loadedFileName = m_openFile.FileName;

                        WindowTitle = m_loadedFileName;

                        ColViewSource = new CollectionViewSource();

                        ColViewSource.Source = LoadedTextFile.MessageList;

                        SelectedMessage = LoadedTextFile.MessageList[0];

                        IsDataLoaded = true;
                    }

                    else if (node.Name == "color")
                    {
                        VirtualFilesystemFile bmcFile = node as VirtualFilesystemFile;

                        reader = new EndianBinaryReader(bmcFile.File.GetData(), Endian.Big);

                        LoadedColorFile = new BmcColorFile(reader);
                    }
                }

                /*
                 * RARC m_arc = new RARC(m_openFile.FileName);
                 *
                 * for (int i = 0; i < m_arc.Nodes.Count(); i++)
                 * {
                 *  for (int j = 0; j < m_arc.Nodes[i].Entries.Count(); j++)
                 *  {
                 *      if (m_arc.Nodes[i].Entries[j].Name.Contains(".bmg"))
                 *      {
                 *          LoadedTextFile = new BmgTextFile(new EndianBinaryReader(m_arc.Nodes[i].Entries[j].Data, Endian.Big));
                 *
                 *          m_listboxSelectedIndex = 0;
                 *
                 *          IsSearchByText = true;
                 *
                 *          m_loadedFileName = m_openFile.FileName;
                 *
                 *          ColViewSource = new CollectionViewSource();
                 *
                 *          ColViewSource.Source = LoadedTextFile.MessageList;
                 *
                 *          SelectedMessage = LoadedTextFile.MessageList[0];
                 *
                 *          IsDataLoaded = true;
                 *      }
                 *
                 *      if (m_arc.Nodes[i].Entries[j].Name.Contains(".bmc"))
                 *      {
                 *          LoadedColorFile = new BmcColorFile(new EndianBinaryReader(m_arc.Nodes[i].Entries[j].Data, Endian.Big));
                 *      }
                 *  }
                 * }*/
            }
        }
示例#16
0
        public void WriteISO(VirtualFilesystemDirectory root, string path)
        {
            using (FileStream output = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                EndianBinaryWriter writer = new EndianBinaryWriter(output, Endian.Big);

                List <byte>     fstNameBank           = new List <byte>();
                List <FSTEntry> outputFST             = new List <FSTEntry>();
                List <VirtualFilesystemFile> fileList = new List <VirtualFilesystemFile>();
                FSTEntry rootFST = new FSTEntry();

                int fstListOffset = 0;

                VirtualFilesystemDirectory sysDir = (VirtualFilesystemDirectory)root.Children[0];
                VirtualFilesystemFile      header = (VirtualFilesystemFile)sysDir.Children[0];
                writer.Write(header.Data);

                VirtualFilesystemFile apploader = (VirtualFilesystemFile)sysDir.Children[2];
                writer.Write(apploader.Data);

                VirtualFilesystemFile dol = (VirtualFilesystemFile)sysDir.Children[1];
                writer.Write(dol.Data);

                fstListOffset = (int)writer.BaseStream.Position;

                int fstSkipOffsetValue = 12;

                root.Children.RemoveAt(0);

                foreach (VirtualFilesystemNode node in root.Children)
                {
                    fstSkipOffsetValue = GetFSTSkipValue(fstSkipOffsetValue, node);
                }

                byte[] dummyFST = new byte[fstSkipOffsetValue];

                writer.Write(dummyFST);

                rootFST.Type = FSTNodeType.Directory;
                outputFST.Add(rootFST); //Placeholder FST entry for the root

                foreach (VirtualFilesystemNode node in root.Children)
                {
                    DoOutputPrep(node, outputFST, fstNameBank, writer, 0);
                }

                rootFST.FileSizeNextDirIndex = outputFST.Count();
                outputFST[0] = rootFST; //Add actual root FST entry

                writer.BaseStream.Position = fstListOffset;

                foreach (FSTEntry entry in outputFST)
                {
                    writer.Write((byte)entry.Type);
                    writer.Write((byte)0);
                    writer.Write((ushort)entry.FileNameOffset);
                    writer.Write(entry.FileOffsetParentDir);
                    writer.Write(entry.FileSizeNextDirIndex);
                }

                writer.Write(fstNameBank.ToArray());
            }
        }
示例#17
0
        public VirtualFilesystemDirectory LoadISO(EndianBinaryReader reader)
        {
            // This will hold the file system entries that we grab from the ISO.
            List <FSTEntry> FSTTest = new List <FSTEntry>();

            int FSTOffset = reader.ReadInt32At(0x424);

            reader.BaseStream.Position = FSTOffset;

            int numEntries        = reader.ReadInt32At(reader.BaseStream.Position + 8);
            int stringTableOffset = numEntries * 0xC; // Each FST entry is 12/0xC bytes long

            for (int i = 0; i < numEntries; i++)
            {
                FSTEntry fst = new FSTEntry();

                fst.Type = (FSTNodeType)reader.ReadByte();
                reader.SkipByte();                                                         // Padding

                int    curPos       = (int)reader.BaseStream.Position;                     // Save the current stream position
                ushort stringOffset = (ushort)reader.ReadInt16();                          // Get the offset to the entry's name

                reader.BaseStream.Position = stringOffset + stringTableOffset + FSTOffset; // Seek to entry's name

                string name = reader.ReadStringUntil('\0');
                fst.RelativeFileName = name;

                reader.BaseStream.Position = curPos + 2; // Return to where we were so that we can read additional data for this entry

                fst.FileOffsetParentDir  = reader.ReadInt32();
                fst.FileSizeNextDirIndex = reader.ReadInt32();

                FSTTest.Add(fst);
            }

            // Root of the ISO. The function will return this.
            VirtualFilesystemDirectory rootDir = new VirtualFilesystemDirectory("root");
            // Holds system data. DOL, apploader, etc. Named to provide compatibility with GCRebuilder
            VirtualFilesystemDirectory sysData = new VirtualFilesystemDirectory("&&systemdata");

            // Header data for the ISO
            byte[] headerData = reader.ReadBytesAt(0, 0x2440);
            sysData.Children.Add(new VirtualFilesystemFile("iso", ".hdr", headerData));

            int headerOffset = reader.ReadInt32At(0x420);

            // Executable data
            byte[] dolData = reader.ReadBytesAt(headerOffset, FSTOffset - headerOffset);
            sysData.Children.Add(new VirtualFilesystemFile("Start", ".dol", dolData));

            // Apploader
            byte[] appLoaderData = reader.ReadBytesAt(0x2440, headerOffset - 0x2440);
            sysData.Children.Add(new VirtualFilesystemFile("AppLoader", ".ldr", appLoaderData));

            // Table of contents (FST)
            byte[] fstData = reader.ReadBytesAt(FSTOffset, reader.ReadInt32At(0x428));
            sysData.Children.Add(new VirtualFilesystemFile("Game", ".toc", fstData));

            rootDir.Children.Add(sysData);

            int count = 1;

            while (count < numEntries)
            {
                if (FSTTest[count].Type == FSTNodeType.Directory)
                {
                    VirtualFilesystemDirectory dir = new VirtualFilesystemDirectory(FSTTest[count].RelativeFileName);
                    FSTEntry curEnt = FSTTest[count];

                    while (count < curEnt.FileSizeNextDirIndex - 1)
                    {
                        count = GetDirStructureRecursive(count + 1, FSTTest, FSTTest[count + 1], dir, reader);
                    }

                    rootDir.Children.Add(dir);
                }
                else
                {
                    VirtualFilesystemFile file = GetFileData(FSTTest[count], reader);
                    rootDir.Children.Add(file);
                }

                count += 1;
            }

            return(rootDir);
        }
示例#18
0
        private FileEntry[] GetDirDataRecursive(VirtualFilesystemDirectory rootDir, VirtualFilesystemDirectory parentDir)
        {
            List <FileEntry> dirFileEntries = new List <FileEntry>();

            FileEntry file;

            Node dirNode;

            // I'll admit this isn't ideal. If I'm looking at the native archives right, they tend
            // to follow the rule of "files first, directories second" when it comes to file entries.
            // Therefore, I'm going to set it up right now so that it will get files first, *then* directories.

            foreach (VirtualFilesystemNode node in rootDir.Children)
            {
                // We just need a file entry here
                if (node.Type == NodeType.File)
                {
                    VirtualFilesystemFile virtFile = node as VirtualFilesystemFile;

                    file = new FileEntry
                    {
                        ID           = (ushort)exportFileEntries.Count,
                        NameHashcode = HashName(virtFile.Name + virtFile.Extension),
                        Flags        = 0x11,
                        Name         = virtFile.Name + virtFile.Extension,
                        Data         = virtFile.Data
                    };

                    dirFileEntries.Add(file);
                }
            }

            foreach (VirtualFilesystemNode node in rootDir.Children)
            {
                // We need a node and a file entry here
                if (node.Type == NodeType.Directory)
                {
                    VirtualFilesystemDirectory virtDir = node as VirtualFilesystemDirectory;

                    dirNode = new Node
                    {
                        Type            = virtDir.Name.Substring(0, 3).ToUpper() + " ",
                        Name            = virtDir.Name,
                        NameHashcode    = HashName(virtDir.Name),
                        FirstFileOffset = (uint)exportFileEntries.Count
                    };

                    exportNodes.Add(dirNode);

                    file = new FileEntry
                    {
                        ID           = ushort.MaxValue,
                        NameHashcode = HashName(virtDir.Name),
                        Flags        = 0x02,
                        Name         = virtDir.Name,
                        Data         = new byte[] { (byte)(exportNodes.IndexOf(exportNodes.Find(i => i.Name == virtDir.Name))) },
                    };

                    dirFileEntries.Add(file);
                }
            }

            exportFileEntries.AddRange(dirFileEntries.ToArray());

            InsertDirOperatorEntries(rootDir, parentDir);

            // The recursive part. One more foreach!

            foreach (VirtualFilesystemNode node in rootDir.Children)
            {
                if (node.Type == NodeType.Directory)
                {
                    VirtualFilesystemDirectory dir = node as VirtualFilesystemDirectory;

                    Node tempNode = exportNodes.Find(i => i.Name == node.Name);

                    tempNode.Entries = GetDirDataRecursive(dir, rootDir);
                }
            }

            return(dirFileEntries.ToArray());
        }
示例#19
0
        private static J3D LoadModelFromResource(WActorResource.ModelResource res, string archive)
        {
            J3D j3d = null;

            if (string.IsNullOrEmpty(res.Path) || string.IsNullOrEmpty(archive))
            {
                return(null);
            }

            string archivePath = Path.Combine(WSettingsManager.GetSettings().RootDirectoryPath, "files", "res/Object/", archive + ".arc");

            if (!File.Exists(archivePath))
            {
                return(null);
            }

            VirtualFilesystemDirectory model_arc   = ArchiveUtilities.LoadArchive(archivePath);
            VirtualFilesystemFile      archiveFile = model_arc.GetFileAtPath(res.Path);

            if (archiveFile == null)
            {
                Console.WriteLine("LoadActorByName failed because the specified path \"{0}\" does not exist in archive \"{1}\"!", res.Path, archive);
                return(null);
            }

            byte[] j3dData = archiveFile.Data;

            j3d = new J3D(archiveFile.Name);
            using (EndianBinaryReader reader = new EndianBinaryReader(j3dData, Endian.Big))
                j3d.LoadFromStream(reader, WSettingsManager.GetSettings().DumpTextures, WSettingsManager.GetSettings().DumpShaders);

            if (res.Position != null)
            {
                j3d.SetOffsetTranslation((Vector3)res.Position);
            }
            if (res.Rotation != null)
            {
                j3d.SetOffsetRotation((Vector3)res.Rotation);
            }
            if (res.Scale != null)
            {
                j3d.SetOffsetScale((Vector3)res.Scale);
            }

            j3d.SetHardwareLight(0, m_mainLight);
            j3d.SetHardwareLight(1, m_secondaryLight);
            j3d.SetTextureOverride("ZBtoonEX", "resources/textures/ZBtoonEX.png");
            j3d.SetTextureOverride("ZAtoon", "resources/textures/ZAtoon.png");

            if (res.Animations == null)
            {
                res.Animations = new WActorResource.AnimationResource[0];
            }

            foreach (var anim in res.Animations)
            {
                VirtualFilesystemDirectory anim_arc = model_arc;

                if (!string.IsNullOrEmpty(anim.ArchiveName))
                {
                    string anim_arc_path = Path.Combine(WSettingsManager.GetSettings().RootDirectoryPath, "files", "res/Object/", anim.ArchiveName + ".arc");

                    if (!File.Exists(anim_arc_path))
                    {
                        return(null);
                    }

                    anim_arc = ArchiveUtilities.LoadArchive(anim_arc_path);
                }

                VirtualFilesystemFile anim_file = anim_arc.GetFileAtPath(anim.Path);

                if (anim_file == null)
                {
                    continue;
                }

                byte[] anim_data = anim_file.Data;

                // Decompress the file if necessary
                if (anim_data[0] == 'Y')
                {
                    MemoryStream decompressed_data = null;

                    using (EndianBinaryReader decompressor = new EndianBinaryReader(anim_data, Endian.Big))
                    {
                        decompressed_data = Yaz0.Decode(decompressor);
                    }

                    anim_data = decompressed_data.ToArray();
                }

                switch (anim.Type)
                {
                case "bck":
                    BCK loaded_bck = new BCK(anim_file.Name);
                    using (EndianBinaryReader reader = new EndianBinaryReader(anim_data, Endian.Big))
                        loaded_bck.LoadFromStream(reader);

                    j3d.BoneAnimations.Add(loaded_bck);
                    j3d.SetBoneAnimation(anim_file.Name);

                    loaded_bck.Tick(anim.StartTime);

                    if (anim.PausedOnLoad)
                    {
                        loaded_bck.Pause();
                    }
                    break;

                case "btk":
                    BTK loaded_btk = new BTK(anim_file.Name);
                    using (EndianBinaryReader reader = new EndianBinaryReader(anim_data, Endian.Big))
                        loaded_btk.LoadFromStream(reader);

                    j3d.MaterialAnimations.Add(loaded_btk);
                    j3d.SetMaterialAnimation(anim_file.Name);

                    loaded_btk.Tick(anim.StartTime);

                    if (anim.PausedOnLoad)
                    {
                        loaded_btk.Pause();
                    }
                    break;

                case "brk":
                    BRK loaded_brk = new BRK(anim_file.Name);
                    using (EndianBinaryReader reader = new EndianBinaryReader(anim_data, Endian.Big))
                        loaded_brk.LoadFromStream(reader);

                    j3d.RegisterAnimations.Add(loaded_brk);
                    j3d.SetRegisterAnimation(anim_file.Name);

                    loaded_brk.Tick(anim.StartTime);

                    if (anim.PausedOnLoad)
                    {
                        loaded_brk.Pause();
                    }
                    break;

                case "bmt":
                    BMT loaded_bmt = new BMT(anim_file.Name);
                    using (EndianBinaryReader reader = new EndianBinaryReader(anim_data, Endian.Big))
                        loaded_bmt.LoadFromStream(reader);

                    j3d.ExternalMaterials.Add(loaded_bmt);
                    j3d.SetExternalMaterial(anim_file.Name);

                    if (loaded_bmt.MAT3 != null)
                    {
                        // a hack to get bmts working
                        Material dummyMat = null;
                        j3d.AssignVertexAttributesToMaterialsRecursive(j3d.INF1Tag.HierarchyRoot, ref dummyMat, loaded_bmt.MAT3);
                        j3d.GenerateShadersForMaterials(loaded_bmt.MAT3);
                    }

                    break;

                default:
                    break;
                }
            }

            j3d.Tick(1 / (float)60);

            if (res.ChildModels == null)
            {
                res.ChildModels = new WActorResource.ModelResource[0];
            }

            foreach (var childRes in res.ChildModels)
            {
                var childJ3d = LoadModelFromResource(childRes, archive);
                j3d.AddChildModel(childJ3d, childRes.ParentJointName);
            }

            return(j3d);
        }
示例#20
0
        public static J3D LoadActorByName(string actorName)
        {
            // Stop to check if we've already loaded this model.
            var existRef = m_j3dList.Find(x => string.Compare(x.FilePath, actorName, StringComparison.InvariantCultureIgnoreCase) == 0);

            if (existRef != null)
            {
                existRef.ReferenceCount++;
                return(existRef.Asset);
            }

            if (actorName == null)
            {
                return(null);
            }

            // Check to see if we have an Actor Descriptor for this actor.
            if (!m_actorDescriptors.ContainsKey(actorName))
            {
                return(null);
            }

            WActorDescriptor descriptor = m_actorDescriptors[actorName];

            // Check to see that this actor descriptor specifies a model path.
            if (string.IsNullOrEmpty(descriptor.ModelPath) || string.IsNullOrEmpty(descriptor.ArchiveName))
            {
                return(null);
            }

            string archivePath = Path.Combine(Properties.Settings.Default.RootDirectory, "res/Object/", descriptor.ArchiveName + ".arc");

            // Check to see that the archive exists
            if (!File.Exists(archivePath))
            {
                return(null);
            }

            // Finally, open the archive so we can look insdie of it to see if it exists.
            VirtualFilesystemDirectory archive     = ArchiveUtilities.LoadArchive(archivePath);
            VirtualFilesystemFile      archiveFile = archive.GetFileAtPath(descriptor.ModelPath);

            if (archiveFile == null)
            {
                Console.WriteLine("LoadActorByName failed because the specified path \"{0}\" does not exist in archive \"{1}\"!", descriptor.ModelPath, descriptor.ArchiveName);
                return(null);
            }

            // Now that we finally have the file, we can try to load a J3D from it.
            byte[] j3dData = archiveFile.Data;

            J3D j3d = new J3D(archiveFile.Name);

            using (EndianBinaryReader reader = new EndianBinaryReader(j3dData, Endian.Big))
                j3d.LoadFromStream(reader, Properties.Settings.Default.DumpTexturesToDisk, Properties.Settings.Default.DumpShadersToDisk);

            j3d.SetHardwareLight(0, m_mainLight);
            j3d.SetHardwareLight(1, m_secondaryLight);

            // Apply patches for Wind Waker by default, since they don't seem to break anything else.
            j3d.SetTextureOverride("ZBtoonEX", "resources/textures/ZBtoonEX.png");
            j3d.SetTextureOverride("ZAtoon", "resources/textures/ZAtoon.png");
            j3d.SetColorWriteOverride("eyeLdamA", false);
            j3d.SetColorWriteOverride("eyeLdamB", false);
            j3d.SetColorWriteOverride("mayuLdamA", false);
            j3d.SetColorWriteOverride("mayuLdamB", false);
            j3d.SetColorWriteOverride("eyeRdamA", false);
            j3d.SetColorWriteOverride("eyeRdamB", false);
            j3d.SetColorWriteOverride("mayuRdamA", false);
            j3d.SetColorWriteOverride("mayuRdamB", false);

            existRef          = new TSharedRef <J3D>();
            existRef.FilePath = actorName;
            existRef.Asset    = j3d;
            existRef.ReferenceCount++;

            m_j3dList.Add(existRef);

            return(j3d);
        }
示例#21
0
        private void LoadEntities(Map newMap, EditorCore core, Dictionary <Scene, VirtualFilesystemDirectory> archiveMap, WWorld world)
        {
            MapEntityLoader entityLoader = new MapEntityLoader(core, newMap);

            // For each room/scene, find the associated dzr/dzs file and load its
            // contents into the entityLoader.
            foreach (var kvp in archiveMap)
            {
                // Check to see if this Archive has stage/room entity data.
                var roomEntData  = kvp.Value.FindByExtension(".dzr");
                var stageEntData = kvp.Value.FindByExtension(".dzs");

                VirtualFilesystemFile vfsFile = null;
                if (roomEntData.Count > 0)
                {
                    vfsFile = roomEntData[0];
                }
                else if (stageEntData.Count > 0)
                {
                    vfsFile = stageEntData[0];
                }
                else
                {
                    continue;
                }

                using (EndianBinaryReader reader = new EndianBinaryReader(new System.IO.MemoryStream(vfsFile.File.GetData()), Endian.Big))
                {
                    WLog.Info(LogCategory.EntityLoading, null, "Loading .dzr/.dzs (Room/Stage Entity Dat) for {0}{1}...", vfsFile.Name, vfsFile.Extension);
                    entityLoader.LoadFromStream(kvp.Key, reader);
                    WLog.Info(LogCategory.EntityLoading, null, "Finished loading .dzr/.dzs (Room/Stage Entity Dat) for {0}{1}.", vfsFile.Name, vfsFile.Extension);
                }
            }

            // Once we've loaded all of the entities from the stream, we're going to
            // post-process them to resolve object references.
            entityLoader.PostProcessEntities();

            // Finally, we can actually convert these into map objects
            foreach (var roomOrStageData in entityLoader.GetData())
            {
                Stage stage = roomOrStageData.Key as Stage;
                Room  room  = roomOrStageData.Key as Room;

                if (stage != null)
                {
                    PostProcessStage(stage, roomOrStageData.Value);
                    foreach (var entity in stage.Entities)
                    {
                        entity.World = world;
                    }
                }
                if (room != null)
                {
                    PostProcessRoom(room, roomOrStageData.Value);
                    foreach (var entity in room.Entities)
                    {
                        entity.World = world;
                    }
                }
            }
        }
示例#22
0
        private void SaveMessageData()
        {
            VirtualFilesystemFile text_bank = m_MessageArchive.GetFileAtPath("zel_00.bmg");

            Encoding enc = Encoding.ASCII;

            switch (m_OriginalEncoding)
            {
            case TextEncoding.CP1252:
                enc = Encoding.GetEncoding(1252);
                break;

            case TextEncoding.Shift_JIS:
                enc = Encoding.GetEncoding(932);
                break;

            case TextEncoding.UTF_16:
                enc = Encoding.BigEndianUnicode;
                break;

            case TextEncoding.UTF_8:
                enc = Encoding.UTF8;
                break;
            }

            using (MemoryStream new_bmg_strm = new MemoryStream())
            {
                EndianBinaryWriter bmg_writer = new EndianBinaryWriter(new_bmg_strm, Endian.Big);
                bmg_writer.Write("MESGbmg1".ToCharArray());
                bmg_writer.Write(0);
                bmg_writer.Write(2);
                bmg_writer.Write((byte)m_OriginalEncoding);
                bmg_writer.Write(new byte[15]);

                using (MemoryStream text_data_strm = new MemoryStream())
                {
                    EndianBinaryWriter text_data_writer = new EndianBinaryWriter(text_data_strm, Endian.Big);
                    text_data_writer.Write((byte)0);

                    using (MemoryStream message_data_strm = new MemoryStream())
                    {
                        EndianBinaryWriter message_data_writer = new EndianBinaryWriter(message_data_strm, Endian.Big);

                        foreach (Message m in m_Messages)
                        {
                            m.Save(message_data_writer, text_data_writer, enc);
                        }

                        int delta = WMath.Pad16Delta(message_data_strm.Length);

                        bmg_writer.Write("INF1".ToCharArray());
                        bmg_writer.Write((uint)(message_data_strm.Length + 16 + delta));
                        bmg_writer.Write((ushort)m_Messages.Count);
                        bmg_writer.Write((ushort)0x18);
                        bmg_writer.Write(0);

                        bmg_writer.Write(message_data_strm.ToArray());

                        for (int i = 0; i < delta; i++)
                        {
                            bmg_writer.Write((byte)0);
                        }
                    }

                    // Seek to the end of the stream, and then pad us to 32-byte alignment.
                    text_data_strm.Seek(0, SeekOrigin.End);
                    int pad32delta = WMath.Pad32Delta(text_data_strm.Position + 8);
                    for (int i = 0; i < pad32delta; i++)
                    {
                        text_data_writer.Write((byte)0x00);
                    }

                    bmg_writer.Write("DAT1".ToCharArray());
                    bmg_writer.Write((uint)text_data_strm.Length + 8);

                    bmg_writer.Write(text_data_strm.ToArray());
                }

                text_bank.Data = new_bmg_strm.ToArray();
            }

            m_IsDataDirty = false;
            OnPropertyChanged("WindowTitle");
        }
示例#23
0
        static void GetDirectoriesRecursive(VirtualFilesystemDirectory root, string rootString)
        {
            List <string> files = new List <string>(Directory.GetFiles(rootString));

            // The entries in this subdir need to be in a particular order.
            // We'll fill them in with null so that we can replace them later.
            if (Path.GetFileName(rootString).ToLowerInvariant() == "&&systemdata")
            {
                for (int i = 0; i < 4; i++)
                {
                    root.Children.Add(null);
                }
            }

            foreach (string str in files)
            {
                string fileName          = Path.GetFileNameWithoutExtension(str);
                string fileExt           = Path.GetExtension(str);
                VirtualFileContents cont = new VirtualFileContents(File.ReadAllBytes(str));

                VirtualFilesystemFile newFile = new VirtualFilesystemFile(fileName, fileExt, cont);

                string fileNameLower = fileName.ToLowerInvariant();
                string fileExtLower  = fileExt.ToLowerInvariant();
                string fullFileName  = fileNameLower + fileExtLower;

                // These will replace the nulls we put in &&systemdata's child list earlier.
                if ((fullFileName == "iso.hdr") || (fullFileName == "isoheader.hdr"))
                {
                    root.Children[0] = newFile;
                }
                else if (fullFileName == "apploader.ldr")
                {
                    root.Children[2] = newFile;
                }
                else if (fullFileName == "start.dol")
                {
                    root.Children[1] = newFile;
                }
                else if (fullFileName == "game.toc")
                {
                    root.Children[3] = newFile;
                }
                else
                {
                    root.Children.Add(newFile);
                }
            }

            List <string> dirs = new List <string>(Directory.GetDirectories(rootString));

            foreach (string str in dirs)
            {
                string dirName                 = Path.GetFileName(str);
                string dirNameLower            = dirName.ToLowerInvariant();
                VirtualFilesystemDirectory dir = new VirtualFilesystemDirectory(dirName);

                GetDirectoriesRecursive(dir, str);

                if (dirNameLower == "&&systemdata")
                {
                    root.Children.Insert(0, dir);
                }
                else
                {
                    root.Children.Add(dir);
                }
            }
        }
示例#24
0
        public void WriteISO(VirtualFilesystemDirectory root, string path)
        {
            using (FileStream output = new FileStream(path, FileMode.Create, FileAccess.Write))
            {
                EndianBinaryWriter writer = new EndianBinaryWriter(output, Endian.Big);

                List <byte>     fstNameBank           = new List <byte>();
                List <FSTEntry> outputFST             = new List <FSTEntry>();
                List <VirtualFilesystemFile> fileList = new List <VirtualFilesystemFile>();
                FSTEntry rootFST = new FSTEntry();

                VirtualFilesystemDirectory sysDir = (VirtualFilesystemDirectory)root.Children.SingleOrDefault(e => e.Name == "&&systemdata")
                                                    ?? (VirtualFilesystemDirectory)root.Children.SingleOrDefault(e => e.Name == "sys");
                VirtualFilesystemFile header = (VirtualFilesystemFile)sysDir.Children.Single(e => e is VirtualFilesystemFile && ((VirtualFilesystemFile)e).NameWithExtension == "iso.hdr");
                writer.Write(header.Data);

                VirtualFilesystemFile apploader = (VirtualFilesystemFile)sysDir.Children.SingleOrDefault(e => e is VirtualFilesystemFile && ((VirtualFilesystemFile)e).NameWithExtension == "AppLoader.ldr")
                                                  ?? (VirtualFilesystemFile)sysDir.Children.SingleOrDefault(e => e is VirtualFilesystemFile && ((VirtualFilesystemFile)e).NameWithExtension == "apploader.img");
                writer.Write(apploader.Data);

                var dolOffsetWithoutPadding = writer.BaseStream.Position;
                var dolOffset = AlignTo(dolOffsetWithoutPadding, DolAlignment);
                for (long i = dolOffsetWithoutPadding; i < dolOffset; i++)
                {
                    writer.Write((byte)0);
                }

                VirtualFilesystemFile dol = (VirtualFilesystemFile)sysDir.Children.Single(e => e is VirtualFilesystemFile && ((VirtualFilesystemFile)e).Extension == ".dol");
                writer.Write(dol.Data);

                var fstListOffsetWithoutPadding = writer.BaseStream.Position;
                var fstListOffset = AlignTo(fstListOffsetWithoutPadding, FstAlignment);
                for (long i = fstListOffsetWithoutPadding; i < fstListOffset; i++)
                {
                    writer.Write((byte)0);
                }

                int fstLength = 12;

                root.Children.RemoveAt(0);

                foreach (VirtualFilesystemNode node in root.Children)
                {
                    fstLength = GetFSTSkipValue(fstLength, node);
                }

                byte[] dummyFST = new byte[fstLength];

                writer.Write(dummyFST);

                rootFST.Type = FSTNodeType.Directory;
                outputFST.Add(rootFST); //Placeholder FST entry for the root

                foreach (VirtualFilesystemNode node in root.Children)
                {
                    DoOutputPrep(node, outputFST, fstNameBank, writer, 0);
                }

                rootFST.FileSizeNextDirIndex = outputFST.Count();
                outputFST[0] = rootFST; //Add actual root FST entry

                writer.BaseStream.Position = fstListOffset;

                foreach (FSTEntry entry in outputFST)
                {
                    writer.Write((byte)entry.Type);
                    writer.Write((byte)0);
                    writer.Write((ushort)entry.FileNameOffset);
                    writer.Write(entry.FileOffsetParentDir);
                    writer.Write(entry.FileSizeNextDirIndex);
                }

                writer.Write(fstNameBank.ToArray());

                writer.BaseStream.Position = OffsetOfDolOffset;
                writer.Write((uint)dolOffset);
                writer.Write((uint)fstListOffset);
                writer.Write((uint)fstLength);
                writer.Write((uint)fstLength); // Doesn't work for multi disks
            }
        }
        public VirtualFilesystemDirectory ReadFile(EndianBinaryReader reader)
        {
            if (reader.ReadUInt32() != 0x52415243) // "RARC"
            {
                throw new InvalidDataException("Invalid Magic, not a RARC File");
            }

            uint fileSize = reader.ReadUInt32();

            reader.SkipUInt32(); // Unknown
            uint dataOffset = reader.ReadUInt32() + 0x20;

            reader.Skip(16); // Unknown - 4 unsigned ints
            uint numNodes = reader.ReadUInt32();

            reader.Skip(8); // Unknown - 2 unsigned ints
            uint fileEntryOffset = reader.ReadUInt32() + 0x20;

            reader.SkipUInt32(); // Unknown
            uint stringTableOffset = reader.ReadUInt32() + 0x20;

            reader.Skip(8); // Unknown - 2 unsigned ints.

            // Read all of the node headers.
            Node[] nodes = new Node[numNodes];

            for (int i = 0; i < numNodes; i++)
            {
                nodes[i] = new Node
                {
                    Type            = new string(reader.ReadChars(4)),
                    Name            = ReadStringAtOffset(reader, stringTableOffset, reader.ReadUInt32()),
                    NameHashcode    = reader.ReadUInt16(),
                    Entries         = new FileEntry[reader.ReadUInt16()],
                    FirstFileOffset = reader.ReadUInt32()
                };
            }


            // Create a virtual directory for every folder within the ARC before we process any of them.
            List <VirtualFilesystemDirectory> allDirs = new List <VirtualFilesystemDirectory>(nodes.Length);

            foreach (Node node in nodes)
            {
                VirtualFilesystemDirectory vfDir = new VirtualFilesystemDirectory(node.Name);
                allDirs.Add(vfDir);
            }

            for (int k = 0; k < nodes.Length; k++)
            {
                Node node = nodes[k];
                VirtualFilesystemDirectory curDir = allDirs[k];

                for (int i = 0; i < node.Entries.Length; i++)
                {
                    // Jump to the entry's offset in the file.
                    reader.BaseStream.Position   = fileEntryOffset + ((node.FirstFileOffset + i) * 0x14); // 0x14 is the size of a File Entry in bytes
                    node.Entries[i]              = new FileEntry();
                    node.Entries[i].ID           = reader.ReadUInt16();
                    node.Entries[i].NameHashcode = reader.ReadUInt16();
                    node.Entries[i].Flags        = reader.ReadByte();
                    reader.SkipByte(); // Padding
                    node.Entries[i].Name = ReadStringAtOffset(reader, stringTableOffset, reader.ReadUInt16());

                    // Skip these ones cause I don't know how computers work.
                    if (node.Entries[i].Name == "." || node.Entries[i].Name == "..")
                    {
                        continue;
                    }

                    uint entryDataOffset = reader.ReadUInt32();
                    uint dataSize        = reader.ReadUInt32();

                    // If it's a directory, then entryDataOffset contains the index of the parent node
                    if (node.Entries[i].IsDirectory)
                    {
                        node.Entries[i].SubDirIndex = entryDataOffset;
                        var newSubDir = allDirs[(int)entryDataOffset];
                        newSubDir.NodeID = node.Entries[i].ID;
                        curDir.Children.Add(newSubDir);
                    }
                    else
                    {
                        node.Entries[i].Data = reader.ReadBytesAt(dataOffset + entryDataOffset, (int)dataSize);

                        string fileName  = Path.GetFileNameWithoutExtension(node.Entries[i].Name);
                        string extension = Path.GetExtension(node.Entries[i].Name);

                        VirtualFilesystemFile vfFile = new VirtualFilesystemFile(fileName, extension, node.Entries[i].Data);
                        vfFile.NodeID = node.Entries[i].ID;
                        curDir.Children.Add(vfFile);
                    }

                    reader.SkipInt32(); // Padding
                }
            }

            // The ROOT directory should always be the first node. We don't have access to the node's TYPE anymore
            // so we're going to assume its always the first one listed.
            return(allDirs.Count > 0 ? allDirs[0] : null);
        }