示例#1
0
        // add it or return it if it's there
        private static NarcArchiveDirectoryEntry insertFolderInto(NarcArchiveDirectoryEntry _d, string _searchName)
        {
            for (int i = 0; i < _d.Entries.Count; ++i)
            {
                if (_d.Entries[i].Name == _searchName && _d.Entries[i] is NarcArchiveDirectoryEntry)
                {
                    return((NarcArchiveDirectoryEntry)_d.Entries[i]);
                }
            }
            NarcArchiveDirectoryEntry _newEntry = new NarcArchiveDirectoryEntry();

            _newEntry.Name   = _searchName;
            _newEntry.Parent = _d;
            _d.Entries.Add(_newEntry);
            return(_newEntry);
        }
        /// <summary>
        /// Initializes an instance of <see cref="NarcArchiveRootDirectoryEntry"/> from a specified path.
        /// </summary>
        /// <param name="path">The path.</param>
        public NarcArchiveRootDirectoryEntry(string path)
        {
            Path = path;

            var directoryEntries = new Queue <NarcArchiveDirectoryEntry>();

            directoryEntries.Enqueue(this);

            while (directoryEntries.Count > 0)
            {
                var currentDirectoryEntry = directoryEntries.Dequeue();

                var fileSystemEntries = Directory.EnumerateFileSystemEntries(currentDirectoryEntry.Path);
                foreach (var fileSystemEntry in fileSystemEntries)
                {
                    var isDirectory = File.GetAttributes(fileSystemEntry).HasFlag(FileAttributes.Directory);
                    if (isDirectory)
                    {
                        var directoryEntry = new NarcArchiveDirectoryEntry
                        {
                            Name   = new DirectoryInfo(fileSystemEntry).Name,
                            Path   = fileSystemEntry,
                            Parent = currentDirectoryEntry,
                        };

                        currentDirectoryEntry.Entries.Add(directoryEntry);
                        directoryEntries.Enqueue(directoryEntry);
                    }
                    else
                    {
                        var fileEntry = new NarcArchiveFileEntry
                        {
                            Name      = new FileInfo(fileSystemEntry).Name,
                            Path      = fileSystemEntry,
                            Directory = currentDirectoryEntry,
                        };

                        currentDirectoryEntry.Entries.Add(fileEntry);
                    }
                }
            }
        }
示例#3
0
        public static void create(string[] _inNames, CopyOnlyStream[] _inStreams, string outputPath)
        {
            bool _usingFilenames = (_inNames != null);
            NarcArchiveRootDirectoryEntry _root = new NarcArchiveRootDirectoryEntry();

            if (_usingFilenames)
            {
                // make all the parent directory thingies and then shove the file entries into them.
                for (int i = 0; i < _inStreams.Length; ++i)
                {
                    NarcArchiveDirectoryEntry _curParent = _root;
                    int _startSearchIndex = 0;
                    while (true)
                    {
                        int _nextSlashIndex = _inNames[i].IndexOf('/', _startSearchIndex, _inNames[i].Length - _startSearchIndex);
                        if (_nextSlashIndex != -1)
                        {
                            String _curFolderName = _inNames[i].Substring(_startSearchIndex, _nextSlashIndex - _startSearchIndex);
                            _curParent        = insertFolderInto(_curParent, _curFolderName);
                            _startSearchIndex = _nextSlashIndex + 1;
                        }
                        else
                        {
                            break;
                        }
                    }
                    _curParent.Entries.Add(new NarcArchiveFileEntry {
                        dataStream = _inStreams[i], Name = Path.GetFileName(_inNames[i])
                    });
                }
            }
            else
            {
                for (int i = 0; i < _inStreams.Length; ++i)
                {
                    _root.Entries.Add(new NarcArchiveFileEntry {
                        dataStream = _inStreams[i]
                    });
                }
            }
            lowCreate(_root, outputPath, _usingFilenames);
        }
示例#4
0
        // returns tuple with:
        // fimgPosition
        // hasFilenames
        // fileEntries
        public static Tuple <long, bool, List <NarcArchiveFileEntry> > startReadingNarc(FileStream input, BinaryReader reader, string inputPathForErrorMessages, bool ignoreFilenames)
        {
            // Read the NARC header
            if (!(reader.ReadByte() == 'N' &&
                  reader.ReadByte() == 'A' &&
                  reader.ReadByte() == 'R' &&
                  reader.ReadByte() == 'C' &&
                  reader.ReadByte() == 0xFE &&
                  reader.ReadByte() == 0xFF &&
                  reader.ReadByte() == 0 &&
                  reader.ReadByte() == 1 &&
                  reader.ReadInt32() == input.Length))
            {
                throw new InvalidFileTypeException(string.Format(ErrorMessages.NotANarcFile, Path.GetFileName(inputPathForErrorMessages)));
            }

            var headerLength = reader.ReadInt16();
            var fatbPosition = headerLength;

            // Read the FATB section
            input.Position = fatbPosition;
            if (!(reader.ReadByte() == 'B' &&
                  reader.ReadByte() == 'T' &&
                  reader.ReadByte() == 'A' &&
                  reader.ReadByte() == 'F'))
            {
                throw new InvalidFileTypeException(string.Format(ErrorMessages.NotANarcFile, Path.GetFileName(inputPathForErrorMessages)));
            }

            var fatbLength   = reader.ReadInt32();
            var fntbPosition = fatbPosition + fatbLength;

            var fileEntryCount = reader.ReadInt32();
            var fileEntries    = new List <NarcArchiveFileEntry>(fileEntryCount);

            for (var i = 0; i < fileEntryCount; i++)
            {
                var offset = reader.ReadInt32();
                var length = reader.ReadInt32() - offset;
                fileEntries.Add(new NarcArchiveFileEntry
                {
                    Offset = offset,
                    Length = length,
                });
            }

            // Read the FNTB section
            input.Position = fntbPosition;
            if (!(reader.ReadByte() == 'B' &&
                  reader.ReadByte() == 'T' &&
                  reader.ReadByte() == 'N' &&
                  reader.ReadByte() == 'F'))
            {
                throw new InvalidFileTypeException(string.Format(ErrorMessages.NotANarcFile, Path.GetFileName(inputPathForErrorMessages)));
            }

            var  fntbLength   = reader.ReadInt32();
            long fimgPosition = fntbPosition + fntbLength;

            var hasFilenames = !ignoreFilenames;

            // If the FNTB length is 16 or less, it's impossible for the entries to have filenames.
            // This section will always be at least 16 bytes long, but technically it's only required to be at least 8 bytes long.
            if (fntbLength <= 16)
            {
                hasFilenames = false;
            }

            var rootNameEntryOffset = reader.ReadInt32();

            // If the root name entry offset is 4, then the entries don't have filenames.
            if (rootNameEntryOffset == 4)
            {
                hasFilenames = false;
            }

            if (hasFilenames)
            {
                var rootFirstFileIndex = reader.ReadInt16();
                var rootDirectory      = new NarcArchiveRootDirectoryEntry();

                var directoryEntryCount = reader.ReadInt16();                 // This includes the root directory
                var directoryEntries    = new List <NarcArchiveDirectoryEntry>(directoryEntryCount)
                {
                    rootDirectory,
                };

                // This NARC contains filenames and directory names, so read them
                for (var i = 1; i < directoryEntryCount; i++)
                {
                    var nameEntryTableOffset = reader.ReadInt32();
                    var firstFileIndex       = reader.ReadInt16();
                    var parentDirectoryIndex = reader.ReadInt16() & 0xFFF;

                    directoryEntries.Add(new NarcArchiveDirectoryEntry
                    {
                        Index           = i,
                        Parent          = directoryEntries[parentDirectoryIndex],
                        NameEntryOffset = nameEntryTableOffset,
                        FirstFileIndex  = firstFileIndex,
                    });
                }

                NarcArchiveDirectoryEntry currentDirectory = rootDirectory;
                var directoryIndex = 0;
                var fileIndex      = 0;
                while (directoryIndex < directoryEntryCount)
                {
                    var entryNameLength = reader.ReadByte();
                    if ((entryNameLength & 0x80) != 0)
                    {
                        // This is a directory name entry
                        var entryName           = reader.ReadString(entryNameLength & 0x7F);
                        var entryDirectoryIndex = reader.ReadInt16() & 0xFFF;
                        var directoryEntry      = directoryEntries[entryDirectoryIndex];

                        directoryEntry.Name = entryName;
                    }
                    else if (entryNameLength != 0)
                    {
                        // This is a file name entry
                        var entryName = reader.ReadString(entryNameLength);
                        var fileEntry = fileEntries[fileIndex];

                        fileEntry.Parent = directoryEntries[directoryIndex];
                        fileEntry.Name   = entryName;

                        fileIndex++;
                    }
                    else
                    {
                        // This is the end of a directory
                        directoryIndex++;
                        if (directoryIndex >= directoryEntryCount)
                        {
                            break;
                        }
                        currentDirectory = directoryEntries[directoryIndex];
                    }
                }
            }
            return(new Tuple <long, bool, List <NarcArchiveFileEntry> >(fimgPosition, hasFilenames, fileEntries));
        }
示例#5
0
        public static void Extract(string inputPath, string outputPath, bool ignoreFilenames = false, bool _justPrintNames = false)
        {
            using (var input = new FileStream(inputPath, FileMode.Open, FileAccess.Read))
                using (var reader = new BinaryReader(input))
                {
                    // Read the NARC header
                    if (!(reader.ReadByte() == 'N' &&
                          reader.ReadByte() == 'A' &&
                          reader.ReadByte() == 'R' &&
                          reader.ReadByte() == 'C' &&
                          reader.ReadByte() == 0xFE &&
                          reader.ReadByte() == 0xFF &&
                          reader.ReadByte() == 0 &&
                          reader.ReadByte() == 1 &&
                          reader.ReadInt32() == input.Length))
                    {
                        throw new InvalidFileTypeException(string.Format(ErrorMessages.NotANarcFile, Path.GetFileName(inputPath)));
                    }

                    var headerLength = reader.ReadInt16();
                    var fatbPosition = headerLength;

                    // Read the FATB section
                    input.Position = fatbPosition;
                    if (!(reader.ReadByte() == 'B' &&
                          reader.ReadByte() == 'T' &&
                          reader.ReadByte() == 'A' &&
                          reader.ReadByte() == 'F'))
                    {
                        throw new InvalidFileTypeException(string.Format(ErrorMessages.NotANarcFile, Path.GetFileName(inputPath)));
                    }

                    var fatbLength   = reader.ReadInt32();
                    var fntbPosition = fatbPosition + fatbLength;

                    var fileEntryCount = reader.ReadInt32();
                    var fileEntries    = new List <NarcArchiveFileEntry>(fileEntryCount);
                    for (var i = 0; i < fileEntryCount; i++)
                    {
                        var offset = reader.ReadInt32();
                        var length = reader.ReadInt32() - offset;
                        fileEntries.Add(new NarcArchiveFileEntry
                        {
                            Offset = offset,
                            Length = length,
                        });
                    }

                    // Read the FNTB section
                    input.Position = fntbPosition;
                    if (!(reader.ReadByte() == 'B' &&
                          reader.ReadByte() == 'T' &&
                          reader.ReadByte() == 'N' &&
                          reader.ReadByte() == 'F'))
                    {
                        throw new InvalidFileTypeException(string.Format(ErrorMessages.NotANarcFile, Path.GetFileName(inputPath)));
                    }

                    var fntbLength   = reader.ReadInt32();
                    var fimgPosition = fntbPosition + fntbLength;

                    var hasFilenames = !ignoreFilenames;

                    // If the FNTB length is 16 or less, it's impossible for the entries to have filenames.
                    // This section will always be at least 16 bytes long, but technically it's only required to be at least 8 bytes long.
                    if (fntbLength <= 16)
                    {
                        hasFilenames = false;
                    }

                    var rootNameEntryOffset = reader.ReadInt32();

                    // If the root name entry offset is 4, then the entries don't have filenames.
                    if (rootNameEntryOffset == 4)
                    {
                        hasFilenames = false;
                    }

                    if (hasFilenames)
                    {
                        var rootFirstFileIndex = reader.ReadInt16();
                        var rootDirectory      = new NarcArchiveRootDirectoryEntry();

                        var directoryEntryCount = reader.ReadInt16(); // This includes the root directory
                        var directoryEntries    = new List <NarcArchiveDirectoryEntry>(directoryEntryCount)
                        {
                            rootDirectory,
                        };

                        // This NARC contains filenames and directory names, so read them
                        for (var i = 1; i < directoryEntryCount; i++)
                        {
                            var nameEntryTableOffset = reader.ReadInt32();
                            var firstFileIndex       = reader.ReadInt16();
                            var parentDirectoryIndex = reader.ReadInt16() & 0xFFF;

                            directoryEntries.Add(new NarcArchiveDirectoryEntry
                            {
                                Index           = i,
                                Parent          = directoryEntries[parentDirectoryIndex],
                                NameEntryOffset = nameEntryTableOffset,
                                FirstFileIndex  = firstFileIndex,
                            });
                        }

                        NarcArchiveDirectoryEntry currentDirectory = rootDirectory;
                        var directoryIndex = 0;
                        var fileIndex      = 0;
                        while (directoryIndex < directoryEntryCount)
                        {
                            var entryNameLength = reader.ReadByte();
                            if ((entryNameLength & 0x80) != 0)
                            {
                                // This is a directory name entry
                                var entryName           = reader.ReadString(entryNameLength & 0x7F);
                                var entryDirectoryIndex = reader.ReadInt16() & 0xFFF;
                                var directoryEntry      = directoryEntries[entryDirectoryIndex];

                                directoryEntry.Name = entryName;

                                if (_justPrintNames)
                                {
                                    Console.WriteLine("Dir: " + entryName);
                                }
                            }
                            else if (entryNameLength != 0)
                            {
                                // This is a file name entry
                                var entryName = reader.ReadString(entryNameLength);
                                var fileEntry = fileEntries[fileIndex];

                                fileEntry.Directory = directoryEntries[directoryIndex];
                                fileEntry.Name      = entryName;

                                fileIndex++;

                                if (_justPrintNames)
                                {
                                    Console.WriteLine(entryName);
                                }
                            }
                            else
                            {
                                // This is the end of a directory
                                directoryIndex++;
                                if (directoryIndex >= directoryEntryCount)
                                {
                                    break;
                                }
                                currentDirectory = directoryEntries[directoryIndex];
                            }
                        }
                    }
                    if (_justPrintNames)
                    {
                        return;
                    }

                    // Read the FIMG section
                    input.Position = fimgPosition;
                    if (!(reader.ReadByte() == 'G' &&
                          reader.ReadByte() == 'M' &&
                          reader.ReadByte() == 'I' &&
                          reader.ReadByte() == 'F'))
                    {
                        throw new InvalidFileTypeException(string.Format(ErrorMessages.NotANarcFile, Path.GetFileName(inputPath)));
                    }

                    if (hasFilenames)
                    {
                        foreach (var fileEntry in fileEntries)
                        {
                            var entryOutputPath      = Path.Combine(outputPath, fileEntry.FullName);
                            var entryOutputDirectory = Path.GetDirectoryName(entryOutputPath);
                            if (!Directory.Exists(entryOutputDirectory))
                            {
                                Directory.CreateDirectory(entryOutputDirectory);
                            }

                            using (var output = new FileStream(entryOutputPath, FileMode.Create, FileAccess.Write))
                                using (var entryStream = new SubReadStream(input, fimgPosition + 8 + fileEntry.Offset, fileEntry.Length))
                                {
                                    entryStream.CopyTo(output);
                                }
                        }
                    }
                    else
                    {
                        // This NARC doesn't contain filenames and directory names, so just use a file index as their filename
                        var index       = 0;
                        var numOfDigits = Math.Floor(Math.Log10(fileEntryCount) + 1);
                        foreach (var fileEntry in fileEntries)
                        {
                            var entryName       = $"{Path.GetFileNameWithoutExtension(inputPath)}_{index.ToString($"D{numOfDigits}")}";
                            var entryOutputPath = Path.Combine(outputPath, entryName);
                            if (!Directory.Exists(outputPath))
                            {
                                Directory.CreateDirectory(outputPath);
                            }

                            using (var output = new FileStream(entryOutputPath, FileMode.Create, FileAccess.Write))
                                using (var entryStream = new SubReadStream(input, fimgPosition + 8 + fileEntry.Offset, fileEntry.Length))
                                {
                                    entryStream.CopyTo(output);
                                }

                            index++;
                        }
                    }
                }
        }