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:58]
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 80: Line 82:
 **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.1233957499.txt.gz ยท Last modified: 2009-02-06 21:58 (external edit)