falcon4:file_formats:rsc_idx_fileformat
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| falcon4:file_formats:rsc_idx_fileformat [2009/02/06 21:51] – lightning | falcon4:file_formats:rsc_idx_fileformat [2024/07/31 08:52] (current) – links added. snakeman | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | ====== IDX/RSC file pairs====== | + | ====== |
| + | |||
| + | [[https:// | ||
| .RSC files in Falcon are " | .RSC files in Falcon are " | ||
| Line 7: | Line 10: | ||
| 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: | ||
| ===== .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 77: | ||
| |Version|'' | |Version|'' | ||
| - | Immediately following the HEADER section in the .RSC file, comes the DATA section. | ||
| ==== .RSC FILE DATA SECTION ==== | ==== .RSC FILE DATA SECTION ==== | ||
| - | The .RSC file' | + | Immediately following the HEADER section in the .RSC file, comes the DATA section. |
| + | |||
| + | The DATA section extends from absolute offset '' | ||
| **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 '' | **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 '' | ||
| + | |||
| + | |||
| + | ===== 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. | ||
| + | <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, | ||
| + | { | ||
| + | fs.Seek(0, SeekOrigin.Begin); | ||
| + | fs.Read(bytes, | ||
| + | } | ||
| + | _resourceIndex = new F4ResourceBundleIndex(); | ||
| + | int curByte = 0; | ||
| + | _resourceIndex.Size= BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | _resourceIndex.ResourceIndexVersion = BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | uint size = _resourceIndex.Size; | ||
| + | List< | ||
| + | |||
| + | while (size >0) | ||
| + | { | ||
| + | _resourceIndex.NumResources++; | ||
| + | uint resourceType = BitConverter.ToUInt32(bytes, | ||
| + | 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(' | ||
| + | if (nullLoc > 0) | ||
| + | { | ||
| + | resourceName = resourceName.Substring(0, | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | resourceName = null; | ||
| + | } | ||
| + | if (resourceType == (uint)(F4ResourceType.ImageResource)) | ||
| + | { | ||
| + | F4ImageResourceHeader thisResourceHeader = new F4ImageResourceHeader(); | ||
| + | thisResourceHeader.Type = resourceType; | ||
| + | thisResourceHeader.ID = resourceName; | ||
| + | thisResourceHeader.Flags = BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | thisResourceHeader.CenterX = BitConverter.ToUInt16(bytes, | ||
| + | curByte += 2; | ||
| + | thisResourceHeader.CenterY = BitConverter.ToUInt16(bytes, | ||
| + | curByte += 2; | ||
| + | thisResourceHeader.Width = BitConverter.ToUInt16(bytes, | ||
| + | curByte += 2; | ||
| + | thisResourceHeader.Height = BitConverter.ToUInt16(bytes, | ||
| + | curByte += 2; | ||
| + | thisResourceHeader.ImageOffset = BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | thisResourceHeader.PaletteSize = BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | thisResourceHeader.PaletteOffset = BitConverter.ToUInt32(bytes, | ||
| + | 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 += 4; | ||
| + | thisResourceHeader.Channels = BitConverter.ToUInt16(bytes, | ||
| + | curByte += 2; | ||
| + | thisResourceHeader.SoundType = BitConverter.ToUInt16(bytes, | ||
| + | curByte += 2; | ||
| + | thisResourceHeader.Offset = BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | thisResourceHeader.HeaderSize = BitConverter.ToUInt32(bytes, | ||
| + | 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 += 4; | ||
| + | thisResourceHeader.Size= BitConverter.ToUInt32(bytes, | ||
| + | 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) + " | ||
| + | if (resourceDataFileInfo.Exists) | ||
| + | { | ||
| + | bytes = new byte[resourceDataFileInfo.Length]; | ||
| + | |||
| + | using (FileStream fs = new FileStream(resourceDataFileInfo.FullName, | ||
| + | { | ||
| + | fs.Seek(0, SeekOrigin.Begin); | ||
| + | fs.Read(bytes, | ||
| + | } | ||
| + | F4ResourceRawDataPackage rawDataPackage = new F4ResourceRawDataPackage(); | ||
| + | curByte = 0; | ||
| + | rawDataPackage.Size = BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | rawDataPackage.Version = BitConverter.ToUInt32(bytes, | ||
| + | curByte += 4; | ||
| + | rawDataPackage.Data = new byte[rawDataPackage.Size]; | ||
| + | for (int k = 0; k < rawDataPackage.Size; | ||
| + | { | ||
| + | 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 += 4; | ||
| + | byte[] toReturn = new byte[dataSize+8]; | ||
| + | Array.Copy(_resourceIndex.ResourceData.Data, | ||
| + | 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; | ||
| + | { | ||
| + | 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, | ||
| + | ushort[] palette = new ushort[imageHeader.PaletteSize]; | ||
| + | if ((imageHeader.Flags & (uint)F4ResourceFlags.EightBit) == (uint)F4ResourceFlags.EightBit) | ||
| + | { | ||
| + | for (int i = 0; i < palette.Length; | ||
| + | { | ||
| + | palette[i] = BitConverter.ToUInt16(_resourceIndex.ResourceData.Data, | ||
| + | } | ||
| + | } | ||
| + | int curByte = 0; | ||
| + | for (int y = 0; y < imageHeader.Height; | ||
| + | { | ||
| + | for (int x = 0; x < imageHeader.Width; | ||
| + | { | ||
| + | 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, | ||
| + | A = 255; | ||
| + | R = ((thisPixelPaletteEntry & 0x7C00) >> 10) << 3; | ||
| + | G = ((thisPixelPaletteEntry & 0x3E0) >> 5) << 3; | ||
| + | B = (thisPixelPaletteEntry & 0x1F) << 3; | ||
| + | curByte+=2; | ||
| + | } | ||
| + | toReturn.SetPixel(x, | ||
| + | } | ||
| + | } | ||
| + | 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; | ||
| + | { | ||
| + | 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, | ||
| + | <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 = @" | ||
| + | F4Resources.F4ResourceBundleReader resourceBundleReader = new F4Resources.F4ResourceBundleReader(); | ||
| + | resourceBundleReader.Load(resourceBundleIndexPath); | ||
| + | for (int i = 0; i < resourceBundleReader.NumResources; | ||
| + | { | ||
| + | Application.DoEvents(); | ||
| + | F4Resources.F4ResourceType thisResourceType = resourceBundleReader.GetResourceType(i); | ||
| + | switch (thisResourceType) | ||
| + | { | ||
| + | case F4Resources.F4ResourceType.Unknown: | ||
| + | break; | ||
| + | case F4Resources.F4ResourceType.ImageResource: | ||
| + | Bitmap thisImage = resourceBundleReader.GetImageResource(i); | ||
| + | break; | ||
| + | case F4Resources.F4ResourceType.SoundResource: | ||
| + | byte[] thisSound = resourceBundleReader.GetSoundResource(i); | ||
| + | string tempFile = Path.GetTempFileName(); | ||
| + | try | ||
| + | { | ||
| + | using (FileStream fs = new FileStream(tempFile, | ||
| + | { | ||
| + | fs.Write(thisSound, | ||
| + | fs.Flush(); | ||
| + | fs.Close(); | ||
| + | } | ||
| + | PlaySound(tempFile, | ||
| + | } | ||
| + | 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, | ||
| + | SND_ASYNC = 0x0001, | ||
| + | SND_NODEFAULT = 0x0002, | ||
| + | SND_MEMORY = 0x0004, | ||
| + | SND_LOOP = 0x0008, | ||
| + | SND_NOSTOP = 0x0010, | ||
| + | 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 | ||
| + | } | ||
| + | |||
| + | [System.Runtime.InteropServices.DllImport(" | ||
| + | private static extern bool PlaySound(string szSound, System.IntPtr hMod, SoundFlags flags); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
falcon4/file_formats/rsc_idx_fileformat.1233957107.txt.gz · Last modified: 2009/02/06 21:51 (external edit)
