Register for your free account! | Forgot your password?

Go Back   elitepvpers > MMORPGs > Conquer Online 2 > CO2 Programming
You last visited: Today at 11:11

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



Read from a file package

Discussion on Read from a file package within the CO2 Programming forum part of the Conquer Online 2 category.

Reply
 
Old   #1
 
elite*gold: 0
Join Date: Sep 2012
Posts: 51
Received Thanks: 32
Read from a file package

While improving my C# programming skills, I wrote and rewrote lot of code, trying each time to use my new knowledge. Now I'm starting again to write from scratch my code to handle TQ files.
To access files inside a package, I wonder if it is a good idea to use memory (byte array, memory stream...) or better implement a substream to read files inside a package. For example, if I need to read several (or a lot) files inside a WDF package, what would be better, a substream or a copy in memory?
Currently my WDF class uses a memory stream or a byte array to access files inside a WDF package, but I would like to know if the memory saving is worth to use a substream class, with all their disadvantages. I already wrote a substream class, not fully tested yet, but working.
Any comment would be appreciated.

Code:
using System;
using System.IO;

namespace TQGames_Core.IO
{
    /// <summary>
    /// Provides a generic view of a subset of a sequence of bytes (a stream)
    /// </summary>
    public class SubStream : Stream, IDisposable
    {
        #region Fields
        private long _start;
        private long _length;
        private Stream _baseStream = null;
        private bool _readOnly = false;
        private bool _disposed = false;
        #endregion Fields

        #region Properties
        /// <summary>
        /// Get or set the position within the current substream
        /// </summary>
        public override long Position {
            get
            {
                return this._baseStream.Position - this._start;
            }
            set
            {
                if (!this._baseStream.CanSeek)
                {
                    throw new NotSupportedException("Base stream does not support seek");
                }
                else
                {
                    if (value > this._length)
                    {
                        throw new ArgumentOutOfRangeException("Position", "Attempted to move beyond end of stream");
                    }
                    else
                    {
                        this._baseStream.Position = value + this._start;
                    }
                }
            }
        }

        /// <summary>
        /// Get the start offset within the base stream
        /// </summary>
        public long Start
        {
            get
            {
                return this._start;
            }
        }

        /// <summary>
        /// Get the lenght of the substream
        /// </summary>
        public override long Length
        {
            get
            {
                return this._length;
            }
        }

        /// <summary>
        /// Get a value indicating whether the current substream supports reading
        /// </summary>
        public override bool CanRead
        {
            get { return this._baseStream.CanRead; }
        }

        /// <summary>
        /// Get a value indicating whether the current substream supports writing
        /// </summary>
        public override bool CanWrite
        {
            get 
            {
                if (this._readOnly)
                    return false;
                else
                    return true;
            }
        }

        /// <summary>
        /// Get a value indicating whether the current substream supports seeking
        /// </summary>
        public override bool CanSeek
        {
            get
            {
                return this._baseStream.CanSeek;
            }
        }

        /// <summary>
        /// Get a value indicating whether the current substream can time out
        /// </summary>
        public override bool CanTimeout
        {
            get
            {
                return base.CanTimeout;
            }
        }

        /// <summary>
        /// Get or set a value, in miliseconds, that determines how long the substream will attempt to read before timing out
        /// </summary>
        public override int ReadTimeout
        {
            get
            {
                return base.ReadTimeout;
            }
            set
            {
                base.ReadTimeout = value;
            }
        }

        /// <summary>
        /// Get or set a value, in miliseconds, that determines how long the substream will attempt to write before timing out
        /// </summary>
        public override int WriteTimeout
        {
            get
            {
                return base.WriteTimeout;
            }
            set
            {
                base.WriteTimeout = value;
            }
        }

        /// <summary>
        /// Get a value indicating whether the current substream has been already disposed
        /// </summary>
        public bool Disposed
        {
            get
            {
                if (!this._baseStream.CanRead && !this._baseStream.CanWrite)
                {
                    Dispose(true);
                }
                return this._disposed;
            }
        }
        #endregion Properties

        #region Constructor
        /// <summary>
        /// New instance with provided data
        /// </summary>
        /// <param name="baseStream">Base stream to get a subset</param>
        /// <param name="start">The start offset within the base stream</param>
        /// <param name="length">The lenght of the substream</param>
        /// <param name="readOnly">Optional, true if it must be read only, false by default</param>
        public SubStream(Stream baseStream, long start, long length, bool readOnly = false)
        {
            if (start < 0)
            {
                throw new ArgumentOutOfRangeException("Substream extends before base stream start");
            }
            if (start + length > baseStream.Length)
            {
                throw new ArgumentOutOfRangeException("Substream extends beyond end of base stream");
            }
            if (!readOnly && !baseStream.CanWrite)
            {
                throw new ArgumentException("Base stream is not writable");
            }
            this._baseStream = baseStream;
            this._start = start;
            this._length = length;
            this._readOnly = readOnly;
        }
        #endregion Constructor

        #region Public Methods
        /// <summary>
        /// Close the current substream and release any resources (such as sockets and file handles) associated with the base stream.
        /// Instead of calling this method, ensure that the substream is properly disposed
        /// </summary>
        public override void Close()
        {
            base.Close();
        }

        /// <summary>
        /// Releases the unmanaged resources used by the Stream and optionally releases the managed resources
        /// </summary>
        /// <param name="disposing">False to dispose only unmanaged resources (called by Garbage Collector)</param>
        protected override void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                this._disposed = true;
                if (disposing)
                {
                    // Release managed objects
                    if (this._baseStream != null)
                    {
                        this._baseStream.Dispose();
                        this._baseStream = null;
                    }
                }
                // Release unmanaged objects

                // Set large fields to null

                // Call Dispose on the base class
                base.Dispose(disposing);
            }
        }

        /// <summary>
        /// Set the lenght of the substream
        /// </summary>
        /// <param name="value"></param>
        public override void SetLength(long value)
        {
            throw new NotSupportedException("Attempt to change length of a substream");
        }

        /// <summary>
        /// Clear all buffers for the base stream and causes any buffered data to be written to the underlying device
        /// </summary>
        public override void Flush()
        {
            this._baseStream.Flush();
        }

        /// <summary>
        /// Set the position within the current substream
        /// </summary>
        /// <param name="offset">A byte offset relative to the origin parameter</param>
        /// <param name="origin">A value of type <typeparamref name="SeekOrigin"/> indicating the reference point used to obtain the new position</param>
        /// <returns>The new position within the current substream</returns>
        public override long Seek(long offset, SeekOrigin origin)
        {
            if (!this._baseStream.CanSeek)
            {
                throw new NotSupportedException("Base stream does not support seek");
            }
            long newPosition = offset;
            if (origin == SeekOrigin.Begin)
            {
                newPosition += this._start;
            }
            else if (origin == SeekOrigin.Current)
            {
                newPosition += this._baseStream.Position;
            }
            else if (origin == SeekOrigin.End)
            {
                newPosition += this._start + this._length;
            }
            if (newPosition < this._start)
            {
                throw new ArgumentOutOfRangeException("offset", "Attempt to move before start of stream");
            }
            if (newPosition > this._start + this._length)
            {
                throw new ArgumentOutOfRangeException("offset", "Attempt to move beyond end of stream");
            }
            this._baseStream.Position = newPosition;
            return this._baseStream.Position - this._start;
        }

        /// <summary>
        /// Read a byte from the substream and advances the position within the substream by one byte, or returns -1 if at the end of the substream
        /// </summary>
        /// <returns>The unsigned byte cast to an Int32, or -1 if at the end of the substream</returns>
        //TODO: Check if the current position is within the substream
        public override int ReadByte()
        {
            return base.ReadByte();
        }

        /// <summary>
        /// Read a sequence of bytes from the current substream and advances the position within the substream by the number of bytes read
        /// </summary>
        /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current substream</param>
        /// <param name="count">The maximum number of bytes to be read from the current substream</param>
        /// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the substream has been reached</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (this._disposed)
            {
                throw new ObjectDisposedException("SubStream", "Attempt to read from a disposed SubStream");
            }
            if (!this._baseStream.CanRead)
            {
                throw new NotSupportedException("The stream does not support reading.");
            }
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (offset + count > buffer.Length)
            {
                throw new ArgumentException("Buffer is too small to read required bytes");
            }
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset", "offset cannot be negative");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count", "Attempt to read negative amount of bytes");
            }
            if (this._baseStream.Position > this._length)
            {
                return 0;
            }
            if (this._baseStream.Position + count > this._length)
            {
                count = (int)(this._length - this._baseStream.Position);
            }
            if (count > int.MaxValue) count = int.MaxValue;
            int amountRead = this._baseStream.Read(buffer, offset, count);
            return amountRead;
        }

        /// <summary>
        /// Begin an asynchronous read operation
        /// </summary>
        /// <param name="buffer">The buffer to read the data into</param>
        /// <param name="offset">The byte offset in buffer at which to begin writing data read from the substream</param>
        /// <param name="count">The maximum number of bytes to read</param>
        /// <param name="callback">An optional asynchronous callback, to be called when the read is complete</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous read request from other requests</param>
        /// <returns>An IAsyncResult that represents the asynchronous read, which could still be pending</returns>
        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            return base.BeginRead(buffer, offset, count, callback, state);
        }

        /// <summary>
        /// Wait for the pending asynchronous read to complete
        /// </summary>
        /// <param name="asyncResult">The reference to the pending asynchronous request to finish</param>
        /// <returns>The number of bytes read from the substream, between zero (0) and the number of bytes you requested. Return zero (0) only at the end of the substream, otherwise, they should block until at least one byte is available</returns>
        public override int EndRead(IAsyncResult asyncResult)
        {
            return base.EndRead(asyncResult);
        }

        /// <summary>
        /// Write a byte to the current position in the substream and advances the position within the substream by one byte
        /// </summary>
        /// <param name="value">The byte to write to the substream</param>
        public override void WriteByte(byte value)
        {
            base.WriteByte(value);
        }

        /// <summary>
        /// Write a sequence of bytes to the current substream and advances the current position within this substream by the number of bytes written
        /// </summary>
        /// <param name="buffer">An array of bytes. This method copies count bytes from buffer to the current substream</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current substream</param>
        /// <param name="count">The number of bytes to be written to the current substream</param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (this._readOnly)
            {
                throw new NotSupportedException("Substream is not writable");
            }
            if (!this._baseStream.CanWrite)
            {
                throw new NotSupportedException("Base stream is not writable");
            }
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException("offset", "offset cannot be negative");
            }
            if (count < 0)
            {
                throw new ArgumentOutOfRangeException("count", "Attempt to write negative amount of bytes");
            }
            if ((this._baseStream.Position + count) > (this._start + this._length))
            {
                throw new ArgumentOutOfRangeException("count", "Attempt to write beyond end of substream");
            }
            this._baseStream.Write(buffer, offset, count);
        }

        /// <summary>
        /// Begin an asynchronous write operation
        /// </summary>
        /// <param name="buffer">The buffer to write data from</param>
        /// <param name="offset">The byte offset in buffer from which to begin writing</param>
        /// <param name="count">The maximum number of bytes to write</param>
        /// <param name="callback">An optional asynchronous callback, to be called when the write is complete</param>
        /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests</param>
        /// <returns>An IAsyncResult that represents the asynchronous write, which could still be pending</returns>
        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            return base.BeginWrite(buffer, offset, count, callback, state);
        }

        /// <summary>
        /// End an asynchronous write operation
        /// </summary>
        /// <param name="asyncResult">A reference to the outstanding asynchronous I/O request</param>
        public override void EndWrite(IAsyncResult asyncResult)
        {
            base.EndWrite(asyncResult);
        }
        #endregion Public Methods
    }
}
Code:
using System;
using System.Collections.Generic;
using System.IO;
using TQGames_Core.IO;

namespace TQGames_Core.IO.Packages
{
    //TODO: ExtractFilesFromAni (Extract all files referenciated inside an ani file) 
    /// <summary>
    /// Handles WindSoul Data File Packages
    /// <remarks>Only implemented read operations</remarks>
    /// </summary>
    public class WDF : IDisposable
    {
        #region Constants
        /// <summary>
        /// All WindSoul Data File Packages start with "PFDW" (Package File Data WindSoul)
        /// </summary>
        public static UInt32 WDF_ID = 0x57444650;                       // "PFDW" = Windsoul Data File Package
        #endregion Constants

        #region Fields
        private object _thisLock = new object();
        private bool _disposed = false;
        private string _filename = null;
        private FileStream _fs = null;
        private WDF_Header _header = new WDF_Header();
        private Dictionary<UInt32, WDF_InnerFileHeader> _InnerFiles = null;
        private Dictionary<UInt32, string> _fileTypes = null;
        #endregion Fields

        #region Properties
        /// <summary>
        /// Gets a boolean value indicating if the instance has been already disposed
        /// </summary>
        public bool Disposed { get { return this._disposed; } }
        
        /// <summary>
        /// Gets the wdf file name
        /// </summary>
        public string Filename { get { return this._filename; } }

        /// <summary>
        /// Gets the amount of files packed in the wdf
        /// </summary>
        public Int32 Amount { get { return this._header.Amount; } }

        /// <summary>
        /// Gets a list of files hash
        /// </summary>
        public List<UInt32> HashList { get { return new List<UInt32>(this._InnerFiles.Keys); } }

        /// <summary>
        /// Gets a list of files headers
        /// </summary>
        public List<WDF_InnerFileHeader> HeaderList { get { return new List<WDF_InnerFileHeader>(this._InnerFiles.Values); } }

        /// <summary>
        /// Gets the file header corresponding to the specified hash
        /// </summary>
        /// <param name="hash">hash of the file to check</param>
        /// <returns>the file header</returns>
        /// <exception cref="KeyNotFoundException">if the WDF does not contain this hash</exception>
        public WDF_InnerFileHeader this[UInt32 hash]
        {
            get
            {
                WDF_InnerFileHeader innerFile;
                if (!this._InnerFiles.TryGetValue(hash, out innerFile))
                {
                    throw new KeyNotFoundException();
                }
                return innerFile;
            }
        }
        #endregion Properties

        #region Constructor
        // TODO: not implemented yet
        /// <summary>
        /// Create a new WDF file from specified list of pathnames
        /// </summary>
        /// <param name="pathnamesList">list of files (full pathnames) to pack in the wdf store</param>
        public WDF(List<string> pathnamesList)
        {
            throw new NotImplementedException("Not implemented");
        }

        /// <summary>
        /// Create a new instance to handle an existing WDF file with specified fileName
        /// </summary>
        /// <param name="fileName">wdf file name</param>
        public WDF(string fileName)
        {
            if (!File.Exists(fileName))
            {
                throw new FileNotFoundException("WDF file " + fileName + " not found.");
            }
            else
            {
                this._filename = fileName;
                this._InnerFiles = new Dictionary<UInt32, WDF_InnerFileHeader>();
                this._fileTypes = new Dictionary<UInt32, string>();
                this._fs = new FileStream(this._filename, FileMode.Open, FileAccess.Read, FileShare.Read);
                ReadHeader();
                // GuessAllFilesType();
            }
        }
        #endregion Constructor

        #region Public Methods
        /// <summary>
        /// Release used resources
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Release used resources
        /// </summary>
        /// <param name="disposing">false to dispose only unmanaged resources, true to dispose all resources</param>
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this._disposed)
            {
                lock (this._thisLock)
                {
                    // If disposing equals true, dispose all managed and unmanaged resources.
                    if (disposing)
                    {
                        // Dispose managed resources.
                        this._fs.Close();
                        this._fs.Dispose();
                    }
                    // Dispose unmanaged resources.
                }
            }
        }
        
        /// <summary>
        /// Close the wdf file
        /// </summary>
        public void Close()
        {
            this._InnerFiles.Clear();
            this._fileTypes.Clear();
            this._fs.Close();
        }

        /// <summary>
        /// Check if the WDF store contains the specified file
        /// </summary>
        /// <param name="pathName">full path+filename to check</param>
        /// <returns>true if found</returns>
        public bool ContainsFile(string pathName)
        {
            lock (this._thisLock)
            {
                if (this._InnerFiles.ContainsKey(Crypto.HashNames.String2Id(pathName)))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Check if the WDF store contains the file with specified hash
        /// </summary>
        /// <param name="hash">hash of the full path+filename to check</param>
        /// <returns>true if found</returns>
        public bool ContainsFile(UInt32 hash)
        {
            lock (this._thisLock)
            {
                if (this._InnerFiles.ContainsKey(hash))
                    return true;
            }
            return false;
        }

        /// <summary>
        /// Extract the file specified by "hash" and write using the binarywriter "bw"
        /// <remarks>Does not check if reach end of WDF file</remarks>
        /// </summary>
        /// <param name="bw">BinaryWriter to write to</param>
        /// <param name="hash">hash of the full pathname of the file to extract</param>
        /// <returns>false if not found, true if success</returns>
        public bool ExtractFile(BinaryWriter bw, UInt32 hash)
        {
            // Check if the file is in the .WDF package
            if (!ContainsFile(hash))
            {
                // To do: Alert in some way the file was not found in this .WDF package
                return false;
            }
            // Read file data from WDF
            byte[] buffer = new byte[1024];
            UInt32 numBlocks = this._InnerFiles[hash].Size / 1024;
            int rest = (int)this._InnerFiles[hash].Size % 1024;
            this._fs.Seek(_InnerFiles[hash].Offset, SeekOrigin.Begin);   // Seek for the first byte
            // Write data to streamwriter bw
            for (int i = 0; i < numBlocks; i++)
            {
                this._fs.Read(buffer, 0, 1024);
                bw.Write(buffer);
            }
            this._fs.Read(buffer, 0, rest);
            bw.Write(buffer);
            bw.Flush();
            return true;
        }

        /// <summary>
        /// Extract the file specified by "hash" and write it to the specified filder, using its hash as filename and guessing extension
        /// </summary>
        /// <param name="targetFolder">target folder to write the file</param>
        /// <param name="hash">hash of the full pathname of the file to extract</param>
        /// <returns>false if not found, true if success</returns>
        public bool ExtractFile(string targetFolder, UInt32 hash)
        {
            // Check if the file is in the .WDF package
            if (!ContainsFile(hash))
            {
                // To do: Alert in some way the file was not found in this .WDF package
                return false;
            }
            string filename = hash.ToString() + GuessFileType(hash);
            string targetPath = targetFolder + Path.DirectorySeparatorChar + filename;
            // Open the new file in write access mode
            BinaryWriter bw = new BinaryWriter(File.Open(targetPath, FileMode.Create));
            // Extract data from WDF and write to the new file
            ExtractFile(bw, hash);
            bw.Close();
            return true;
        }

        /// <summary>
        /// Extract the file specified by "path" and write to a new file in the specified folder (overwrites if already exists)
        /// </summary>
        /// <param name="targetFolder">target folder to write the file</param>
        /// <param name="pathName">full pathname of the file to extract</param>
        /// <returns>false if not found, true if success</returns>
        public bool ExtractFile(string targetFolder, string pathName)
        {
            // Get the hashName for "path"
            UInt32 hash = Crypto.HashNames.String2Id(pathName);
            // Check if the file is in the .WDF package
            if (!ContainsFile(hash))
            {
                // To do: Alert in some way the file was not found in this .WDF package
                return false;
            }
            // Full pathName for the new file
            string targetPath = targetFolder + Path.DirectorySeparatorChar + pathName.Replace('\\', '/');
            // Create the folder structure as needed
            Directory.CreateDirectory(Path.GetDirectoryName(targetPath));
            // Open the new file in write access mode
            BinaryWriter bw = new BinaryWriter(File.Open(targetPath, FileMode.Create));
            // Extract data from WDF and write to the new file
            ExtractFile(bw, hash);
            bw.Close();
            return true;
        }

        /// <summary>
        /// Extract all files and write to the specified folder, using hash as filename and guessing extension
        /// </summary>
        /// <param name="targetFolder">target folder to write the files</param>
        /// <returns>amount of files extracted</returns>
        public UInt32 ExtractAllFiles(string targetFolder)
        {
            UInt32 filesCount = 0;
            foreach (UInt32 hash in this._InnerFiles.Keys)
            {
                string filename = hash.ToString() + GuessFileType(hash);
                // Full pathName for the new file
                string targetPath = targetFolder + Path.DirectorySeparatorChar + filename;
                // Open the new file in write access mode
                BinaryWriter bw = new BinaryWriter(File.Open(targetPath, FileMode.Create));
                // Extract data from WDF and write to the new file
                if (ExtractFile(bw, hash)) filesCount++;
                bw.Close();
            }
            return filesCount;
        }

        // Todo: not implemented yet
        /// <summary>
        /// Extract all files referenciated inside an ani file
        /// </summary>
        /// <param name="aniPath">pathname of the ani file</param>
        /// <param name="targetFolder">target folder to write the files</param>
        /// <returns>amount of files extracted</returns>
        public UInt32 ExtractFilesFromAni(string aniPath, string targetFolder)
        {
            throw new NotImplementedException();
            // UInt32 filesCount = 0;
            // return filesCount;
        }

        /// <summary>
        /// Guess the file type for the specified hash
        /// </summary>
        /// <param name="hash">hash of the full pathname of the file to check</param>
        /// <returns>guessed extension of the file</returns>
        public string GuessFileType(UInt32 hash)
        {
            if (this._InnerFiles.ContainsKey(hash))
            {
                byte[] buffer = new byte[16];
                this._fs.Seek(this._InnerFiles[hash].Offset, SeekOrigin.Begin);   // Seek for the first byte
                this._fs.Read(buffer, 0, 16);
                string ext = GuessFileExt(buffer);
                return ext;
            }
            else
            {
                throw new FileNotFoundException("Cannot find " + hash + " in the file: " + this._filename);
            }
        }

        /// <summary>
        /// Read all bytes for a file inside the WDF
        /// </summary>
        /// <param name="pathName">full pathname of the file</param>
        /// <returns></returns>
        public byte[] Read(string pathName)
        {
            // Get the hashName for "path"
            UInt32 hash = Crypto.HashNames.String2Id(pathName);
            return Read(hash);
        }

        /// <summary>
        /// Read all bytes for a file inside the WDF
        /// </summary>
        /// <param name="hash">hash of the full pathname</param>
        /// <returns></returns>
        public byte[] Read(UInt32 hash)
        {
            // Check if the file is in the .WDF package
            if (!ContainsFile(hash))
            {
                // Todo: Alert in some way the file was not found in this .WDF package
                return null;
            }
            byte[] buffer = new byte[this._InnerFiles[hash].Size];
            this._fs.Seek(_InnerFiles[hash].Offset, SeekOrigin.Begin);      // Seek for the first byte
            this._fs.Read(buffer, 0, (int)this._InnerFiles[hash].Size);     // Read all bytes
            return buffer;
        }
        
        /// <summary>
        /// Get a readonly stream to access a file inside the WDF
        /// </summary>
        /// <param name="pathName">full pathname of the file</param>
        /// <returns>a readonly stream</returns>
        public Stream GetStream(string pathName)
        {
            // Get the hashName for "path"
            UInt32 hash = Crypto.HashNames.String2Id(pathName);
            return GetStream(hash);
        }

        /// <summary>
        /// Get a readonly stream to access a file inside the WDF
        /// </summary>
        /// <param name="hash">hash of the full pathname</param>
        /// <returns>a readonly stream</returns>
        public Stream GetStream(UInt32 hash)
        {
            // Check if the file is in the .WDF package
            if (!ContainsFile(hash))
            {
                // Todo: Alert in some way the file was not found in this .WDF package
                return null;
            }
            Stream s = new MemoryStream(Read(hash), false);
            return s;
        }
        #endregion Public Methods

        #region Private Methods
        // Read the WDF Header and the file entries table
        private void ReadHeader()
        {
            lock (_thisLock)
            {
                byte[] buffer = new byte[16];
                this._fs.Read(buffer, 0, 12);     // Read WDF Header
                this._header.Id = BitConverter.ToUInt32(buffer, 0);
                if (this._header.Id != WDF_ID)
                {
                    throw new InvalidDataException("Invalid WDF Header in file: " + this._filename);
                }
                this._header.Amount = BitConverter.ToInt32(buffer, 4);
                this._header.Offset = BitConverter.ToUInt32(buffer, 8);
                this._fs.Seek(this._header.Offset, SeekOrigin.Begin);   // Seek for the first inner file header
                // Read all headers for inner files
                for (Int32 i = 0; i < this._header.Amount; i++)
                {
                    this._fs.Read(buffer, 0, 16);
                    WDF_InnerFileHeader fh = new WDF_InnerFileHeader();
                    fh.NameHash = BitConverter.ToUInt32(buffer, 0);
                    fh.Offset = BitConverter.ToUInt32(buffer, 4);
                    fh.Size = BitConverter.ToUInt32(buffer, 8);
                    fh.Space = BitConverter.ToUInt32(buffer, 12);
                    if (this._InnerFiles.ContainsKey(fh.NameHash))
                    {
                        // throw new Exception("Duplicated " + fh.NameHash + " in the file: " + this._filename);
                    }
                    else
                    {
                        this._InnerFiles.Add(fh.NameHash, fh);
                    }
                }
            }
        }

        // Guess the file extension by comparing first bytes with known Magic Types
        private string GuessFileExt(byte[] b)
        {
            if (IsMagicType(b, Common.DDS_ID)) return ".dds";
            if (IsMagicType(b, Common.JPG_ID)) return ".jpg";
            if (IsMagicType(b, Common.BMP_ID)) return ".bmp";
            if (IsMagicType(b, Common.C3_ID)) return ".c3";
            if (IsMagicType(b, Common.WAV_ID)) return ".wav";
            if (IsMagicType(b, Common.MP3a_ID)) return ".mp3";
            if (IsMagicType(b, Common.MP3b_ID)) return ".mp3";
            if (IsMagicType(b, Common.ID3_ID)) return ".mp3";
            if (IsMagicType(b, Common.CWS_ID)) return ".swf";
            if (IsMagicType(b, Common.UTF8_ID)) return ".txt";
            if (IsMagicType(b, Common.PNG_ID)) return ".png";
            if (IsMagicType(b, Common.RSDB_ID)) return ".dbc";
            if (IsMagicType(b, Common.MESH_ID)) return ".dbc";
            if (IsMagicType(b, Common.PUL_ID)) return ".pul";
            return ".bin";  // By default, if unknown file type
        }

        // Compare read bytes with a file magic type
        private bool IsMagicType(byte[] b, byte[] mt)
        {
            for (int i = 0; i < mt.Length; i++)
            {
                if (b[i] != mt[i]) return false;
            }
            return true;
        }

        // Populate FilesType with data from guessing file extensions
        private void GuessAllFilesType()
        {
            _fileTypes.Clear();
            byte[] buffer = new byte[16];
            lock (_fileTypes)
            {
                foreach (KeyValuePair<UInt32, WDF_InnerFileHeader> file in _InnerFiles)
                {
                    if (_fileTypes.ContainsKey(file.Value.NameHash))
                    {
                        // throw new Exception("Duplicated " + file.Value.NameHash + " in the file: " + Filename);
                    }
                    else
                    {
                        _fs.Seek(file.Value.Offset, SeekOrigin.Begin);   // Seek for the first byte
                        _fs.Read(buffer, 0, 16);
                        string ext = GuessFileExt(buffer);
                        _fileTypes.Add(file.Value.NameHash, ext);
                    }
                }
            }
        }
        #endregion Private Methods
    }

    /// <summary>
    /// Windsoul Data File Package Header
    /// </summary>
    public struct WDF_Header
    {
        /// <summary>Must be PFDW</summary>
        public UInt32 Id;
        /// <summary>Amount of files</summary>
        public Int32 Amount;
        /// <summary>Offset address of the first file data</summary>
        public UInt32 Offset;
    };

    /// <summary>
    /// WindSoul Data File Package Inner File Header
    /// </summary>
    public struct WDF_InnerFileHeader
    {
        /// <summary>Hash of the full path+filename</summary>
        public UInt32 NameHash;
        /// <summary>Offset address of the first byte of data</summary>
        public UInt32 Offset;
        /// <summary>Size of the file</summary>
        public UInt32 Size;
        /// <summary>Space used in the WDF store</summary>
        public UInt32 Space;
        //   Space usually is equal to size, but can be higher to size
        //   when file was updated after WDF creation (new file is smaller)
    };
}
Code:
using System;
using System.Text;

namespace TQGames_Core.Crypto
{
    /// Static class to handle hashnames from everywhere
    public static class HashNames
    {
        /// <summary>Get the hash number Id from a full path+filename
        /// <para>Adapted from WindSoul Game Engine source code</para>
        /// <para>Function: unsigned long string_id(const char *str)</para>
        /// <para>http://read.pudn.com/downloads76/sourcecode/game/281928/%E9%A3%8E%E9%AD%82/wdfpck.cpp__.htm</para>
        /// <param name="str">String to convert into hash Id</param>
        /// <returns>a 32 bits unique Id</returns>
        /// </summary>
        public static UInt32 String2Id(string str)
        {
            const UInt32 MAX_INT32 = 0xFFFFFFFF;    // Max value for a 32 bits integer
            const UInt32 ADD_1 = 0x9BE74448;        // 
            const UInt32 ADD_2 = 0x66F42C48;        // ADD_1 and ADD_2 are added to the end of the string
            const UInt32 V_0 = 0xF4FA8928;          // Rotative mask, initial value
            const UInt32 X_0 = 0x37A8470E;          // Common mask x, initial value
            const UInt32 Y_0 = 0x7758B42B;          // Common mask y, initial value
            const UInt32 MASK_W = 0x267B0B11;       // 0010 1100 0111 1011 0000 1011 0001 0001
            const UInt32 MASK_A = 0x02040801;       // 0000 0010 0000 0100 0000 1000 0000 0001
            const UInt32 MASK_B = 0x00804021;       // 0000 0000 1000 0000 0100 0000 0010 0001
            const UInt32 MASK_C = 0xBFEF7FDF;       // 1011 1111 1110 1111 0111 1111 1101 1111
            const UInt32 MASK_D = 0x7DFEFBFF;       // 0111 1101 1111 1110 1111 1011 1111 1111
            UInt32 v = V_0;                         // Rotative mask
            UInt32 x = X_0;                         // Common mask x
            UInt32 y = Y_0;                         // Common mask y
            Int32 length;                           // String lenght, counted in 32 bits integers
            UInt32 low;                             // To handle 32bits numbers
            UInt64 high, op_1, op_2;                // To handle 64bits numbers

            // Replace '\\' to '/' and all letters to lowercase
            str = str.Replace('\\', '/');
            str = str.ToLowerInvariant();

            // Create an array (m) of unsigned int to store the string
            ASCIIEncoding enc = new ASCIIEncoding();    // Use ASCII to get unchanged bytes
            Byte[] by = new Byte[280];                  // The string should be less than 256 bytes
            Array.Clear(by, 0, 280);                    // Fill with zeroes
            UInt32[] m = new UInt32[70];                // 70 x 32 bits integers can host 280 bytes
            Array.Clear(m, 0, 70);                      // Fill with zeroes
            by = enc.GetBytes(str);
            Buffer.BlockCopy(by, 0, m, 0, str.Length);

            // Check the lenght of m, actually used by str and add 2 values to the end
            for (length = 0; length < 64 && m[length] != 0; length++) ; // length appoint to the last integer not zero in the m array 
            m[length++] = ADD_1;
            m[length++] = ADD_2;

            // With each int from the string perform several operations
            for (int i = 0; i < length; i++)
            {
                // Rotate left v 1 bit for each 32bits m[i]
                v = (v << 1) | (v >> 0x1F);
                // Mask x and y with the current 32bits m[i]
                x ^= m[i];
                y ^= m[i];

                // Operate to x and y several masks
                // MASK_C and MASK_D disable 4 bits each
                // MASK_A and MASK_B enable 4 bits each
                // MASK_W switches 14 bits from rotative mask v
                op_1 = (UInt64)y * ((((v ^ MASK_W) + x) | MASK_B) & MASK_D);
                op_2 = (UInt64)x * ((((v ^ MASK_W) + y) | MASK_A) & MASK_C);

                // if op_1 has more than 32 bits, op_1 = lower32bits + 2 * higher32bits
                low = (UInt32)op_1;
                high = (UInt64)2 * ((UInt32)(op_1 >> 32));
                op_1 = (UInt64)low + (UInt32)high;
                // if 2 * higher32bits was again more than 32 bits, add 1
                if (high > MAX_INT32) op_1++;

                // if op_2 has more than 32 bits, op_2 = lower32bits + higher32bits + 1
                // otherwise take just the lower32bits 
                op_2 = (op_2 > MAX_INT32) ? (UInt64)1 + (UInt32)op_2 + (UInt32)(op_2 >> 32) : (UInt32)op_2;

                // Store in x the lower32bits from the result of operation2
                // and add 1 if the last operation exceeded 32 bits
                x = (UInt32)op_2;
                if (op_2 > MAX_INT32) x++;
                // Store in y the lower32bits from the result of operation1
                // and add 2 if the last operation exceeded 32 bits
                y = (UInt32)op_1;
                if (op_1 > MAX_INT32) y += 2;
            }

            // Mask x with y and return the result
            UInt32 Id = x ^ y;
            return Id;
        }
    }
}
urgabel is offline  
Old 07/06/2013, 03:12   #2
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 950
You're never disposing the baseStream.
Super Aids is offline  
Thanks
1 User
Old 07/06/2013, 04:13   #3
 
elite*gold: 0
Join Date: Sep 2012
Posts: 51
Received Thanks: 32
Thanks , updated the "Dispose" method:
Code:
        protected override void Dispose(bool disposing)
        {
            if (!this._disposed)
            {
                this._disposed = true;
                if (disposing)
                {
                    // Release managed objects
                    if (this._baseStream != null)
                    {
                        this._baseStream.Dispose();
                        this._baseStream = null;
                    }
                }
                // Release unmanaged objects

                // Set large fields to null

                // Call Dispose on the base class
                base.Dispose(disposing);
            }
        }
Any other comment? What do you think about the main question? Is it worth to read directly from the WDF package each time, or better get a copy in memory? Should I cache files already in use?
My plan is to standardize the way I access the files inside the packages (files inside a WDF package, files inside a WDB package, sections inside an ani file...), if it is worth.
urgabel is offline  
Reply


Similar Threads Similar Threads
Which file formats can be read?
12/30/2011 - SRO Coding Corner - 3 Replies
So far I only know of reading ddj-s. Are there tools for bsr or dof files out there?
LUA file write/read
08/21/2011 - Metin2 Private Server - 4 Replies
Hallo =) Ich würde gerne wissen ob es möglich ist mit den LUA Befehlen von Mt Textdateien zu schreiben und ob das irgend ein Server verwendet, ggf Erfahrungen mit so einem System gesammelt hat. Danke im vorraus! grüßle LordMampf2
Read Input from .txt file
07/02/2011 - General Coding - 2 Replies
Hallo, Ich habe ein Programm, was über die CMD Konsole ausgeführt wird. In drei verschiedenen Dialogen werden Tasteneingaben eingelesen, um den weiteren Programmablauf zu bestimmen. Diese drei Eingaben will ich jetzt über eine Textdatei bestimmen Ich hab mir das ungefähr so vorgestellt: Man hat eine txt Datei die pro Zeile einen Buchstaben enthält: y
File Read Problem
11/25/2010 - AutoIt - 4 Replies
Hi all Könnt mir jemand diese Func "zurecht" biegen ? Des tuts einfach net Func _such() For $File = 0 to UBound($File)-1 If FileReadLine($File) == "Aa" Then MsgBox(0 , "File Reader" , "Dieses Wort wurde gefunden") Else
how can i read .dat file or .c3 file?
09/09/2009 - Conquer Online 2 - 5 Replies
Hello can anybody tell me how can i read .dat file or .c3 file? i rly need this please help me guys if ya know how to do it:)



All times are GMT +1. The time now is 11:12.


Powered by vBulletin®
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2025 elitepvpers All Rights Reserved.