Skip to content

Instantly share code, notes, and snippets.

@Fireboyd78
Created March 18, 2018 08:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Fireboyd78/bdde1d4a2cebedf29f71a1ce9b04f01d to your computer and use it in GitHub Desktop.
Save Fireboyd78/bdde1d4a2cebedf29f71a1ce9b04f01d to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
namespace XBox.IO
{
public enum D3DFormatType
{
R8G8B8 = 20,
A8R8G8B8 = 21,
X8R8G8B8 = 22,
R5G6B5 = 23,
X1R5G5B5 = 24,
A1R5G5B5 = 25,
A4R4G4B4 = 26,
R3G3B2 = 27,
A8 = 28,
A8R3G3B2 = 29,
X4R4G4B4 = 30,
A8P8 = 40,
P8 = 41,
L8 = 50,
A8L8 = 51,
A4L4 = 52,
V8U8 = 60,
L6V5U5 = 61,
X8L8V8U8 = 62,
Q8W8V8U8 = 63,
V16U16 = 64,
W11V11U10 = 65,
}
public enum ResourceTypeFlags
{
D3DResource = 1,
IndexBuffer = (1 << 16) | D3DResource, // IndexBuffer
Unk2 = (2 << 16) | D3DResource, // ???
Texture = (4 << 16) | D3DResource, // Texture / VolumeTexture / Cubemap
VertexBuffer = (128 << 16) | D3DResource, // VertexBuffer
UserData = (32768 << 16),
}
public enum DMAChannelType
{
None,
ChannelA,
ChannelB,
}
public struct ResourceSizeData
{
int m_value;
static readonly int[] WIDTH = { 0, 0xFFF };
static readonly int[] HEIGHT = { 12, 0xFFF };
static readonly int[] PITCH = { 24, 0xFF };
public static implicit operator ResourceSizeData(int value)
{
return new ResourceSizeData() { m_value = value };
}
private int GetVal(int[] info)
{
return ((m_value >> info[0]) & info[1]);
}
private void SetVal(int[] info, int value)
{
m_value &= (~(info[1] << info[0]));
m_value |= ((value & info[1]) << info[0]);
}
public int Width
{
get { return GetVal(WIDTH); }
set { SetVal(WIDTH, value); }
}
public int Height
{
get { return GetVal(HEIGHT); }
set { SetVal(HEIGHT, value); }
}
public int Pitch
{
get { return GetVal(PITCH); }
set { SetVal(PITCH, value); }
}
}
public struct ResourceFormatData
{
int m_value;
static readonly int[] DMA_CHANNEL = { 0, 0x3 };
static readonly int[] CUBEMAP = { 2, 0x1 };
static readonly int[] BORDERSRC_CLR = { 3, 0x1 };
static readonly int[] DIMENSION = { 4, 0xF };
static readonly int[] FORMAT = { 8, 0xFF };
static readonly int[] MIPMAP = { 16, 0xF };
static readonly int[] USIZE = { 20, 0xF };
static readonly int[] VSIZE = { 24, 0xF };
static readonly int[] PSIZE = { 28, 0xF };
public static implicit operator ResourceFormatData(int value)
{
return new ResourceFormatData() { m_value = value };
}
private int GetVal(int[] info)
{
return ((m_value >> info[0]) & info[1]);
}
private void SetVal(int[] info, int value)
{
m_value &= (~(info[1] << info[0]));
m_value |= ((value & info[1]) << info[0]);
}
private bool GetBoolVal(int[] info)
{
return (GetVal(info) == 1) ? true : false;
}
private void SetBoolVal(int[] info, bool value)
{
SetVal(info, (value) ? 1 : 0);
}
public DMAChannelType DMAChannel
{
get { return (DMAChannelType)GetVal(DMA_CHANNEL); }
set { SetVal(DMA_CHANNEL, (int)value); }
}
public bool IsCubeMap
{
get { return GetBoolVal(CUBEMAP); }
set { SetBoolVal(CUBEMAP, value); }
}
public bool BorderSourceColor
{
get { return GetBoolVal(BORDERSRC_CLR); }
set { SetBoolVal(BORDERSRC_CLR, value); }
}
public int Dimensions
{
get { return GetVal(DIMENSION); }
set { SetVal(DIMENSION, value); }
}
public D3DFormatType Format
{
get { return (D3DFormatType)GetVal(FORMAT); }
set { SetVal(FORMAT, (int)value); }
}
public int MipMaps
{
get { return GetVal(MIPMAP); }
set { SetVal(MIPMAP, value); }
}
public int USize
{
get { return GetVal(USIZE); }
set { SetVal(USIZE, value); }
}
public int VSize
{
get { return GetVal(VSIZE); }
set { SetVal(VSIZE, value); }
}
public int PSize
{
get { return GetVal(PSIZE); }
set { SetVal(PSIZE, value); }
}
}
[StructLayout(LayoutKind.Sequential, Size = 16)]
public struct ResourceData
{
public int Offset;
public int Lock;
public ResourceFormatData Format;
public ResourceSizeData Size;
}
public class ResourceEntry
{
public string Name { get; set; }
public ResourceTypeFlags Type { get; set; }
public ResourceData Info;
public byte[] Buffer { get; set; }
public ResourceEntry(string name, ResourceTypeFlags type)
{
Name = name;
Type = type;
}
}
public class ResourcePackageFile
{
public static readonly int Identifier = 0x525058; // 'XPR'
public List<ResourceEntry> Resources { get; set; }
public void LoadBinary(string filename)
{
using (var fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var bs = new BinaryReader(fs))
{
var ident = bs.ReadInt32();
if ((ident & 0xFFFFFF) != Identifier)
throw new InvalidOperationException("Not an XPR file!");
var version = (ident >> 24) - 0x30;
if (version < 0)
throw new InvalidOperationException("Not a valid XPR file!");
if (version > 1)
throw new InvalidOperationException($"Unsupported XPR version: {version}");
var fileSize = bs.ReadInt32();
var dataOffset = bs.ReadInt32();
var ptr = (int)fs.Position;
Resources = new List<ResourceEntry>();
switch (version)
{
case 0:
{
int cur = 0;
int idx = 1;
while ((cur = bs.ReadInt32()) != -1)
{
var type = (ResourceTypeFlags)cur;
var name = $"{idx++:D4}_{type.ToString()}";
var entry = new ResourceEntry(name, type);
switch (type)
{
case ResourceTypeFlags.IndexBuffer:
case ResourceTypeFlags.VertexBuffer:
{
entry.Info = new ResourceData() {
Offset = bs.ReadInt32(),
Lock = bs.ReadInt32(),
};
} break;
case ResourceTypeFlags.Unk2:
{
// oh god
var offset = bs.ReadInt32();
var rLock = bs.ReadInt32();
var unk1 = bs.ReadInt32();
var unk2 = bs.ReadInt32();
var fmt = bs.ReadInt32();
entry.Info = new ResourceData() {
Offset = offset,
Lock = rLock,
Format = fmt,
Size = unk1,
};
} break;
case ResourceTypeFlags.Texture:
{
entry.Info = fs.Read<ResourceData>(16);
} break;
case ResourceTypeFlags.UserData:
{
var size = bs.ReadInt32();
entry.Buffer = bs.ReadBytes(size);
} break;
}
Resources.Add(entry);
}
} break;
case 1:
{
var count = bs.ReadInt32();
var listPtr = (int)fs.Position;
for (int i = 0; i < count; i++)
{
fs.Position = listPtr + (i * 8);
var nsOffset = bs.ReadInt32() + ptr;
var rsOffset = bs.ReadInt32() + ptr;
// read name
fs.Position = nsOffset;
var name = "";
char ch;
while ((ch = bs.ReadChar()) != '\0')
{
name += ch;
}
// read data
fs.Position = rsOffset;
var type = (ResourceTypeFlags)bs.ReadInt32();
var entry = new ResourceEntry(name, type);
switch (type)
{
case ResourceTypeFlags.IndexBuffer:
case ResourceTypeFlags.VertexBuffer:
{
entry.Info = new ResourceData() {
Offset = bs.ReadInt32(),
Lock = bs.ReadInt32(),
};
}
break;
case ResourceTypeFlags.Unk2:
{
// oh god
var offset = bs.ReadInt32();
var rLock = bs.ReadInt32();
var unk1 = bs.ReadInt32();
var unk2 = bs.ReadInt32();
var fmt = bs.ReadInt32();
entry.Info = new ResourceData() {
Offset = offset,
Lock = rLock,
Format = fmt,
Size = unk1,
};
}
break;
case ResourceTypeFlags.Texture:
{
entry.Info = fs.Read<ResourceData>(16);
}
break;
case ResourceTypeFlags.UserData:
{
var size = bs.ReadInt32();
entry.Buffer = bs.ReadBytes(size);
}
break;
}
Resources.Add(entry);
}
} break;
}
for (int i = 0; i < Resources.Count; i++)
{
var res = Resources[i];
if ((res.Type & ResourceTypeFlags.D3DResource) != 0)
{
var offset = (dataOffset + res.Info.Offset);
var next = ((i + 1) < Resources.Count) ? (dataOffset + Resources[i + 1].Info.Offset) : fileSize;
var size = (next - offset);
fs.Position = offset;
res.Buffer = bs.ReadBytes(size);
}
}
}
}
}
public static class StreamExtensions
{
public static T Read<T>(this Stream stream)
{
var length = Marshal.SizeOf(typeof(T));
return stream.Read<T>(length);
}
public static T Read<T>(this Stream stream, int length)
{
var data = new byte[length];
var ptr = Marshal.AllocHGlobal(length);
stream.Read(data, 0, length);
Marshal.Copy(data, 0, ptr, length);
var t = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return t;
}
public static void Write<T>(this Stream stream, T data)
{
var length = Marshal.SizeOf(typeof(T));
Write<T>(stream, data, length);
}
public static void Write<T>(this Stream stream, T data, int length)
{
// this might be extremely unsafe to do, but it should work fine
var buffer = new byte[length];
var pData = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, 0);
Marshal.StructureToPtr(data, pData, false);
stream.Write(buffer, 0, length);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment