Ejemplo n.º 1
0
 /// <summary>
 /// Loads the thumbnail and display info for the file
 /// </summary>
 /// <param name="inputStream">The file data stream</param>
 /// <param name="fileExtension">The file extension</param>
 /// <param name="width">The thumbnail width</param>
 /// <param name="manager">The manager</param>
 /// <returns>The thumbnail data</returns>
 public virtual ArchiveFileThumbnailData LoadThumbnail(ArchiveFileStream inputStream, FileExtension fileExtension, int width, IArchiveDataManager manager)
 {
     return(new ArchiveFileThumbnailData(null, new DuoGridItemViewModel[]
     {
         // TODO: Read and include .wav metadata, such as track length etc.
     }));
 }
    /// <summary>
    /// Indicates if a file with the specifies file extension and data is of this type
    /// </summary>
    /// <param name="fileExtension">The file extension to check</param>
    /// <param name="inputStream">The file data to check</param>
    /// <param name="manager">The manager</param>
    /// <returns>True if it is of this type, otherwise false</returns>
    public override bool IsOfType(FileExtension fileExtension, ArchiveFileStream inputStream, IArchiveDataManager manager)
    {
        if (fileExtension != new FileExtension(".tga.ckd", multiple: true) && fileExtension != new FileExtension(".png.ckd", multiple: true))
        {
            return(false);
        }

        // Set the Stream position
        TextureCooked?tex = ReadTEXHeader(inputStream, manager);

        // Check for type match
        if (IsOfType(inputStream, manager, tex))
        {
            return(true);
        }

        // If the format has a magic header we check for it
        if (FormatMagic != null)
        {
            // Use a reader
            using Reader reader = new(inputStream.Stream, manager.Context !.GetSettings <UbiArtSettings>().GetEndian == BinarySerializer.Endian.Little, true);

            // Get the magic header
            uint magic = reader.ReadUInt32();

            // Check if it matches the magic
            return(magic == FormatMagic);
        }

        return(false);
    }
Ejemplo n.º 3
0
    public override bool IsOfType(ArchiveFileStream inputStream, IArchiveDataManager manager, TextureCooked?tex)
    {
        UbiArtSettings settings = manager.Context !.GetSettings <UbiArtSettings>();

        // TODO: Find better way to check this
        return(settings.Platform == Platform.Xbox360);
    }
    /// <summary>
    /// Loads the thumbnail and display info for the file
    /// </summary>
    /// <param name="inputStream">The file data stream</param>
    /// <param name="fileExtension">The file extension</param>
    /// <param name="width">The thumbnail width</param>
    /// <param name="manager">The manager</param>
    /// <returns>The thumbnail data</returns>
    public ArchiveFileThumbnailData LoadThumbnail(ArchiveFileStream inputStream, FileExtension fileExtension, int width, IArchiveDataManager manager)
    {
        // Load the file
        GF file = GetFileContent(inputStream, manager);

        // Load the raw bitmap data
        RawBitmapData rawBmp = file.GetRawBitmapData(width, (int)(file.Height / ((double)file.Width / width)));

        var format = rawBmp.PixelFormat == PixelFormat.Format32bppArgb ? PixelFormats.Bgra32 : PixelFormats.Bgr24;

        // Get a thumbnail source
        BitmapSource thumbnailSource = BitmapSource.Create(rawBmp.Width, rawBmp.Height, 96, 96, format, null, rawBmp.PixelData, (rawBmp.Width * format.BitsPerPixel + 7) / 8);

        // Get the thumbnail with the specified size
        return(new ArchiveFileThumbnailData(thumbnailSource, new DuoGridItemViewModel[]
        {
            new DuoGridItemViewModel(
                header: new ResourceLocString(nameof(Resources.Archive_FileInfo_Img_Size)),
                text: $"{file.Width}x{file.Height}"),
            new DuoGridItemViewModel(
                header: new ResourceLocString(nameof(Resources.Archive_FileInfo_Img_HasAlpha)),
                text: new GeneratedLocString(() => $"{file.PixelFormat.SupportsTransparency()}")),
            new DuoGridItemViewModel(
                header: new ResourceLocString(nameof(Resources.Archive_FileInfo_Img_Mipmaps)),
                text: $"{file.ExclusiveMipmapCount}"),
            new DuoGridItemViewModel(
                header: new ResourceLocString(nameof(Resources.Archive_FileInfo_Format)),
                text: $"{file.PixelFormat.ToString().Replace("Format_", "")}",
                minUserLevel: UserLevel.Technical),
        }));
    }
    /// <summary>
    /// Converts the file data from the specified format
    /// </summary>
    /// <param name="inputFormat">The format to convert from</param>
    /// <param name="outputFormat">The format to convert to</param>
    /// <param name="currentFileStream">The current file stream</param>
    /// <param name="inputStream">The input file data stream to convert from</param>
    /// <param name="outputStream">The output stream for the converted data</param>
    /// <param name="manager">The manager</param>
    public void ConvertFrom(FileExtension inputFormat, FileExtension outputFormat, ArchiveFileStream currentFileStream, ArchiveFileStream inputStream, ArchiveFileStream outputStream, IArchiveDataManager manager)
    {
        // Load the bitmap
        using Bitmap bmp = new(inputStream.Stream);

        // Load the current file
        GF gf = GetFileContent(currentFileStream, manager);

        // IDEA: If bmp is not in supported format, then convert it?

        RawBitmapData rawBitmapData;

        // Get the bitmap lock
        using (BitmapLock bmpLock = new(bmp))
        {
            // Get the raw bitmap data
            rawBitmapData = new RawBitmapData(bmp.Width, bmp.Height, bmpLock.Pixels, bmp.PixelFormat);

            // Force the new pixel format to be 888 or 8888 if set to do so
            if (Services.Data.Archive_GF_ForceGF8888Import)
            {
                gf.PixelFormat = gf.PixelFormat.SupportsTransparency() ? GF_Format.Format_32bpp_BGRA_8888 : GF_Format.Format_24bpp_BGR_888;
            }

            // Check if the format should be updated for transparency
            if (Services.Data.Archive_GF_UpdateTransparency != UserData_Archive_GF_TransparencyMode.PreserveFormat)
            {
                // NOTE: Only 24 and 32 bpp bitmaps are supported
                // Check if the imported file is transparent
                bool?isTransparent = bmp.PixelFormat switch
                {
                    PixelFormat.Format32bppArgb => (Services.Data.Archive_GF_UpdateTransparency == UserData_Archive_GF_TransparencyMode.UpdateBasedOnPixelFormat ||
                                                    bmpLock.UtilizesAlpha()),
                    PixelFormat.Format24bppRgb => false,
                    _ => null
                };

                // NOTE: Currently only supported for formats with 3 or 4 channels
                // Check if the format should be updated for transparency
                if (gf.Channels >= 3 && isTransparent != null)
                {
                    // Update the format
                    gf.PixelFormat = isTransparent.Value ? GF_Format.Format_32bpp_BGRA_8888 : GF_Format.Format_24bpp_BGR_888;
                }
            }
        }

        byte oldRepeatByte = gf.RepeatByte;

        OpenSpaceSettings settings = manager.Context !.GetSettings <OpenSpaceSettings>();

        // Import the bitmap
        gf.ImportFromBitmap(settings, rawBitmapData, Services.Data.Archive_GF_GenerateMipmaps);

        Logger.Debug("The repeat byte has been updated for a .gf file from {0} to {1}", oldRepeatByte, gf.RepeatByte);

        // Serialize the data to get the bytes
        manager.Context.WriteStreamData(outputStream.Stream, gf, name: outputStream.Name, leaveOpen: true);
    }
    /// <summary>
    /// Converts the file data from the specified format
    /// </summary>
    /// <param name="inputFormat">The format to convert from</param>
    /// <param name="outputFormat">The format to convert to</param>
    /// <param name="inputStream">The input file data stream to convert from</param>
    /// <param name="outputStream">The output stream for the converted data</param>
    public void ConvertFrom(FileExtension inputFormat, MagickFormat outputFormat, ArchiveFileStream inputStream, ArchiveFileStream outputStream)
    {
        // Get the image in specified format
        using MagickImage img = new(inputStream.Stream, GetMagickFormat(inputFormat.FileExtensions));

        // Write to stream as native format
        img.Write(outputStream.Stream, outputFormat);
    }
    /// <summary>
    /// Gets an image from the file data
    /// </summary>
    /// <param name="inputStream">The file data stream</param>
    /// <param name="format">The file format</param>
    /// <param name="manager">The manager to check</param>
    /// <returns>The image</returns>
    protected override MagickImage GetImage(ArchiveFileStream inputStream, FileExtension format, IArchiveDataManager manager)
    {
        // Set the Stream position
        ReadTEXHeader(inputStream, manager);

        // Return the image
        return(new MagickImage(inputStream.Stream));
    }
 /// <summary>
 /// Loads the thumbnail and display info for the file
 /// </summary>
 /// <param name="inputStream">The file data stream</param>
 /// <param name="fileExtension">The file extension</param>
 /// <param name="width">The thumbnail width</param>
 /// <param name="manager">The manager</param>
 /// <returns>The thumbnail data</returns>
 public override ArchiveFileThumbnailData LoadThumbnail(ArchiveFileStream inputStream, FileExtension fileExtension, int width, IArchiveDataManager manager)
 {
     // Only load thumbnails for supported formats
     if (!IsFormatSupported)
     {
         return(new ArchiveFileThumbnailData(null, Array.Empty <DuoGridItemViewModel>()));
     }
     else
     {
         return(base.LoadThumbnail(inputStream, fileExtension, width, manager));
     }
 }
Ejemplo n.º 9
0
    public IArchiveFileType GetFileType(ArchiveFileStream stream)
    {
        // Get types supported by the current manager
        IArchiveFileType[] types = FileTypes.Where(x => x.IsSupported(Manager)).ToArray();

        // First attempt to find matching file type based off of the file extension to avoid having to read the file
        IArchiveFileType?match = types.FirstOrDefault(x => x.IsOfType(FileExtension));

        // If no match, check the data
        if (match == null)
        {
            // Find a match from the stream data
            match = types.FirstOrDefault(x => x.IsOfType(FileExtension, stream, Manager));
        }

        // Return the type and set to default if still null
        return(match ?? DefaultFileType);
    }
Ejemplo n.º 10
0
    public ArchiveFileStream GetFileData(IDisposable?generator)
    {
        if (!IsPendingImport && generator == null)
        {
            throw new ArgumentNullException(nameof(generator), "A generator must be specified if there is no pending import");
        }

        // Get the stream
        ArchiveFileStream stream = IsPendingImport
            ? new ArchiveFileStream(PendingImport, FileName, false)
            : new ArchiveFileStream(() => Manager.GetFileData(generator !, ArchiveEntry), FileName, true);

        // Seek to the beginning
        stream.SeekToBeginning();

        // Return the stream
        return(stream);
    }
    /// <summary>
    /// Converts the file data to the specified format
    /// </summary>
    /// <param name="inputFormat">The format to convert from</param>
    /// <param name="outputFormat">The format to convert to</param>
    /// <param name="inputStream">The input file data stream</param>
    /// <param name="outputStream">The output stream for the converted data</param>
    /// <param name="manager">The manager</param>
    public void ConvertTo(FileExtension inputFormat, FileExtension outputFormat, ArchiveFileStream inputStream, Stream outputStream, IArchiveDataManager manager)
    {
        // Get the image format
        ImageFormat imgFormat = outputFormat.PrimaryFileExtension switch
        {
            ".png" => ImageFormat.Png,
            ".jpeg" => ImageFormat.Jpeg,
            ".jpg" => ImageFormat.Jpeg,
            ".bmp" => ImageFormat.Bmp,
            _ => throw new Exception($"The specified file format {outputFormat.PrimaryFileExtension} is not supported")
        };

        // Get the bitmap
        using Bitmap bmp = GetFileContent(inputStream, manager).GetRawBitmapData().GetBitmap();

        // Save the bitmap to the output stream
        bmp.Save(outputStream, imgFormat);
    }
    /// <summary>
    /// Converts the file data from the specified format
    /// </summary>
    /// <param name="inputFormat">The format to convert from</param>
    /// <param name="outputFormat">The format to convert to</param>
    /// <param name="currentFileStream">The current file stream</param>
    /// <param name="inputStream">The input file data stream to convert from</param>
    /// <param name="outputStream">The output stream for the converted data</param>
    /// <param name="manager">The manager</param>
    public override void ConvertFrom(FileExtension inputFormat, FileExtension outputFormat, ArchiveFileStream currentFileStream,
                                     ArchiveFileStream inputStream, ArchiveFileStream outputStream, IArchiveDataManager manager)
    {
        // Get the current TEX data
        TextureCooked?tex = ReadTEXHeader(currentFileStream, manager);

        // If there's no TEX header we handle the image data directly
        if (tex == null)
        {
            if (outputFormat == Format)
            {
                inputStream.Stream.CopyTo(outputStream.Stream);
            }
            else
            {
                ConvertFrom(inputFormat, MagickFormat, inputStream, outputStream);
            }
        }
        else
        {
            // Get the image in specified format
            using MagickImage img = new(inputStream.Stream, GetMagickFormat(inputFormat.FileExtensions));

            // Change the type to the output format
            img.Format = MagickFormat;

            // Get the image bytes
            byte[] bytes = img.ToByteArray();

            // Update the TEX header
            tex.Height = (ushort)img.Height;
            tex.Width  = (ushort)img.Width;
            // TODO: Figure out what the values are on Wii U where they don't match the actual size
            tex.TextureSize  = (uint)bytes.Length;
            tex.TextureSize2 = (uint)bytes.Length;
            tex.ImageData    = bytes;

            tex.Pre_SerializeImageData = true;

            // Write the TEX file
            manager.Context !.WriteStreamData(outputStream.Stream, tex, name: outputStream.Name, leaveOpen: true);
        }
    }
        public override void Dispose()
        {
            // Cancel refreshing thumbnails
            ExplorerDialogViewModel.CancelRefreshingThumbnails = true;

            // Dispose base class
            base.Dispose();

            // Dispose the stream
            ArchiveFileStream?.Dispose();

            // Dispose every directory
            ClearAndDisposeItems();

            // Dispose the generator
            ArchiveFileGenerator?.Dispose();

            RL.Logger?.LogInformationSource($"The archive {DisplayName} has been disposed");
        }
    /// <summary>
    /// Loads the thumbnail and display info for the file
    /// </summary>
    /// <param name="inputStream">The file data stream</param>
    /// <param name="fileExtension">The file extension</param>
    /// <param name="width">The thumbnail width</param>
    /// <param name="manager">The manager</param>
    /// <returns>The thumbnail data</returns>
    public virtual ArchiveFileThumbnailData LoadThumbnail(ArchiveFileStream inputStream, FileExtension fileExtension, int width, IArchiveDataManager manager)
    {
        // Get the image
        using MagickImage img = GetImage(inputStream, fileExtension, manager);

        // Resize to a thumbnail
        img.Thumbnail(width, (int)(img.Height / ((double)img.Width / width)));

        BitmapSource thumb = img.ToBitmapSource();

        return(new ArchiveFileThumbnailData(thumb, new DuoGridItemViewModel[]
        {
            new DuoGridItemViewModel(
                header: new ResourceLocString(nameof(Resources.Archive_FileInfo_Img_Size)),
                text: $"{img.Width}x{img.Height}"),
            new DuoGridItemViewModel(
                header: new ResourceLocString(nameof(Resources.Archive_FileInfo_Format)),
                text: new GeneratedLocString(() => $"{GetFormat(fileExtension)}")),
        }));
    }
    /// <summary>
    /// Reads the TEX header if there is one
    /// </summary>
    /// <param name="inputStream">The input stream</param>
    /// <param name="manager">The manager</param>
    /// <returns>The TEX header, if available</returns>
    protected TextureCooked?ReadTEXHeader(ArchiveFileStream inputStream, IArchiveDataManager manager)
    {
        // Use a reader
        using Reader reader = new(inputStream.Stream, manager.Context !.GetSettings <UbiArtSettings>().GetEndian == BinarySerializer.Endian.Little, true);

        // Check if it's in a TEX wrapper
        inputStream.Stream.Position = 4;
        bool usesTexWrapper = reader.ReadUInt32() == TEXHeader;

        // Reset the position
        inputStream.Stream.Position = 0;

        // If it uses a TEX wrapper we need to serialize the header
        if (usesTexWrapper)
        {
            // Serialize the header
            return(manager.Context.ReadStreamData <TextureCooked>(inputStream.Stream, name: inputStream.Name, leaveOpen: true, onPreSerialize: x => x.Pre_SerializeImageData = false));
        }

        return(null);
    }
Ejemplo n.º 16
0
    /// <summary>
    /// Gets an image from the file data
    /// </summary>
    /// <param name="inputStream">The file data stream</param>
    /// <param name="format">The file format</param>
    /// <param name="manager">The manager to check</param>
    /// <returns>The image</returns>
    protected override MagickImage GetImage(ArchiveFileStream inputStream, FileExtension format, IArchiveDataManager manager)
    {
        // Serialize data
        TextureCooked tex = manager.Context !.ReadStreamData <TextureCooked>(inputStream.Stream, name: inputStream.Name, leaveOpen: true, onPreSerialize: x =>
        {
            x.Pre_SerializeImageData = true;
            x.Pre_FileSize           = inputStream.Stream.Length;
        });

        // Get the untiled image data
        byte[] untiledImgData = tex.Header_Xbox360.Untile(tex.ImageData, true);

        DDSParser.DDSStruct header = new()
        {
            pixelformat = new DDSParser.DDSStruct.pixelformatstruct()
            {
                rgbbitcount = 32
            },
            width  = (uint)tex.Header_Xbox360.Width,
            height = (uint)tex.Header_Xbox360.Height,
            depth  = 1
        };

        byte[] rawImgData = tex.Header_Xbox360.CompressionType switch
        {
            TextureCooked_Xbox360Header.TextureCompressionType.DXT1 => DDSParser.DecompressDXT1(header, untiledImgData),
            TextureCooked_Xbox360Header.TextureCompressionType.DXT3 => DDSParser.DecompressDXT3(header, untiledImgData),
            TextureCooked_Xbox360Header.TextureCompressionType.DXT5 => DDSParser.DecompressDXT5(header, untiledImgData),
            _ => throw new ArgumentOutOfRangeException(nameof(tex.Header_Xbox360.CompressionType), tex.Header_Xbox360.CompressionType, null)
        };

        // Return the image
        return(new MagickImage(rawImgData, new MagickReadSettings()
        {
            Format = MagickFormat.Rgba,
            Width = tex.Header_Xbox360.Width,
            Height = tex.Header_Xbox360.Height
        }));
    }
}
Ejemplo n.º 17
0
    /// <summary>
    /// Disposes the archive and its folders and files
    /// </summary>
    public override void Dispose()
    {
        // Cancel refreshing thumbnails
        ExplorerDialogViewModel.CancelInitializeFiles = true;

        // Dispose base class
        base.Dispose();

        // Dispose the stream
        ArchiveFileStream.Dispose();

        // Dispose every directory
        ClearAndDisposeItems();

        // Dispose the generator
        ArchiveFileGenerator?.Dispose();

        // Dispose the cache
        ThumbnailCache.Dispose();

        Logger.Info("The archive {0} has been disposed", DisplayName);
    }
    /// <summary>
    /// Converts the file data to the specified format
    /// </summary>
    /// <param name="inputFormat">The format to convert from</param>
    /// <param name="outputFormat">The format to convert to</param>
    /// <param name="inputStream">The input file data stream</param>
    /// <param name="outputStream">The output stream for the converted data</param>
    /// <param name="manager">The manager</param>
    public virtual void ConvertTo(FileExtension inputFormat, FileExtension outputFormat, ArchiveFileStream inputStream, Stream outputStream, IArchiveDataManager manager)
    {
        // Get the image
        using MagickImage img = GetImage(inputStream, inputFormat, manager);

        // Write to stream as new format
        img.Write(outputStream, GetMagickFormat(outputFormat.FileExtensions));
    }
Ejemplo n.º 19
0
    /// <summary>
    /// Writes the files to the archive
    /// </summary>
    /// <param name="generator">The generator</param>
    /// <param name="archive">The loaded archive data</param>
    /// <param name="outputFileStream">The file output stream for the archive</param>
    /// <param name="files">The files to include</param>
    public void WriteArchive(IDisposable?generator, object archive, ArchiveFileStream outputFileStream, IList <ArchiveFileItem> files)
    {
        Logger.Info("An R1 PC archive is being repacked...");

        // Get the archive data
        var data = (PC_FileArchive)archive;

        // Create the file generator
        using ArchiveFileGenerator <PC_FileArchiveEntry> fileGenerator = new();

        // Get files and entries
        var archiveFiles = files.Select(x => new
        {
            Entry    = (PC_FileArchiveEntry)x.ArchiveEntry,
            FileItem = x
        }).ToArray();

        // Set files and directories
        data.Entries = archiveFiles.Select(x => x.Entry).ToArray();

        BinaryFile binaryFile = new StreamFile(Context, outputFileStream.Name, outputFileStream.Stream, leaveOpen: true);

        try
        {
            Context.AddFile(binaryFile);

            // Initialize the data
            data.Init(binaryFile.StartPointer);

            // Set the current pointer position to the header size
            data.RecalculateSize();
            uint pointer = (uint)data.Size;

            // Load each file
            foreach (var file in archiveFiles)
            {
                // Process the file name
                file.Entry.FileName = ProcessFileName(file.Entry.FileName);

                // Get the file entry
                PC_FileArchiveEntry entry = file.Entry;

                // Add to the generator
                fileGenerator.Add(entry, () =>
                {
                    // Get the file stream to write to the archive
                    Stream fileStream = file.FileItem.GetFileData(generator).Stream;

                    // Set the pointer
                    entry.FileOffset = pointer;

                    // Update the pointer by the file size
                    pointer += entry.FileSize;

                    // Invoke event
                    OnWritingFileToArchive?.Invoke(this, new ValueEventArgs <ArchiveFileItem>(file.FileItem));

                    return(fileStream);
                });
            }

            // Make sure we have a generator for each file
            if (fileGenerator.Count != data.Entries.Length)
            {
                throw new Exception("The .dat file can't be serialized without a file generator for each file");
            }

            // Write the file contents
            foreach (PC_FileArchiveEntry file in data.Entries)
            {
                // Get the file stream
                using Stream fileStream = fileGenerator.GetFileStream(file);

                // Set the position to the pointer
                outputFileStream.Stream.Position = file.FileOffset;

                // Write the contents from the generator
                fileStream.CopyTo(outputFileStream.Stream);
            }

            outputFileStream.Stream.Position = 0;

            // Serialize the data
            FileFactory.Write(Context, binaryFile.FilePath, data);

            Logger.Info("The R1 PC archive has been repacked");
        }
        finally
        {
            Context.RemoveFile(binaryFile);
        }
    }
 /// <summary>
 /// Indicates if a file with the specifies file extension and data is of this type
 /// </summary>
 /// <param name="fileExtension">The file extension to check</param>
 /// <param name="inputStream">The file data to check</param>
 /// <param name="manager">The manager</param>
 /// <returns>True if it is of this type, otherwise false</returns>
 public virtual bool IsOfType(FileExtension fileExtension, ArchiveFileStream inputStream, IArchiveDataManager manager) => false;
    /// <summary>
    /// Converts the file data to the specified format
    /// </summary>
    /// <param name="inputFormat">The format to convert from</param>
    /// <param name="outputFormat">The format to convert to</param>
    /// <param name="inputStream">The input file data stream</param>
    /// <param name="outputStream">The output stream for the converted data</param>
    /// <param name="manager">The manager</param>
    public override void ConvertTo(FileExtension inputFormat, FileExtension outputFormat, ArchiveFileStream inputStream, Stream outputStream,
                                   IArchiveDataManager manager)
    {
        // Check if it's the native format
        if (outputFormat == Format)
        {
            // Set the start position
            ReadTEXHeader(inputStream, manager);

            // Copy the image data
            inputStream.Stream.CopyTo(outputStream);
        }
        else
        {
            // Convert the image normally
            base.ConvertTo(inputFormat, outputFormat, inputStream, outputStream, manager);
        }
    }
 /// <summary>
 /// Gets an image from the file data
 /// </summary>
 /// <param name="inputStream">The file data stream</param>
 /// <param name="format">The file format</param>
 /// <param name="manager">The manager to check</param>
 /// <returns>The image</returns>
 protected virtual MagickImage GetImage(ArchiveFileStream inputStream, FileExtension format, IArchiveDataManager manager) => new MagickImage(inputStream.Stream, GetMagickFormat(format));
 public virtual bool IsOfType(ArchiveFileStream inputStream, IArchiveDataManager manager, TextureCooked?tex) => false;
    /// <summary>
    /// Writes the files to the archive
    /// </summary>
    /// <param name="generator">The generator</param>
    /// <param name="archive">The loaded archive data</param>
    /// <param name="outputFileStream">The file output stream for the archive</param>
    /// <param name="files">The files to include</param>
    public void WriteArchive(IDisposable?generator, object archive, ArchiveFileStream outputFileStream, IList <ArchiveFileItem> files)
    {
        Logger.Info("An IPK archive is being repacked...");

        // Get the archive data
        var data = (BundleFile)archive;

        // Create the file generator
        using ArchiveFileGenerator <BundleFile_FileEntry> fileGenerator = new();

        // Get files and entries
        var archiveFiles = files.Select(x => new
        {
            Entry    = (BundleFile_FileEntry)x.ArchiveEntry,
            FileItem = x
        }).ToArray();

        // Set the files
        data.FilePack.Files        = archiveFiles.Select(x => x.Entry).ToArray();
        data.BootHeader.FilesCount = (uint)data.FilePack.Files.Length;

        // Save the old base offset
        uint oldBaseOffset = data.BootHeader.BaseOffset;

        // Keep track of the current pointer position
        ulong currentOffset = 0;

        // Handle each file
        foreach (var file in archiveFiles)
        {
            // Get the file
            BundleFile_FileEntry entry = file.Entry;

            // Reset the offset array to always contain 1 item
            entry.Offsets = new ulong[]
            {
                entry.Offsets?.FirstOrDefault() ?? 0
            };

            // Set the count
            entry.OffsetsCount = (uint)entry.Offsets.Length;

            // Add to the generator
            fileGenerator.Add(entry, () =>
            {
                // When reading the original file we need to use the old base offset
                uint newBaseOffset         = data.BootHeader.BaseOffset;
                data.BootHeader.BaseOffset = oldBaseOffset;

                // Get the file bytes to write to the archive
                Stream fileStream = file.FileItem.GetFileData(generator).Stream;

                data.BootHeader.BaseOffset = newBaseOffset;

                // Set the offset
                entry.Offsets[0] = currentOffset;

                // Increase by the file size
                currentOffset += entry.ArchiveSize;

                // Invoke event
                OnWritingFileToArchive?.Invoke(this, new ValueEventArgs <ArchiveFileItem>(file.FileItem));

                return(fileStream);
            });
        }

        BinaryFile binaryFile = new StreamFile(Context, outputFileStream.Name, outputFileStream.Stream, leaveOpen: true);

        try
        {
            Context.AddFile(binaryFile);

            // Initialize the data
            data.Init(binaryFile.StartPointer);

            // Set the base offset
            data.RecalculateSize();
            data.BootHeader.BaseOffset = (uint)data.Size;

            // Write the files
            WriteArchiveContent(data, outputFileStream.Stream, fileGenerator, Config.ShouldCompress(data.BootHeader));

            outputFileStream.Stream.Position = 0;

            // Serialize the data
            FileFactory.Write(Context, binaryFile.FilePath, data);

            Logger.Info("The IPK archive has been repacked");
        }
        finally
        {
            Context.RemoveFile(binaryFile);
        }
    }
 /// <summary>
 /// Converts the file data from the specified format
 /// </summary>
 /// <param name="inputFormat">The format to convert from</param>
 /// <param name="outputFormat">The format to convert to</param>
 /// <param name="currentFileStream">The current file stream</param>
 /// <param name="inputStream">The input file data stream to convert from</param>
 /// <param name="outputStream">The output stream for the converted data</param>
 /// <param name="manager">The manager</param>
 public virtual void ConvertFrom(FileExtension inputFormat, FileExtension outputFormat, ArchiveFileStream currentFileStream, ArchiveFileStream inputStream, ArchiveFileStream outputStream, IArchiveDataManager manager)
 {
     ConvertFrom(inputFormat, GetMagickFormat(outputFormat), inputStream, outputStream);
 }
Ejemplo n.º 26
0
    /// <summary>
    /// Exports the directory
    /// </summary>
    /// <param name="forceNativeFormat">Indicates if the native format should be forced</param>
    /// <param name="selectedFilesOnly">Indicates if only selected files in the current directory should be exported</param>
    /// <returns>The task</returns>
    public async Task ExportAsync(bool forceNativeFormat, bool selectedFilesOnly = false)
    {
        // Run as a load operation
        using (await Archive.LoadOperation.RunAsync())
        {
            // Lock the access to the archive
            using (await Archive.ArchiveLock.LockAsync())
            {
                // Get the output path
                DirectoryBrowserResult result = await Services.BrowseUI.BrowseDirectoryAsync(new DirectoryBrowserViewModel()
                {
                    Title = Resources.Archive_ExportHeader
                });

                if (result.CanceledByUser)
                {
                    return;
                }

                // Make sure there isn't an existing file at the output path
                if ((result.SelectedDirectory + ExportDirName).FileExists)
                {
                    await Services.MessageUI.DisplayMessageAsync(String.Format(Resources.Archive_ExportDirFileConflict, ExportDirName), MessageType.Error);

                    return;
                }

                // Run as a task
                await Task.Run(async() =>
                {
                    // Get the manager
                    IArchiveDataManager manager = Archive.Manager;

                    // Save the selected format for each collection
                    Dictionary <IArchiveFileType, FileExtension?> selectedFormats = new();

                    try
                    {
                        ArchiveDirectoryViewModel[] allDirs;

                        if (selectedFilesOnly)
                        {
                            allDirs = new ArchiveDirectoryViewModel[]
                            {
                                this
                            }
                        }
                        ;
                        else
                        {
                            allDirs = this.GetAllChildren(true).ToArray();
                        }

                        int fileIndex  = 0;
                        int filesCount = allDirs.SelectMany(x => x.Files).Count(x => !selectedFilesOnly || x.IsSelected);

                        // Handle each directory
                        foreach (ArchiveDirectoryViewModel item in allDirs)
                        {
                            // Get the directory path
                            FileSystemPath path = result.SelectedDirectory + ExportDirName + item.FullPath.Remove(0, FullPath.Length).Trim(manager.PathSeparatorCharacter);

                            // Create the directory
                            Directory.CreateDirectory(path);

                            // Save each file
                            foreach (ArchiveFileViewModel file in item.Files.Where(x => !selectedFilesOnly || x.IsSelected))
                            {
                                // Get the file stream
                                using ArchiveFileStream fileStream = file.GetDecodedFileStream();

                                // Initialize the file without loading the thumbnail
                                file.InitializeFile(fileStream, ArchiveFileViewModel.ThumbnailLoadMode.None);

                                fileStream.SeekToBeginning();

                                // Check if the format has not been selected
                                if (!forceNativeFormat && !selectedFormats.ContainsKey(file.FileType) && file.FileType is not ArchiveFileType_Default)
                                {
                                    // Get the available extensions
                                    string[] ext = new string[]
                                    {
                                        Resources.Archive_Export_Format_Original
                                    }.Concat(file.FileType.ExportFormats.Select(x => x.FileExtensions)).ToArray();

                                    // Have user select the format
                                    FileExtensionSelectionDialogResult extResult = await Services.UI.SelectFileExtensionAsync(new FileExtensionSelectionDialogViewModel(ext, String.Format(Resources.Archive_FileExtensionSelectionInfoHeader, file.FileType.TypeDisplayName)));

                                    // Since this operation can't be canceled we get the first format
                                    if (extResult.CanceledByUser)
                                    {
                                        extResult.SelectedFileFormat = ext.First();
                                    }

                                    // Add the selected format
                                    FileExtension?e = extResult.SelectedFileFormat == ext.First()
                                        ? null
                                        : new FileExtension(extResult.SelectedFileFormat, multiple: true);

                                    selectedFormats.Add(file.FileType, e);
                                }

                                // Get the selected format
                                FileExtension?format = forceNativeFormat || file.FileType is ArchiveFileType_Default
                                    ? null
                                    : selectedFormats[file.FileType];

                                // Get the final file name to use when exporting
                                FileSystemPath exportFileName = format == null
                                    ? new FileSystemPath(file.FileName)
                                    : new FileSystemPath(file.FileName).ChangeFileExtension(format, true);

                                Archive.SetDisplayStatus($"{String.Format(Resources.Archive_ExportingFileStatus, file.FileName)}" +
                                                         $"{Environment.NewLine}{++fileIndex}/{filesCount}");

                                try
                                {
                                    // Export the file
                                    file.ExportFile(path + exportFileName, fileStream, format);
                                }
                                catch (Exception ex)
                                {
                                    // If the export failed for a native format we throw
                                    if (format == null)
                                    {
                                        throw;
                                    }

                                    Logger.Error(ex, "Exporting archive file {0}", file.FileName);

                                    // If the export failed and we tried converting it we instead export it as the native format
                                    // Start by setting the file in the error state, thus changing the type
                                    file.InitializeAsError();

                                    // Seek to the beginning of the stream in case some bytes were read
                                    fileStream.SeekToBeginning();

                                    // Export the file as the native format
                                    file.ExportFile(path + file.FileName, fileStream, null);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex, "Exporting archive directory {0}", DisplayName);

                        await Services.MessageUI.DisplayExceptionMessageAsync(ex, String.Format(Resources.Archive_ExportError, DisplayName));

                        return;
                    }
                    finally
                    {
                        Archive.SetDisplayStatus(String.Empty);
                    }

                    await Services.MessageUI.DisplaySuccessfulActionMessageAsync(Resources.Archive_ExportFilesSuccess);
                });
            }
        }
    }
Ejemplo n.º 27
0
 /// <summary>
 /// Converts the file data from the specified format
 /// </summary>
 /// <param name="inputFormat">The format to convert from</param>
 /// <param name="outputFormat">The format to convert to</param>
 /// <param name="currentFileStream">The current file stream</param>
 /// <param name="inputStream">The input file data stream to convert from</param>
 /// <param name="outputStream">The output stream for the converted data</param>
 /// <param name="manager">The manager</param>
 public virtual void ConvertFrom(FileExtension inputFormat, FileExtension outputFormat, ArchiveFileStream currentFileStream, ArchiveFileStream inputStream, ArchiveFileStream outputStream, IArchiveDataManager manager)
 {
     throw new NotSupportedException("Converting .wav files is not supported");
 }
Ejemplo n.º 28
0
    /// <summary>
    /// Imports files to the directory
    /// </summary>
    /// <returns>The task</returns>
    public async Task ImportAsync()
    {
        // Run as a load operation
        using (await Archive.LoadOperation.RunAsync())
        {
            // Lock the access to the archive
            using (await Archive.ArchiveLock.LockAsync())
            {
                // Get the directory
                DirectoryBrowserResult result = await Services.BrowseUI.BrowseDirectoryAsync(new DirectoryBrowserViewModel()
                {
                    Title = Resources.Archive_ImportDirectoryHeader,
                });

                if (result.CanceledByUser)
                {
                    return;
                }

                // Run as a task
                await Task.Run(async() =>
                {
                    // Keep track of the number of files getting imported
                    int imported = 0;

                    try
                    {
                        // Enumerate each directory view model
                        foreach (ArchiveDirectoryViewModel dir in this.GetAllChildren(true))
                        {
                            // Enumerate each file
                            foreach (ArchiveFileViewModel file in dir.Files)
                            {
                                // Get the file directory, relative to the selected directory
                                FileSystemPath fileDir = result.SelectedDirectory + dir.FullPath.Remove(0, FullPath.Length).Trim(Path.DirectorySeparatorChar);

                                if (!fileDir.DirectoryExists)
                                {
                                    continue;
                                }

                                // Get the base file path
                                FileSystemPath baseFilePath = fileDir + new FileSystemPath(file.FileName);

                                // Get the file path, without an extension
                                FileSystemPath filePath = baseFilePath.RemoveFileExtension(true);

                                // Make sure there are potential file matches
                                if (!Directory.GetFiles(fileDir, $"{filePath.Name}*", SearchOption.TopDirectoryOnly).Any())
                                {
                                    continue;
                                }

                                // Get the file stream
                                using ArchiveFileStream fileStream = file.GetDecodedFileStream();

                                // Initialize the file without loading the thumbnail
                                file.InitializeFile(fileStream, ArchiveFileViewModel.ThumbnailLoadMode.None);

                                // Check if the base file exists without changing the extensions
                                if (baseFilePath.FileExists)
                                {
                                    // Import the file
                                    file.ImportFile(baseFilePath, false);

                                    imported++;

                                    continue;
                                }

                                // Attempt to find a file for each supported extension
                                foreach (FileExtension ext in file.FileType.ImportFormats)
                                {
                                    // Get the path
                                    FileSystemPath fullFilePath = filePath.ChangeFileExtension(ext);

                                    // Make sure the file exists
                                    if (!fullFilePath.FileExists)
                                    {
                                        continue;
                                    }

                                    // Import the file
                                    file.ImportFile(fullFilePath, true);

                                    imported++;

                                    // Break the loop
                                    break;
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex, "Importing archive directory {0}", DisplayName);

                        await Services.MessageUI.DisplayExceptionMessageAsync(ex, Resources.Archive_ImportDir_Error);

                        return;
                    }

                    // Make sure at least one file has been imported
                    if (imported == 0)
                    {
                        await Services.MessageUI.DisplayMessageAsync(Resources.Archive_ImportNoFilesError, MessageType.Warning);
                    }
                });
            }
        }
    }
Ejemplo n.º 29
0
    /// <summary>
    /// Writes the files to the archive
    /// </summary>
    /// <param name="generator">The generator</param>
    /// <param name="archive">The loaded archive data</param>
    /// <param name="outputFileStream">The file output stream for the archive</param>
    /// <param name="files">The files to include</param>
    public void WriteArchive(IDisposable?generator, object archive, ArchiveFileStream outputFileStream, IList <ArchiveFileItem> files)
    {
        Logger.Info("A CNT archive is being repacked...");

        // Get the archive data
        var data = (CNT)archive;

        // Create the file generator
        using ArchiveFileGenerator <CNT_File> fileGenerator = new();

        // Get files and entries
        var archiveFiles = files.Select(x => new
        {
            Entry    = (CNT_File)x.ArchiveEntry,
            FileItem = x
        }).ToArray();

        // Set files and directories
        data.Directories = files.Select(x => x.Directory).Distinct().Where(x => !x.IsNullOrWhiteSpace()).ToArray();
        data.Files       = archiveFiles.Select(x => x.Entry).ToArray();

        // Set the directory indexes
        foreach (var file in archiveFiles)
        {
            // Set the directory index
            file.Entry.DirectoryIndex = file.FileItem.Directory == String.Empty ? -1 : data.Directories.FindItemIndex(x => x == file.FileItem.Directory);
        }

        BinaryFile binaryFile = new StreamFile(Context, outputFileStream.Name, outputFileStream.Stream, leaveOpen: true);

        try
        {
            Context.AddFile(binaryFile);

            // Initialize the data
            data.Init(binaryFile.StartPointer);

            // Set the current pointer position to the header size
            data.RecalculateSize();
            uint pointer = (uint)data.Size;

            // Disable checksum
            data.IsChecksumUsed = false;

            // NOTE: We can't disable the XOR key entirely as that would disable it for the file bytes too, which would require them all to be decrypted
            // Reset XOR keys
            data.StringsXORKey = 0;

            // Load each file
            foreach (var file in archiveFiles)
            {
                // Get the file entry
                CNT_File entry = file.Entry;

                // Reset checksum and XOR key
                entry.FileChecksum       = 0;
                entry.Pre_FileNameXORKey = 0;

                // Add to the generator
                fileGenerator.Add(entry, () =>
                {
                    // Get the file stream to write to the archive
                    Stream fileStream = file.FileItem.GetFileData(generator).Stream;

                    // Set the pointer
                    entry.FileOffset = pointer;

                    // Update the pointer by the file size
                    pointer += entry.FileSize;

                    // Invoke event
                    OnWritingFileToArchive?.Invoke(this, new ValueEventArgs <ArchiveFileItem>(file.FileItem));

                    return(fileStream);
                });
            }

            // Make sure we have a generator for each file
            if (fileGenerator.Count != data.Files.Length)
            {
                throw new Exception("The .cnt file can't be serialized without a file generator for each file");
            }

            // Write the file contents
            foreach (CNT_File file in data.Files)
            {
                // Get the file stream
                using Stream fileStream = fileGenerator.GetFileStream(file);

                // Set the position to the pointer
                outputFileStream.Stream.Position = file.FileOffset;

                // Write the contents from the generator
                fileStream.CopyTo(outputFileStream.Stream);
            }

            outputFileStream.Stream.Position = 0;

            // Serialize the data
            FileFactory.Write(Context, binaryFile.FilePath, data);

            Logger.Info("The CNT archive has been repacked");
        }
        finally
        {
            Context.RemoveFile(binaryFile);
        }
    }
Ejemplo n.º 30
0
    /// <summary>
    /// Saves any pending changes to the archive and reloads it
    /// </summary>
    /// <returns>The task</returns>
    public async Task SaveAsync()
    {
        Logger.Info("The archive {0} is being repacked", DisplayName);

        // Run as a load operation
        using (await Archive.LoadOperation.RunAsync())
        {
            // Lock the access to the archive
            using (await Archive.ArchiveLock.LockAsync())
            {
                // Find the selected item path
                string?selectedDirAddr = ExplorerDialogViewModel.SelectedDir == null
                    ? null
                    : ExplorerDialogViewModel.GetDirectoryAddress(ExplorerDialogViewModel.SelectedDir);

                // Run as a task
                await Task.Run(async() =>
                {
                    // Stop file initialization
                    if (ExplorerDialogViewModel.IsInitializingFiles)
                    {
                        ExplorerDialogViewModel.CancelInitializeFiles = true;
                    }

                    Archive.SetDisplayStatus(String.Format(Resources.Archive_RepackingStatus, DisplayName));

                    try
                    {
                        // Get a temporary file path to write to
                        using TempFile tempOutputFile = new(false);

                        // Create the file and get the stream
                        using (ArchiveFileStream outputStream = new(File.Create(tempOutputFile.TempPath), tempOutputFile.TempPath.Name, true))
                        {
                            // Write to the stream
                            Manager.WriteArchive(
                                generator: ArchiveFileGenerator,
                                archive: ArchiveData ?? throw new Exception("Archive data has not been loaded"),
                                outputFileStream: outputStream,
                                files: this.GetAllChildren <ArchiveDirectoryViewModel>(true).
                                SelectMany(x => x.Files).
                                Select(x => x.FileData).
                                ToArray());
                        }

                        // Dispose the archive file stream
                        ArchiveFileStream.Dispose();

                        ArchiveData = null;

                        // If the operation succeeded, replace the archive file with the temporary output
                        Services.File.MoveFile(tempOutputFile.TempPath, FilePath, true);

                        // Re-open the file stream
                        OpenFile();
                    }
                    catch (Exception ex)
                    {
                        Logger.Error(ex, "Repacking archive {0}", DisplayName);

                        await Services.MessageUI.DisplayExceptionMessageAsync(ex, Resources.Archive_RepackError);

                        // Re-open the file stream if closed
                        if (ArchiveFileStream.SafeFileHandle?.IsClosed != false)
                        {
                            OpenFile();
                        }
                    }
                });

                // Reload the archive
                LoadArchive();

                // Load the previously selected directory if it still exists
                if (selectedDirAddr != null)
                {
                    ExplorerDialogViewModel.LoadDirectory(selectedDirAddr);
                }

                Archive.SetDisplayStatus(String.Empty);
            }
        }
    }