User Tools

Site Tools


falcon4:file_formats:rsc_idx_fileformat

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
falcon4:file_formats:rsc_idx_fileformat [2009-02-06 21:51]
lightning
falcon4:file_formats:rsc_idx_fileformat [2009-02-07 07:02] (current)
Line 7: Line 7:
 below ONLY APPLY to .IDX files that index a corresponding .RSC (resource bundle) file.  Other types of .IDX files ARE NOT below ONLY APPLY to .IDX files that index a corresponding .RSC (resource bundle) file.  Other types of .IDX files ARE NOT
 described below. described below.
 +
 +**NOTE ON DATA TYPES AND ENDIAN-NESS:​** The data type "​long"​ refers to a 32-bit (double-WORD) data type.  A "​short"​ refers to a 16-bit (double-byte,​ a.k.a single WORD) data type.  The byte order on disk is little-endian,​ so, for example, the following four sequential bytes on disk { ''​%%0xDD%%'',​ ''​%%0xC8%%'',​ ''​%%0x3F%%'',​ ''​%%0x02%%''​ }, when treated as a "​long",​ would translate to the value ''​%%0x023FC8DD%%''​ (the reverse sequence).  ​
  
 ===== .IDX FILE FORMAT (for .IDX files that index a corresponding .RSC file) ===== ===== .IDX FILE FORMAT (for .IDX files that index a corresponding .RSC file) =====
Line 72: Line 74:
 |Version|''​%%0x04%%''​|4|long|The version number of the file format being used for this .RSC file (must match the version number of the corresponding .IDX file)\\ example: ''​%%0x023fc8dd%%''​| |Version|''​%%0x04%%''​|4|long|The version number of the file format being used for this .RSC file (must match the version number of the corresponding .IDX file)\\ example: ''​%%0x023fc8dd%%''​|
  
-Immediately following the HEADER section in the .RSC file, comes the DATA section. ​ The DATA section extends from absolute offset ''​%%0x08%%''​ in the .RSC file, to the end of the file.  Individual records can be extracted from the .RSC file by first parsing the corresponding .IDX file in order to understand the types of resources that the .RSC file contains data for, as well as discovering those resources'​ locations and sizes within the .RSC file.  ​ 
  
 ==== .RSC FILE DATA SECTION ==== ==== .RSC FILE DATA SECTION ====
-The .RSC file'​s ​DATA section ​begins at absolute offset ''​%%0x08%%''​ in the .RSC file, and extends ​to the end of the file.+Immediately following the HEADER section in the .RSC file, comes the DATA section.   
 + 
 +The DATA section extends from absolute offset ''​%%0x08%%''​ in the .RSC file, to the end of the file.  ​Individual records can be extracted from the .RSC file by first parsing the corresponding .IDX file in order to understand the types of resources that the .RSC file contains data for, as well as discovering those resources'​ locations and sizes within the .RSC file.  ​
  
 **NOTE:** all values of all .Offset fields within indidivual records in the .IDX file, specify offsets **relative** to the **start** of the DATA section in the .RSC file.  For example, if the .IDX record specifies an offset of ''​%%0x00%%'',​ the actual data would be located in the .RSC file starting at **absolute offset** = ''​%%0x08%%''​ (i.e., 0 bytes past the start of the DATA section, which itself starts at **absolute offset**=''​%%0x08%%''​) **NOTE:** all values of all .Offset fields within indidivual records in the .IDX file, specify offsets **relative** to the **start** of the DATA section in the .RSC file.  For example, if the .IDX record specifies an offset of ''​%%0x00%%'',​ the actual data would be located in the .RSC file starting at **absolute offset** = ''​%%0x08%%''​ (i.e., 0 bytes past the start of the DATA section, which itself starts at **absolute offset**=''​%%0x08%%''​)
  
 +
 +===== Example Code in C# =====
 +The following C# class (F4Resources.F4ResourceBundleReader) illustrates how to read a Falcon Resource Bundle (.RSC file + .IDX file) at a low level. ​ It's not optimized for speed (i.e. it uses SetPixel for setting image colors and it uses Array.Copy for copying raw binary data instead of the faster native memory bit-to-block transfer techniques),​ but it is more illustrative,​ as a result.
 +<code cpp>
 +using System;
 +using System.Collections.Generic;​
 +using System.Text;​
 +using System.IO;
 +using System.Drawing;​
 +namespace F4Resources
 +{
 +
 +    public enum F4ResourceType : uint
 +    {
 +        Unknown = 0,
 +        ImageResource = 100,
 +        SoundResource = 101,
 +        FlatResource = 102,
 +    }
 +    public class F4ResourceBundleReader
 +    {
 +
 +        [Flags]
 +        protected internal enum F4ResourceFlags : uint
 +        {
 +            EightBit = 0x00000001,
 +            SixteenBit = 0x00000002,
 +            UseColorKey = 0x40000000,
 +        }
 +        protected internal class F4ResourceRawDataPackage
 +        {
 +            public uint Version;
 +            public uint Size;
 +            public byte[] Data;
 +        }
 +        protected internal class F4ResourceHeader
 +        {
 +            public uint Type;
 +            public string ID = null;
 +        }
 +        protected internal class F4FlatResourceHeader : F4ResourceHeader
 +        {
 +            public uint Offset;
 +            public uint Size;
 +        }
 +        protected internal class F4ImageResourceHeader : F4ResourceHeader
 +        {
 +            public uint Flags;
 +            public ushort CenterX;
 +            public ushort CenterY;
 +            public ushort Width;
 +            public ushort Height;
 +            public uint ImageOffset;​
 +            public uint PaletteSize;​
 +            public uint PaletteOffset;​
 +        }
 +        protected internal class F4SoundResourceHeader : F4ResourceHeader
 +        {
 +            public uint Flags;
 +            public ushort Channels;
 +            public ushort SoundType;
 +            public uint Offset;
 +            public uint HeaderSize;
 +        }
 +        protected internal class F4ResourceBundleIndex
 +        {
 +            public uint Size;
 +            public uint NumResources;​
 +            public uint ResourceIndexVersion;​
 +            public F4ResourceHeader[] ResourceHeaders;​
 +            public F4ResourceRawDataPackage ResourceData;​
 +        }
 +        ​
 +        private F4ResourceBundleIndex _resourceIndex = null;
 +        public virtual void Load(string resourceBundleIndexPath)
 +        {
 +            FileInfo resourceIndexFileInfo = new FileInfo(resourceBundleIndexPath);​
 +            if (resourceIndexFileInfo.Exists)
 +            {
 +                byte[] bytes = new byte[resourceIndexFileInfo.Length];​
 +                using (FileStream fs = new FileStream(resourceBundleIndexPath,​ FileMode.Open))
 +                {
 +                    fs.Seek(0, SeekOrigin.Begin);​
 +                    fs.Read(bytes,​ 0, (int)resourceIndexFileInfo.Length);​
 +                }
 +                _resourceIndex = new F4ResourceBundleIndex();​
 +                int curByte = 0;
 +                _resourceIndex.Size= BitConverter.ToUInt32(bytes,​ curByte);
 +                curByte += 4;
 +                _resourceIndex.ResourceIndexVersion = BitConverter.ToUInt32(bytes,​ curByte);
 +                curByte += 4;
 +                uint size = _resourceIndex.Size;​
 +                List<​F4ResourceHeader>​ headers = new List<​F4ResourceHeader>​();​
 +
 +                while (size >0)
 +                {
 +                    _resourceIndex.NumResources++;​
 +                    uint resourceType = BitConverter.ToUInt32(bytes,​ curByte);
 +                    curByte += 4;
 +                    byte[] resourceId = new byte[32];
 +                    for (int j = 0; j < 32; j++)
 +                    {
 +                        resourceId[j] = bytes[curByte];​
 +                        curByte++;
 +                    }
 +                    string resourceName = Encoding.ASCII.GetString(resourceId);​
 +                    int nullLoc = resourceName.IndexOf('​\0'​);​
 +                    if (nullLoc > 0)
 +                    {
 +                        resourceName = resourceName.Substring(0,​ nullLoc);
 +                    }
 +                    else
 +                    {
 +                        resourceName = null;
 +                    }
 +                    if (resourceType == (uint)(F4ResourceType.ImageResource))
 +                    {
 +                        F4ImageResourceHeader thisResourceHeader = new F4ImageResourceHeader();​
 +                        thisResourceHeader.Type = resourceType;​
 +                        thisResourceHeader.ID = resourceName;​
 +                        thisResourceHeader.Flags = BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        thisResourceHeader.CenterX = BitConverter.ToUInt16(bytes,​ curByte);
 +                        curByte += 2;
 +                        thisResourceHeader.CenterY = BitConverter.ToUInt16(bytes,​ curByte);
 +                        curByte += 2;
 +                        thisResourceHeader.Width = BitConverter.ToUInt16(bytes,​ curByte);
 +                        curByte += 2;
 +                        thisResourceHeader.Height = BitConverter.ToUInt16(bytes,​ curByte);
 +                        curByte += 2;
 +                        thisResourceHeader.ImageOffset = BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        thisResourceHeader.PaletteSize = BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        thisResourceHeader.PaletteOffset = BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        headers.Add(thisResourceHeader);​
 +                        size -= 60;
 +                    }
 +                    else if (resourceType == (uint)(F4ResourceType.SoundResource))
 +                    {
 +                        F4SoundResourceHeader thisResourceHeader = new F4SoundResourceHeader();​
 +                        thisResourceHeader.Type = resourceType;​
 +                        thisResourceHeader.ID = resourceName;​
 +                        thisResourceHeader.Flags = BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        thisResourceHeader.Channels = BitConverter.ToUInt16(bytes,​ curByte);
 +                        curByte += 2;
 +                        thisResourceHeader.SoundType = BitConverter.ToUInt16(bytes,​ curByte);
 +                        curByte += 2;
 +                        thisResourceHeader.Offset = BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        thisResourceHeader.HeaderSize = BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        headers.Add(thisResourceHeader);​
 +                        size -= 52;
 +                    }
 +                    else if (resourceType == (uint)(F4ResourceType.FlatResource))
 +                    {
 +                        F4FlatResourceHeader thisResourceHeader = new F4FlatResourceHeader();​
 +                        thisResourceHeader.Type = resourceType;​
 +                        thisResourceHeader.ID = resourceName;​
 +                        thisResourceHeader.Offset= BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        thisResourceHeader.Size= BitConverter.ToUInt32(bytes,​ curByte);
 +                        curByte += 4;
 +                        headers.Add(thisResourceHeader);​
 +                        size -= 44;
 +                    }
 +                }
 +                _resourceIndex.ResourceHeaders = headers.ToArray();​
 +
 +                FileInfo resourceDataFileInfo = new FileInfo(
 +                    Path.GetDirectoryName(resourceIndexFileInfo.FullName) + Path.DirectorySeparatorChar + 
 +                    Path.GetFileNameWithoutExtension(resourceIndexFileInfo.FullName) + "​.rsc"​);​
 +                if (resourceDataFileInfo.Exists)
 +                {
 +                    bytes = new byte[resourceDataFileInfo.Length];​
 +
 +                    using (FileStream fs = new FileStream(resourceDataFileInfo.FullName,​ FileMode.Open))
 +                    {
 +                        fs.Seek(0, SeekOrigin.Begin);​
 +                        fs.Read(bytes,​ 0, (int)resourceDataFileInfo.Length);​
 +                    }
 +                    F4ResourceRawDataPackage rawDataPackage = new F4ResourceRawDataPackage();​
 +                    curByte = 0;
 +                    rawDataPackage.Size = BitConverter.ToUInt32(bytes,​ curByte);
 +                    curByte += 4;
 +                    rawDataPackage.Version = BitConverter.ToUInt32(bytes,​ curByte);
 +                    curByte += 4;
 +                    rawDataPackage.Data = new byte[rawDataPackage.Size];​
 +                    for (int k = 0; k < rawDataPackage.Size;​ k++)
 +                    {
 +                        rawDataPackage.Data[k] = bytes[curByte];​
 +                        curByte++;
 +                    }
 +                    _resourceIndex.ResourceData = rawDataPackage;​
 +                }
 +                else
 +                {
 +                    throw new FileNotFoundException(resourceDataFileInfo.FullName);​
 +                }
 +
 +            }
 +            else
 +            {
 +                throw new FileNotFoundException(resourceBundleIndexPath);​
 +            }
 +        }
 +        public int NumResources
 +        {
 +            get
 +            {
 +                if (_resourceIndex == null)
 +                {
 +                    return -1;
 +                }
 +                else
 +                {
 +                    return (int)_resourceIndex.NumResources;​
 +                }
 +            }
 +        }
 +        public virtual F4ResourceType GetResourceType(int resourceNum)
 +        {
 +            if (_resourceIndex == null)
 +            {
 +                return F4ResourceType.Unknown;​
 +            }
 +            else
 +            {
 +                return (F4ResourceType)_resourceIndex.ResourceHeaders[resourceNum].Type;​
 +            }
 +        }
 +        public virtual byte[] GetSoundResource(string resourceId)
 +        {
 +            F4SoundResourceHeader resourceHeader = FindResourceHeaderByResourceId(resourceId) as F4SoundResourceHeader;​
 +            return GetSoundResource(resourceHeader);​
 +        }
 +        public virtual byte[] GetSoundResource(int resourceNum)
 +        {
 +            if (_resourceIndex == null || _resourceIndex.ResourceHeaders == null || resourceNum >= _resourceIndex.ResourceHeaders.Length)
 +            {
 +                return null;
 +            }
 +            F4SoundResourceHeader resourceHeader = _resourceIndex.ResourceHeaders[resourceNum] as F4SoundResourceHeader;​
 +            return GetSoundResource(resourceHeader);​
 +        }
 +        protected virtual byte[] GetSoundResource(F4SoundResourceHeader resourceHeader)
 +        {
 +            if (resourceHeader == null) return null;
 +            int curByte = (int)resourceHeader.Offset;​
 +            curByte += 4;
 +            uint dataSize = BitConverter.ToUInt32(_resourceIndex.ResourceData.Data,​ curByte);
 +            curByte += 4;
 +            byte[] toReturn = new byte[dataSize+8];​
 +            Array.Copy(_resourceIndex.ResourceData.Data,​ curByte-8, toReturn, 0, dataSize+8);​
 +            return toReturn;
 +        }
 +        public virtual byte[] GetFlatResource(int resourceNum)
 +        {
 +            if (_resourceIndex == null || _resourceIndex.ResourceHeaders == null || resourceNum >= _resourceIndex.ResourceHeaders.Length)
 +            {
 +                return null;
 +            }
 +            F4FlatResourceHeader resourceHeader = _resourceIndex.ResourceHeaders[resourceNum] as F4FlatResourceHeader;​
 +            return GetFlatResource(resourceHeader);​
 +        }
 +        public virtual byte[] GetFlatResource(string resourceId)
 +        {
 +            F4FlatResourceHeader resourceHeader = FindResourceHeaderByResourceId(resourceId) as F4FlatResourceHeader;​
 +            return GetFlatResource(resourceHeader);​
 +        }
 +        protected virtual byte[] GetFlatResource(F4FlatResourceHeader resourceHeader)
 +        {
 +            if (resourceHeader == null) return null;
 +            byte[] bytes = new byte[resourceHeader.Size];​
 +            for (int i = 0; i < resourceHeader.Size;​ i++)
 +            {
 +                bytes[i] = _resourceIndex.ResourceData.Data[resourceHeader.Offset + i];
 +            }
 +            return bytes;
 +        }
 +        public virtual Bitmap GetImageResource(string resourceId)
 +        {
 +            F4ImageResourceHeader imageHeader = FindResourceHeaderByResourceId(resourceId) as F4ImageResourceHeader;​
 +            return GetImageResource(imageHeader);​
 +        }
 +        public virtual Bitmap GetImageResource(int resourceNum)
 +        {
 +            if (_resourceIndex == null || _resourceIndex.ResourceHeaders == null || resourceNum >= _resourceIndex.ResourceHeaders.Length)
 +            {
 +                return null;
 +            }
 +            F4ImageResourceHeader imageHeader = _resourceIndex.ResourceHeaders[resourceNum] as F4ImageResourceHeader;​
 +            return GetImageResource(imageHeader);​
 +        }
 +        protected virtual Bitmap GetImageResource(F4ImageResourceHeader imageHeader)
 +        {
 +            if (imageHeader == null) return null;
 +            Bitmap toReturn = new Bitmap(imageHeader.Width,​ imageHeader.Height);​
 +            ushort[] palette = new ushort[imageHeader.PaletteSize];​
 +            if ((imageHeader.Flags & (uint)F4ResourceFlags.EightBit) == (uint)F4ResourceFlags.EightBit)
 +            {
 +                for (int i = 0; i < palette.Length;​ i++)
 +                {
 +                    palette[i] = BitConverter.ToUInt16(_resourceIndex.ResourceData.Data,​ (int)imageHeader.PaletteOffset + (i * 2));
 +                }
 +            }
 +            int curByte = 0;
 +            for (int y = 0; y < imageHeader.Height;​ y++)
 +            {
 +                for (int x = 0; x < imageHeader.Width;​ x++)
 +                {
 +                    int A = 0;
 +                    int R = 0;
 +                    int G = 0;
 +                    int B = 0;
 +                    if ((imageHeader.Flags & (uint)F4ResourceFlags.EightBit)==(uint)F4ResourceFlags.EightBit)
 +                    {
 +                        byte thisPixelPaletteIndex = _resourceIndex.ResourceData.Data[imageHeader.ImageOffset + curByte];
 +                        ushort thisPixelPaletteEntry = palette[thisPixelPaletteIndex];​
 +                        A = 255;
 +                        R = ((thisPixelPaletteEntry & 0x7C00) >> 10) << 3;
 +                        G = ((thisPixelPaletteEntry & 0x3E0) >> 5) << 3;
 +                        B = (thisPixelPaletteEntry & 0x1F) << 3;
 +                        curByte++;
 +                    }
 +                    else if ((imageHeader.Flags & (uint)F4ResourceFlags.SixteenBit) == (uint)F4ResourceFlags.SixteenBit)
 +                    {
 +                        ushort thisPixelPaletteEntry = BitConverter.ToUInt16(_resourceIndex.ResourceData.Data,​ (int)(imageHeader.ImageOffset + curByte));
 +                        A = 255;
 +                        R = ((thisPixelPaletteEntry & 0x7C00) >> 10) << 3;
 +                        G = ((thisPixelPaletteEntry & 0x3E0) >> 5) << 3;
 +                        B = (thisPixelPaletteEntry & 0x1F) << 3;
 +                        curByte+=2;
 +                    }
 +                    toReturn.SetPixel(x,​ y, Color.FromArgb(A,​ R, G, B));
 +                }
 +            }
 +            return toReturn;
 +        }
 +        protected virtual F4ResourceHeader FindResourceHeaderByResourceId(string resourceId) ​
 +        {
 +            if (_resourceIndex == null || _resourceIndex.ResourceHeaders == null || resourceId == null)
 +            {
 +                return null;
 +            }
 +            for (int i = 0; i < _resourceIndex.ResourceHeaders.Length;​ i++)
 +            {
 +                F4ResourceHeader thisResourceHeader = _resourceIndex.ResourceHeaders[i];​
 +                string thisResourceId = thisResourceHeader.ID;​
 +                if (thisResourceId.ToLowerInvariant() == resourceId.ToLowerInvariant())
 +                {
 +                    return thisResourceHeader;​
 +                }
 +            }
 +            return null;
 +        }
 +    }
 +}
 +</​code>​
 +
 +The next chunk of C# example code shows the above class being used in a Windows Forms application,​ and illustrates how one might use the class presented in the previous example.
 +<code cpp>
 +using System;
 +using System.Drawing;​
 +using System.Windows.Forms;​
 +using System.IO;
 +using System.Runtime.InteropServices;​
 +
 +namespace WindowsFormsApplication1
 +{
 +    public partial class Form1 : Form
 +    {
 +        public Form1()
 +        {
 +            InitializeComponent();​
 +        }
 +
 +        private void button1_Click(object sender, EventArgs e)
 +        {
 +            string resourceBundleIndexPath = @"​C:​\Microprose\Falcon4\Theaters\Vietnam\art\art\resource\select.idx";​
 +            F4Resources.F4ResourceBundleReader resourceBundleReader = new F4Resources.F4ResourceBundleReader();​
 +            resourceBundleReader.Load(resourceBundleIndexPath);​
 +            for (int i = 0; i < resourceBundleReader.NumResources;​ i++)
 +            {
 +                Application.DoEvents();​
 +                F4Resources.F4ResourceType thisResourceType = resourceBundleReader.GetResourceType(i);​
 +                switch (thisResourceType)
 +                {
 +                    case F4Resources.F4ResourceType.Unknown:​
 +                        break;
 +                    case F4Resources.F4ResourceType.ImageResource:​ //read an image resource (you could assign the image to a picturebox, or save it to disk, or whatever)
 +                        Bitmap thisImage = resourceBundleReader.GetImageResource(i);​
 +                        break;
 +                    case F4Resources.F4ResourceType.SoundResource:​ //read and play a sound file (writes sound to a temp file and calls WinAPI PlaySound to play it)
 +                        byte[] thisSound = resourceBundleReader.GetSoundResource(i);​
 +                        string tempFile = Path.GetTempFileName();​
 +                        try
 +                        {
 +                            using (FileStream fs = new FileStream(tempFile,​ FileMode.Create))
 +                            {
 +                                fs.Write(thisSound,​ 0, thisSound.Length);​
 +                                fs.Flush();
 +                                fs.Close();
 +                            }
 +                            PlaySound(tempFile,​ IntPtr.Zero,​ SoundFlags.SND_FILENAME);​
 +                        }
 +                        finally
 +                        {
 +                            try
 +                            {
 +                                new FileInfo(tempFile).Delete();​
 +                            }
 +                            catch (IOException)
 +                            {
 +                            }
 +                        }
 +                        break;
 +                    case F4Resources.F4ResourceType.FlatResource:​
 +                        byte[] thisFlatResource = resourceBundleReader.GetFlatResource(i);​
 +                        break;
 +                    default:
 +                        break;
 +                }
 +            }
 +        }
 +        [Flags]
 +        public enum SoundFlags : int
 +        {
 +            SND_SYNC = 0x0000, ​ // play synchronously (default) ​
 +            SND_ASYNC = 0x0001, ​ // play asynchronously ​
 +            SND_NODEFAULT = 0x0002, ​ // silence (!default) if sound not found 
 +            SND_MEMORY = 0x0004, ​ // pszSound points to a memory file
 +            SND_LOOP = 0x0008, ​ // loop the sound until next sndPlaySound ​
 +            SND_NOSTOP = 0x0010, ​ // don't stop any currently playing sound 
 +            SND_NOWAIT = 0x00002000, // don't wait if the driver is busy 
 +            SND_ALIAS = 0x00010000, // name is a registry alias 
 +            SND_ALIAS_ID = 0x00110000, // alias is a predefined ID
 +            SND_FILENAME = 0x00020000, // name is file name 
 +            SND_RESOURCE = 0x00040004 ​ // name is resource name or atom 
 +        }
 +
 +        [System.Runtime.InteropServices.DllImport("​winmm.DLL",​ EntryPoint = "​PlaySound",​ SetLastError = true, CharSet = CharSet.Unicode,​ ThrowOnUnmappableChar = true)]
 +        private static extern bool PlaySound(string szSound, System.IntPtr hMod, SoundFlags flags);
 +    }
 +}
 +</​code>​
falcon4/file_formats/rsc_idx_fileformat.1233957107.txt.gz · Last modified: 2009-02-06 21:51 (external edit)