User Tools

Site Tools


falcon4:file_formats:rsc_idx_fileformat

Falcon 4 IDX/RSC file pairs

Falcon 4.0 Forum, Falcon 4 Home, Falcon 4 Campaign, Falcon 4 Cockpits, Falcon 4 Database, Falcon 4 File Formats, Falcon 4 SRTM Terrain, Falcon 4 Terrain, Falcon 4 Textures, Falcon 4 Tools

.RSC files in Falcon are “resource bundles”. A “resource bundle” is a type of file that can contain one or more (embedded) binary files, of varying types. For example, they can contain images, sounds, and/or miscellaneous binary content. A single .RSC file can (and often does) contain multiple resources, potentially of mixed type (i.e. a resource bundle file could contain several images, several sounds, and several binary files, all at once).

The correspondingly-named .IDX file (located in the same folder as the .RSC file in question), stores an index of the contents of the .RSC file. This index provides offset information to the location within the .RSC file's DATA section, where a specific resource's raw binary data begins, as well as the size, in bytes, of the resource's binary data, and some additional metadata describing the type of resource that can be found at that location. The interpretation of the .RSC file's binary data, therefore, is dependent on the information provided in the corresponding .IDX file.

NOTE: The .IDX file extension in Falcon refers to a variety of different kinds of index files. The formats described below ONLY APPLY to .IDX files that index a corresponding .RSC (resource bundle) file. Other types of .IDX files ARE NOT 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 HEADER SECTION

Field Name Offset (Hex) Length (Bytes) Data Type Description
Size 0x00 4 long The size (in bytes) of the data section in this .IDX file (i.e. the file length minus the header length)
Version 0x04 4 long The version number of the file format being used for this .IDX file
(must match the version number of the corresponding .RSC file)
example: 0x023fc8dd

Immediately following the header section in the .IDX file, comes the data section. Each record in the data section in the .IDX describes one resource that can be found in the corresponding .RSC file.

.IDX FILE DATA SECTION

The DATA section in the .IDX file contains one DATA record per embedded resource in the corresponding .RSC file. Each DATA record in the .IDX file starts with 2 common fields (ResourceType and ResourceID). The rest of the DATA record (and hence, the size of an individual data record) depends on the specific ResourceType that is specified for that record.

INDIVIDUAL .IDX FILE DATA RECORD -- COMMON FIELDS

NOTE: all field offsets given in the Offset column below, are relative to the start of the individual data record within the .IDX file, not the start of the .IDX file itself.
For example, the ResourceType field for the first DATA record in the .IDX file begins at relative offset = 0x00 (absolute offset = 0x08, for the first DATA record in the .IDX file).


FieldOffset(Hex)Length (Bytes)Data TypeDescription
ResourceType0x004longThe type of resource that this record points to.
Values for the ResourceType field can be one of the following:

Hex Value Description
0x64 Image resource (i.e. an embedded bitmap)
0x65 Sound resource (i.e. an embedded windows .WAV file)
0x66 Flat file resource (i.e. embedded arbitrary binary content)
ResourceID0x0432char[32]A NULL-terminated ASCII string that identifies this resource.
NOTE: ALL fields that follow, inside of a single .IDX data record, depend on the specific resource type that is being described by that record.

ADDITIONAL FIELDS FOR RESOURCE TYPE = 0x64 (Image resource)

TOTAL RECORD LENGTH: (including the ResourceType and ResourceID fields): 60 bytes

Field NameOffset (Hex)Length (Bytes)Data TypeDescription
Flags0x244longBit flags that describe the image format.

Bitmasks:

EightBit = 0x00000001 Image contains a 256-value (8-bit) palette; each image pixel is described by a single byte representing an index into the color palette array (stored separately).

SixteenBit = 0x00000002 Each image pixel is described by 2 bytes, which, taken together as a 16-bit integer, provide 16 bits of color information per pixel. When this flag is set, no separate palette array exists.

UseColorKey = 0x40000000 The image uses the first color in the palette (or magenta, for non-paletted images) as the color key (transparency color) – any pixels using that color should be rendered as transparent
CenterX0x282shortCenter X pixel (not used??)
CenterY0x2A2shortCenter Y pixel (not used??)
Width0x2C2shortWidth of the image, in pixels
Height0x2E2shortHeight of the image, in pixels
ImageOffset0x304longOffset (in bytes) to the start of the image's pixel data, relative to the start of the .RSC file's DATA section.

The actual size of the data starting at that location will be:

(Width * Height) bytes long (for an 8-bit paletted image),
or
(Width * Height * 2) bytes long (for a 16-bit image)

Pixel data structure
The first byte in the pixel data array in the .RSC file represents the upper-left pixel of the image.
The last byte in the pixel data array in the .RSC file represents the lower-right pixel of the image.
The pixel array is stored with the first (top) row's worth of columns first (i.e. byte 0=(row 0, column 0); byte 1=(row 0, column 1), byte M = (row 0, column =width)… (byte N = row=height, column=width)
PaletteSize0x344longNumber of entries in the image's palette. Each palette entry consists of 2 bytes (16 bits of color info per palette entry).
NOTE: PaletteSize = 0 for non-paletted images.
PaletteOffset0x384longOffset (in bytes) to the start of the image's color palette data array, relative to the start of the the .RSC file's DATA section.

To convert the 16-bit color values from the palette data array (or from the raw pixel data, in the case of non-paletted images) to 32-bit ARGB color values, use the following (pseudocode):
byte A = 0xFF; //alpha byte
byte R = (thisPixelPaletteEntryValue & 0x7C00) >> 7; //red byte
byte G = (thisPixelPaletteEntryValue & 0x3E0) >> 2; //green byte
byte B = (thisPixelPaletteEntryValue & 0x1F) << 3; //blue byte


The 16-bit color data in the palette is actually only using 15 bits (5 for red, 5 for green, and 5 for blue).
* The low-order (rightmost) 5 bits are the blue bits.
* The next higher-order (middle) 5 bits are the green bits.
* The next higher-order 5 bits after that (i.e. the leftmost 5 bits) are the red bits.
* The high-order bit is not used.

The conversion (described above) works by first masking off the relevant bits for a particular color, and then shifting those bits left or right so that each color's bits occupy the 5 most-significant bits in that color's respective byte.

After performing the above conversion, you can then combine all the component bytes togther into a single 32-bit integer, as follows (pseudocode):

long argb = ((A << 24) | (R << 16) | (G <<8) | B);

ADDITIONAL FIELDS FOR RESOURCE TYPE = 0x65 (Sound resource)

TOTAL RECORD LENGTH: (including the ResourceType and ResourceID fields): 52 bytes

Field NameOffset (Hex)Length (Bytes)Data TypeDescription
Flags0x244longBit flags that describe the sound format.

Bitmasks:
???
Channels0x282shortNumber of channels of audio data in the corresponding sound file.

* 1 = mono,
* 2 = stereo

NOTE: This is actually redundant, because this information is also contained in the resource's payload within the .RSC file's DATA section.
SoundType0x2A2shortThis resource's Windows .WAV file encoding type. This field actually contains the value of the wFormatTag member of the .WAV file's WAVEFORMATEX structure.

NOTE: This is actually redundant, because this information is also contained in the resource's payload within the .RSC file's DATA section.
Offset0x2C4longOffset to the start of the sound resource's .WAV file binary data relative to the start of the .RSC file's DATA section.
HeaderSize0x304longSize, in bytes, of the sound file's header section, located inside the resource's binary data payload within the .RSC file's DATA section.

NOTE: The length (in bytes) of the sound resource's actual payload within the .RSC file's DATA section can be found by looking at the integer value occupying the 4 bytes starting at this resource's .Offset+4 in the .RSC file's DATA section itself. You need to add 8 to that value to get the total embedded .WAV file size, in bytes, starting at the .Offset itself.

Example:
if the sound resource's Offset was set to 0x00, you would seek to location 0x04 (Offset+4) in the .RSC file's DATA section, then read the next 4 bytes into a “long” integer. Add 8 to the value of that integer, and that's the number of bytes, starting at this resource's .Offset relative to the start of the .RSC file's DATA section, that make up the entire .WAV file binary for this resource.

ADDITIONAL FIELDS FOR RESOURCE TYPE = 0x66 (Flat [i.e. binary] resource)

TOTAL RECORD LENGTH: (including the ResourceType and ResourceID fields): 44 bytes

Field NameOffset (Hex)Length (Bytes)Data TypeDescription
Offset0x244longByte offset of the flat resource's binary data relative to the start of the corresponding .RSC file's DATA section
Size0x284longSize, in bytes, of the flat resource's contents

.RSC file format

.RSC FILE HEADER SECTION

Field NameOffset (Hex)Length (Bytes)Data TypeDescription
Size0x004longThe size (in bytes) of the data section in this .RSC file (i.e. the file length minus the header length)
Version0x044longThe version number of the file format being used for this .RSC file (must match the version number of the corresponding .IDX file)
example: 0x023fc8dd

.RSC FILE DATA SECTION

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)

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.

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;
        }
    }
}

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.

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);
    }
}
falcon4/file_formats/rsc_idx_fileformat.txt · Last modified: 2024/07/31 08:52 by snakeman

Except where otherwise noted, content on this wiki is licensed under the following license: CC Attribution-Share Alike 4.0 International
CC Attribution-Share Alike 4.0 International Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki

All PMC web site download services are temporarily suspended until web site yearly fees have been recovered, want to download addons/mods? Then Support PMC.

If you are grateful for all the work PMC has done in the past 25 years, use Support PMC page.