mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2026-05-14 11:26:14 +08:00
Moved all GameFiles to CodeWalker.Core
This commit is contained in:
@@ -0,0 +1,792 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using TC = System.ComponentModel.TypeConverterAttribute;
|
||||
using EXP = System.ComponentModel.ExpandableObjectConverter;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TC(typeof(EXP))]public class AwcFile : PackedFile
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
public uint Magic { get; set; }
|
||||
public ushort Version { get; set; }
|
||||
public ushort Flags { get; set; }
|
||||
public int StreamCount { get; set; }
|
||||
public int InfoOffset { get; set; }
|
||||
|
||||
public bool MultiChannel { get; set; }
|
||||
public byte[] MultiChannelData { get; set; }
|
||||
|
||||
public AwcStreamInfo[] StreamInfos { get; set; }
|
||||
public uint[] AudioIds { get; set; }
|
||||
public AwcAudio[] Audios { get; set; }
|
||||
|
||||
static public void Decrypt_RSXXTEA(byte[] data, uint[] key)
|
||||
{
|
||||
// Rockstar's modified version of XXTEA
|
||||
uint[] blocks = new uint[data.Length / 4];
|
||||
Buffer.BlockCopy(data, 0, blocks, 0, data.Length);
|
||||
|
||||
int block_count = blocks.Length;
|
||||
uint a, b = blocks[0], i;
|
||||
|
||||
i = (uint)(0x9E3779B9 * (6 + 52 / block_count));
|
||||
do
|
||||
{
|
||||
for (int block_index = block_count - 1; block_index >= 0; --block_index)
|
||||
{
|
||||
a = blocks[(block_index > 0 ? block_index : block_count) - 1];
|
||||
b = blocks[block_index] -= (a >> 5 ^ b << 2) + (b >> 3 ^ a << 4) ^ (i ^ b) + (key[block_index & 3 ^ (i >> 2 & 3)] ^ a ^ 0x7B3A207F);
|
||||
}
|
||||
i -= 0x9E3779B9;
|
||||
} while (i != 0);
|
||||
|
||||
Buffer.BlockCopy(blocks, 0, data, 0, data.Length);
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
|
||||
//adapted from libertyV code
|
||||
|
||||
|
||||
//MemoryStream ms = new MemoryStream(data);
|
||||
Name = entry.Name;
|
||||
FileEntry = entry;
|
||||
Data = data;
|
||||
|
||||
if ((data == null) || (data.Length < 8))
|
||||
{
|
||||
ErrorMessage = "Data null or too short!";
|
||||
return; //nothing to do, not enough data...
|
||||
}
|
||||
|
||||
Endianess endianess = Endianess.LittleEndian;
|
||||
|
||||
Magic = BitConverter.ToUInt32(data, 0);
|
||||
if (Magic != 0x54414441 && Magic != 0x41444154)
|
||||
{
|
||||
if (data.Length % 4 == 0)
|
||||
{
|
||||
Decrypt_RSXXTEA(data, GTA5Keys.PC_AWC_KEY);
|
||||
Magic = BitConverter.ToUInt32(data, 0);
|
||||
} else
|
||||
ErrorMessage = "Corrupted data!";
|
||||
}
|
||||
|
||||
switch (Magic)
|
||||
{
|
||||
default:
|
||||
ErrorMessage = "Unexpected Magic 0x" + Magic.ToString("X");
|
||||
return;
|
||||
case 0x54414441:
|
||||
endianess = Endianess.LittleEndian;
|
||||
break;
|
||||
case 0x41444154:
|
||||
endianess = Endianess.BigEndian;
|
||||
break;
|
||||
}
|
||||
|
||||
using (MemoryStream ms = new MemoryStream(data))
|
||||
{
|
||||
DataReader r = new DataReader(ms, endianess);
|
||||
|
||||
Magic = r.ReadUInt32();
|
||||
Version = r.ReadUInt16();
|
||||
Flags = r.ReadUInt16();
|
||||
StreamCount = r.ReadInt32();
|
||||
InfoOffset = r.ReadInt32();
|
||||
|
||||
|
||||
//notes from libertyV:
|
||||
// first bit - means that there are unknown word for each stream after this header
|
||||
// second bit - I think that it means that not all the tags are in the start of the file, but all the tags of a stream are near the data tag
|
||||
// third bit - Multi channel audio
|
||||
|
||||
if ((Flags >> 8) != 0xFF)
|
||||
{
|
||||
ErrorMessage = "Flags 0 not supported!";
|
||||
return;
|
||||
}
|
||||
if ((Flags & 0xF8) != 0)
|
||||
{
|
||||
//ErrorMessage = "Flags 1 not supported!";
|
||||
//return;
|
||||
}
|
||||
|
||||
MultiChannel = ((Flags & 4) == 4);
|
||||
|
||||
|
||||
var flag0 = ((Flags & 1) == 1);
|
||||
var infoStart = 16 + (flag0 ? (StreamCount * 2) : 0);
|
||||
|
||||
ms.Position = infoStart;
|
||||
|
||||
List<AwcStreamInfo> infos = new List<AwcStreamInfo>();
|
||||
Dictionary<uint, AwcStreamInfo> infoDict = new Dictionary<uint, AwcStreamInfo>();
|
||||
List<uint> audioIds = new List<uint>();
|
||||
List<AwcAudio> audios = new List<AwcAudio>();
|
||||
|
||||
for (int i = 0; i < StreamCount; i++)
|
||||
{
|
||||
var info = new AwcStreamInfo(r);
|
||||
infos.Add(info);
|
||||
infoDict[info.Id] = info;
|
||||
}
|
||||
for (int i = 0; i < StreamCount; i++)
|
||||
{
|
||||
var info = infos[i];
|
||||
for (int j = 0; j < info.TagCount; j++)
|
||||
{
|
||||
var chunk = new AwcChunkInfo(r);
|
||||
info.Chunks[chunk.Tag] = chunk;
|
||||
}
|
||||
}
|
||||
|
||||
StreamInfos = infos.ToArray();
|
||||
|
||||
|
||||
|
||||
byte hformat = 0xFA;// 250 0x6061D4FA & 0xFF; //JenkHash.GenHash("format");
|
||||
byte hdata = 0x55;// 85 0x5EB5E655 & 0xFF; //JenkHash.GenHash("data");
|
||||
byte hycd = 0x5C;// 92 YCD resource chunk... lip sync anims?
|
||||
byte hunk = 0x36;// 54 unk chunk? small number of bytes (2+)
|
||||
|
||||
|
||||
|
||||
if (MultiChannel)
|
||||
{
|
||||
AwcStreamInfo stream0 = null;
|
||||
if (!infoDict.TryGetValue(0, out stream0))
|
||||
{
|
||||
ErrorMessage = "Couldn't find MultiChannel stream0";
|
||||
return;
|
||||
}
|
||||
|
||||
AwcChunkInfo chunk72 = null;
|
||||
if (!stream0.Chunks.TryGetValue(72, out chunk72))
|
||||
{
|
||||
ErrorMessage = "Couldn't find MultiChannel chunk72";
|
||||
return;
|
||||
}
|
||||
|
||||
ms.Position = chunk72.Offset;
|
||||
|
||||
AwcChannelChunkInfo chanInfo = new AwcChannelChunkInfo(r);
|
||||
if (chanInfo.ChannelCount != StreamCount - 1)
|
||||
{
|
||||
ErrorMessage = "Channel Count did not match Stream Count";
|
||||
return;
|
||||
}
|
||||
|
||||
List<AwcChannelChunkItemInfo> chunkItems = new List<AwcChannelChunkItemInfo>();
|
||||
for (int i = 0; i < chanInfo.ChannelCount; i++)
|
||||
{
|
||||
var itemInfo = new AwcChannelChunkItemInfo(r);
|
||||
chunkItems.Add(itemInfo);
|
||||
audioIds.Add(infos[i + 1].Id);
|
||||
}
|
||||
|
||||
//AudioStreams.Add(new MultiChannelAudio(new ChunkStream(this.Stream, streamsChunks[0][Tag("data")]), channelsInfoHeader, streamsInfo, header.BigEndian));
|
||||
|
||||
AwcChunkInfo cdata = null;
|
||||
if (!stream0.Chunks.TryGetValue(hdata, out cdata))
|
||||
{
|
||||
ErrorMessage = "Couldn't find Stream 0 data chunk";
|
||||
return;
|
||||
}
|
||||
|
||||
ms.Position = cdata.Offset;
|
||||
var lastPos = cdata.Offset + cdata.Size;
|
||||
//int chunkSize = 0x800;
|
||||
uint bigChunkSize = chanInfo.ChunkSize;
|
||||
var chanCount = chanInfo.ChannelCount;
|
||||
|
||||
MultiChannelData = r.ReadBytes(cdata.Size);
|
||||
ms.Position = cdata.Offset;
|
||||
|
||||
//var d = data;//temporary
|
||||
|
||||
////this doesn't seem to work :(
|
||||
//while (ms.Position < lastPos)
|
||||
//{
|
||||
// uint totalChunks = 0;
|
||||
// var startPos = ms.Position;
|
||||
// var curPos = startPos;
|
||||
// //byte[] chunkdata = r.ReadBytes(chunkSize);
|
||||
// //ms.Position = startPos;
|
||||
// AwcChannelChunkHeader[] chanHeaders = new AwcChannelChunkHeader[chanCount];
|
||||
// for (int i = 0; i < chanCount; i++)
|
||||
// {
|
||||
// var chanHeader = new AwcChannelChunkHeader(r);
|
||||
// chanHeaders[i] = chanHeader;
|
||||
// totalChunks += chanHeader.ChunkCount;
|
||||
// }
|
||||
// int headerSize = (int)(totalChunks * 4 + chanInfo.ChannelCount * AwcChannelChunkHeader.Size);
|
||||
// headerSize += (((-headerSize) % chunkSize) + chunkSize) % chunkSize; //todo: simplify this!
|
||||
// curPos += headerSize;
|
||||
// AwcChannelChunk[] chanChunks = new AwcChannelChunk[chanCount];
|
||||
// for (int i = 0; i < chanCount; i++)
|
||||
// {
|
||||
// var chanChunk = new AwcChannelChunk(r, chanHeaders[i], chunkItems[i]);
|
||||
// chanChunks[i] = chanChunk;
|
||||
// curPos += chanChunk.TotalDataSize;
|
||||
// }
|
||||
// if (curPos - startPos > chanInfo.ChunkSize)
|
||||
// {
|
||||
// ErrorMessage = "Chunk was bigger than the chunk size";
|
||||
// break;
|
||||
// }
|
||||
// if ((totalChunks == 0) || ((startPos + chanInfo.ChunkSize) > lastPos))
|
||||
// {
|
||||
// ErrorMessage = "Unable to read chunk";
|
||||
// break;
|
||||
// }
|
||||
// var newPos = startPos + bigChunkSize;
|
||||
// if (newPos >= lastPos) break;
|
||||
// ms.Position = newPos;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
for (int i = 0; i < StreamCount; i++)
|
||||
{
|
||||
var info = infos[i];
|
||||
|
||||
AwcChunkInfo cformat = null;
|
||||
if (!info.Chunks.TryGetValue(hformat, out cformat))
|
||||
{
|
||||
ErrorMessage = "Couldn't find Stream " + i.ToString() + " format chunk";
|
||||
continue;
|
||||
}
|
||||
|
||||
AwcChunkInfo cdata = null;
|
||||
if (!info.Chunks.TryGetValue(hdata, out cdata))
|
||||
{
|
||||
ErrorMessage = "Couldn't find Stream " + i.ToString() + " data chunk";
|
||||
continue;
|
||||
}
|
||||
|
||||
AwcChunkInfo cycd = null;
|
||||
AwcAudioAnimClipDict oycd = null;
|
||||
if (info.Chunks.TryGetValue(hycd, out cycd))
|
||||
{
|
||||
ms.Position = cycd.Offset;
|
||||
oycd = new AwcAudioAnimClipDict(r, cycd);
|
||||
}
|
||||
|
||||
AwcChunkInfo cunk = null;
|
||||
AwcAudioUnk ounk = null;
|
||||
if (info.Chunks.TryGetValue(hunk, out cunk))
|
||||
{
|
||||
ms.Position = cunk.Offset;
|
||||
ounk = new AwcAudioUnk(r, cunk);
|
||||
}
|
||||
|
||||
|
||||
ms.Position = cformat.Offset;
|
||||
AwcFormatChunk formatChunk = new AwcFormatChunk(r);
|
||||
|
||||
ms.Position = cdata.Offset;
|
||||
AwcAudio audio = new AwcAudio(r, info, formatChunk, cdata);
|
||||
|
||||
audio.ClipDict = oycd;
|
||||
audio.UnkData = ounk;
|
||||
|
||||
audios.Add(audio);
|
||||
audioIds.Add(info.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Audios = audios.ToArray();
|
||||
AudioIds = audioIds.ToArray();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
[TC(typeof(EXP))] public class AwcStreamInfo
|
||||
{
|
||||
public uint RawVal { get; set; }
|
||||
public uint TagCount { get; set; }
|
||||
public uint Id { get; set; }
|
||||
|
||||
public Dictionary<byte, AwcChunkInfo> Chunks { get; set; } = new Dictionary<byte, AwcChunkInfo>();
|
||||
|
||||
public AwcStreamInfo(DataReader r)
|
||||
{
|
||||
RawVal = r.ReadUInt32();
|
||||
TagCount = (RawVal >> 29);
|
||||
Id = (RawVal & 0x1FFFFFFF);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString("X") + ": " + TagCount.ToString() + " tags";
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcChunkInfo
|
||||
{
|
||||
public ulong RawVal { get; set; }
|
||||
public byte Tag { get; set; }
|
||||
public int Size { get; set; }
|
||||
public int Offset { get; set; }
|
||||
|
||||
public AwcChunkInfo(DataReader r)
|
||||
{
|
||||
RawVal = r.ReadUInt64();
|
||||
Tag = (byte)(RawVal >> 56);
|
||||
Size = (int)((RawVal >> 28) & 0x0FFFFFFF);
|
||||
Offset = (int)(RawVal & 0x0FFFFFFF);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Tag.ToString() + ": " + Size.ToString() + ", " + Offset.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcChannelChunkInfo
|
||||
{
|
||||
public uint Unk0 { get; set; }
|
||||
public uint ChunkSize { get; set; }
|
||||
public uint ChannelCount { get; set; }
|
||||
|
||||
public AwcChannelChunkInfo(DataReader r)
|
||||
{
|
||||
Unk0 = r.ReadUInt32();
|
||||
ChunkSize = r.ReadUInt32();
|
||||
ChannelCount = r.ReadUInt32();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Unk0.ToString() + ": " + ChunkSize.ToString() + ", " + ChannelCount.ToString() + " channels";
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcChannelChunkItemInfo
|
||||
{
|
||||
public uint Id { get; set; }
|
||||
public uint Samples { get; set; }
|
||||
public ushort Unk0 { get; set; }
|
||||
public ushort SamplesPerSecond { get; set; }
|
||||
public byte Unk1 { get; set; }
|
||||
public byte RoundSize { get; set; }
|
||||
public ushort Unk2 { get; set; }
|
||||
|
||||
public AwcChannelChunkItemInfo(DataReader r)
|
||||
{
|
||||
Id = r.ReadUInt32();
|
||||
Samples = r.ReadUInt32();
|
||||
Unk0 = r.ReadUInt16();
|
||||
SamplesPerSecond = r.ReadUInt16();
|
||||
Unk1 = r.ReadByte();
|
||||
RoundSize = r.ReadByte();
|
||||
Unk2 = r.ReadUInt16();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Id.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec, size: " + RoundSize.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcFormatChunk
|
||||
{
|
||||
public uint Samples { get; set; }
|
||||
public int LoopPoint { get; set; }
|
||||
public ushort SamplesPerSecond { get; set; }
|
||||
public short Headroom { get; set; }
|
||||
public ushort LoopBegin { get; set; }
|
||||
public ushort LoopEnd { get; set; }
|
||||
public ushort PlayEnd { get; set; }
|
||||
public byte PlayBegin { get; set; }
|
||||
|
||||
public enum CodecFormat {
|
||||
PCM = 0,
|
||||
ADPCM = 4
|
||||
}
|
||||
public CodecFormat Codec { get; set; }
|
||||
|
||||
public AwcFormatChunk(DataReader r)
|
||||
{
|
||||
Samples = r.ReadUInt32();
|
||||
LoopPoint = r.ReadInt32();
|
||||
SamplesPerSecond = r.ReadUInt16();
|
||||
Headroom = r.ReadInt16();
|
||||
LoopBegin = r.ReadUInt16();
|
||||
LoopEnd = r.ReadUInt16();
|
||||
PlayEnd = r.ReadUInt16();
|
||||
PlayBegin = r.ReadByte();
|
||||
Codec = (CodecFormat)r.ReadByte();
|
||||
|
||||
//Apparently sometimes this struct is longer? TODO: fix??
|
||||
//r.ReadUInt16();
|
||||
//r.ReadUInt16();
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Headroom.ToString() + ", " + Codec.ToString() + ": " + Samples.ToString() + " samples, " + SamplesPerSecond.ToString() + " samples/sec";
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcAudio
|
||||
{
|
||||
public AwcStreamInfo StreamInfo { get; set; }
|
||||
public AwcFormatChunk Format { get; set; }
|
||||
public AwcChunkInfo DataInfo { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public AwcAudioAnimClipDict ClipDict { get; set; }
|
||||
public AwcAudioUnk UnkData { get; set; }
|
||||
|
||||
|
||||
public short Channels = 1;
|
||||
public short BitsPerSample = 16;
|
||||
public int SamplesPerSecond
|
||||
{
|
||||
get
|
||||
{
|
||||
return Format?.SamplesPerSecond ?? 0;
|
||||
}
|
||||
}
|
||||
public int SampleCount
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)(Format?.Samples ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return "0x" + StreamInfo?.Id.ToString("X").PadLeft(8, '0') ?? "0";
|
||||
}
|
||||
}
|
||||
public string Type
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Format == null) return "Unknown";
|
||||
|
||||
string codec;
|
||||
switch (Format.Codec)
|
||||
{
|
||||
case AwcFormatChunk.CodecFormat.PCM:
|
||||
codec = "PCM";
|
||||
break;
|
||||
case AwcFormatChunk.CodecFormat.ADPCM:
|
||||
codec = "ADPCM";
|
||||
break;
|
||||
default:
|
||||
codec = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
var hz = Format.SamplesPerSecond;
|
||||
|
||||
return codec + ((hz > 0) ? (", " + hz.ToString() + " Hz") : "");
|
||||
}
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return Format == null ? 0 : (float)Format.Samples / Format.SamplesPerSecond;
|
||||
}
|
||||
}
|
||||
|
||||
public string LengthStr
|
||||
{
|
||||
get
|
||||
{
|
||||
return TimeSpan.FromSeconds(Length).ToString("m\\:ss");
|
||||
}
|
||||
}
|
||||
|
||||
public AwcAudio(DataReader r, AwcStreamInfo s, AwcFormatChunk f, AwcChunkInfo d)
|
||||
{
|
||||
StreamInfo = s;
|
||||
Format = f;
|
||||
DataInfo = d;
|
||||
|
||||
Data = r.ReadBytes(d.Size);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var hash = (StreamInfo?.Id.ToString("X") ?? "0").PadLeft(8, '0');
|
||||
return "0x" + hash + ": " + Format?.ToString() ?? "AwcAudio";
|
||||
}
|
||||
|
||||
public byte[] DecodeADPCM(byte[] data, int sampleCount)
|
||||
{
|
||||
byte[] dataPCM = new byte[data.Length * 4];
|
||||
int predictor = 0, step_index = 0, step = 0;
|
||||
int readingOffset = 0, writingOffset = 0, bytesInBlock = 0;
|
||||
|
||||
int[] ima_index_table = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
short[] ima_step_table = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
int clip(int value, int min, int max)
|
||||
{
|
||||
if (value < min)
|
||||
return min;
|
||||
if (value > max)
|
||||
return max;
|
||||
return value;
|
||||
}
|
||||
|
||||
void parseNibble(byte nibble)
|
||||
{
|
||||
step_index = clip(step_index + ima_index_table[nibble], 0, 88);
|
||||
|
||||
int diff = step >> 3;
|
||||
if ((nibble & 4) > 0) diff += step;
|
||||
if ((nibble & 2) > 0) diff += step >> 1;
|
||||
if ((nibble & 1) > 0) diff += step >> 2;
|
||||
if ((nibble & 8) > 0) predictor -= diff;
|
||||
else predictor += diff;
|
||||
|
||||
step = ima_step_table[step_index];
|
||||
|
||||
int samplePCM = clip(predictor, -32768, 32767);
|
||||
dataPCM[writingOffset] = (byte)(samplePCM & 0xFF);
|
||||
dataPCM[writingOffset + 1] = (byte)((samplePCM >> 8) & 0xFF);
|
||||
writingOffset += 2;
|
||||
}
|
||||
|
||||
while ((readingOffset < data.Length) && (sampleCount > 0))
|
||||
{
|
||||
if (bytesInBlock == 0)
|
||||
{
|
||||
step_index = clip(data[readingOffset], 0, 88);
|
||||
predictor = BitConverter.ToInt16(data, readingOffset + 2);
|
||||
step = ima_step_table[step_index];
|
||||
bytesInBlock = 2044;
|
||||
readingOffset += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
parseNibble((byte)(data[readingOffset] & 0x0F));
|
||||
parseNibble((byte)((data[readingOffset] >> 4) & 0x0F));
|
||||
bytesInBlock--;
|
||||
sampleCount -= 2;
|
||||
readingOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
return dataPCM;
|
||||
}
|
||||
|
||||
public Stream GetWavStream()
|
||||
{
|
||||
byte[] dataPCM = null;
|
||||
int bitsPerSample = BitsPerSample;
|
||||
|
||||
switch (Format.Codec)
|
||||
{
|
||||
case AwcFormatChunk.CodecFormat.PCM:
|
||||
dataPCM = Data;
|
||||
break;
|
||||
case AwcFormatChunk.CodecFormat.ADPCM:
|
||||
dataPCM = new byte[Data.Length];
|
||||
Buffer.BlockCopy(Data, 0, dataPCM, 0, Data.Length);
|
||||
AwcFile.Decrypt_RSXXTEA(dataPCM, GTA5Keys.PC_AWC_KEY);
|
||||
dataPCM = DecodeADPCM(dataPCM, SampleCount);
|
||||
bitsPerSample = 16;
|
||||
break;
|
||||
}
|
||||
|
||||
int byteRate = SamplesPerSecond * Channels * bitsPerSample / 8;
|
||||
short blockAlign = (short)(Channels * bitsPerSample / 8);
|
||||
|
||||
MemoryStream stream = new MemoryStream();
|
||||
BinaryWriter w = new BinaryWriter(stream);
|
||||
int wavLength = 12 + 24 + 8 + Data.Length;
|
||||
|
||||
// RIFF chunk
|
||||
w.Write("RIFF".ToCharArray());
|
||||
w.Write((int)(wavLength - 8));
|
||||
w.Write("WAVE".ToCharArray());
|
||||
|
||||
// fmt sub-chunk
|
||||
w.Write("fmt ".ToCharArray());
|
||||
w.Write((int)16); // fmt size
|
||||
w.Write((short)1); // 1 = WAVE_FORMAT_PCM
|
||||
w.Write((short)Channels);
|
||||
w.Write((int)SamplesPerSecond);
|
||||
w.Write((int)byteRate);
|
||||
w.Write((short)blockAlign);
|
||||
w.Write((short)bitsPerSample);
|
||||
|
||||
// data sub-chunk
|
||||
w.Write("data".ToCharArray());
|
||||
w.Write((int)dataPCM.Length);
|
||||
w.Write(dataPCM);
|
||||
|
||||
w.Flush();
|
||||
stream.Position = 0;
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcAudioAnimClipDict
|
||||
{
|
||||
public byte[] Data { get; set; }
|
||||
public ClipDictionary ClipDict { get; set; }
|
||||
|
||||
public AwcAudioAnimClipDict(DataReader r, AwcChunkInfo info)
|
||||
{
|
||||
Data = r.ReadBytes(info.Size);
|
||||
|
||||
if ((Data == null) || (Data.Length < 16)) return;
|
||||
|
||||
var data = Data;
|
||||
|
||||
RpfResourceFileEntry resentry = new RpfResourceFileEntry();
|
||||
uint rsc7 = BitConverter.ToUInt32(data, 0);
|
||||
int version = BitConverter.ToInt32(data, 4);
|
||||
resentry.SystemFlags = BitConverter.ToUInt32(data, 8);
|
||||
resentry.GraphicsFlags = BitConverter.ToUInt32(data, 12);
|
||||
|
||||
if (rsc7 != 0x37435352)
|
||||
{ } //testing..
|
||||
if (version != 46) //46 is Clip Dictionary...
|
||||
{ }
|
||||
|
||||
int newlen = data.Length - 16; //trim the header from the data passed to the next step.
|
||||
int arrlen = Math.Max(newlen, resentry.SystemSize + resentry.GraphicsSize);//expand it as necessary for the reader.
|
||||
byte[] newdata = new byte[arrlen];
|
||||
Buffer.BlockCopy(data, 16, newdata, 0, newlen);
|
||||
data = newdata;
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
ClipDict = rd.ReadBlock<ClipDictionary>();
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (ClipDict?.ClipsMapEntries ?? 0).ToString() + " entries";
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcAudioUnk
|
||||
{
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public AwcAudioUnk(DataReader r, AwcChunkInfo info)
|
||||
{
|
||||
Data = r.ReadBytes(info.Size);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (Data == null) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < Data.Length; i++)
|
||||
{
|
||||
if (sb.Length > 0) sb.Append(' ');
|
||||
sb.Append(Data[i].ToString());
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TC(typeof(EXP))] public class AwcChannelChunkHeader
|
||||
{
|
||||
public static uint Size = 16; //24 for ps3...
|
||||
|
||||
public uint StartChunk { get; set; }
|
||||
public uint ChunkCount { get; set; }
|
||||
public uint SamplesToSkip { get; set; } //mostly 0
|
||||
public uint SamplesPerChunk { get; set; }
|
||||
public uint DataSize { get; set; }
|
||||
|
||||
public AwcChannelChunkHeader(DataReader r)
|
||||
{
|
||||
StartChunk = r.ReadUInt32();
|
||||
ChunkCount = r.ReadUInt32();
|
||||
SamplesToSkip = r.ReadUInt32();
|
||||
SamplesPerChunk = r.ReadUInt32();
|
||||
DataSize = ChunkCount * 0x800;
|
||||
|
||||
//for ps3, two extra ints:
|
||||
//uint unk0 = r.ReadUint32();
|
||||
//DataSize = r.ReadUint32();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public class AwcChannelChunk
|
||||
{
|
||||
public AwcChannelChunkHeader Header { get; set; }
|
||||
public AwcChannelChunkItemInfo Info { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public uint TotalDataSize { get; set; }
|
||||
|
||||
public AwcChannelChunk(DataReader r, AwcChannelChunkHeader h, AwcChannelChunkItemInfo i)
|
||||
{
|
||||
Header = h;
|
||||
Info = i;
|
||||
|
||||
TotalDataSize = h.DataSize;
|
||||
|
||||
var rs = i?.RoundSize ?? 0;
|
||||
int ds = (int)h.DataSize;
|
||||
if (rs != 0)
|
||||
{
|
||||
TotalDataSize = (uint)(TotalDataSize + (((-ds) % rs) + rs) % rs);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,700 @@
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class CacheDatFile : PackedFile
|
||||
{
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
|
||||
public string Version { get; set; }
|
||||
public CacheFileDate[] FileDates { get; set; }
|
||||
|
||||
public Dictionary<uint, MapDataStoreNode> MapNodeDict { get; set; }
|
||||
public MapDataStoreNode[] RootMapNodes { get; set; }
|
||||
//public Dictionary<MetaHash, CInteriorProxy> InteriorProxyDict { get; set; }
|
||||
public Dictionary<MetaHash, BoundsStoreItem> BoundsStoreDict { get; set; }
|
||||
|
||||
public MapDataStoreNode[] AllMapNodes { get; set; }
|
||||
public CInteriorProxy[] AllCInteriorProxies { get; set; }
|
||||
public BoundsStoreItem[] AllBoundsStoreItems { get; set; }
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
FileEntry = entry;
|
||||
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
BinaryReader br = new BinaryReader(ms);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; (i < 100) && (i < data.Length); i++)
|
||||
{
|
||||
//read version string.
|
||||
byte b = data[i];
|
||||
if (b == 0) break;
|
||||
sb.Append((char)b);
|
||||
}
|
||||
Version = sb.ToString().Replace("[VERSION]", "").Replace("\r", "").Replace("\n", "");
|
||||
sb.Clear();
|
||||
int lastn = 0;
|
||||
int lspos = 0;
|
||||
uint structcount = 0;
|
||||
uint modlen;
|
||||
bool indates = false;
|
||||
List<string> lines = new List<string>();
|
||||
var dates = new List<CacheFileDate>();
|
||||
var allMapNodes = new List<MapDataStoreNode>();
|
||||
var allCInteriorProxies = new List<CInteriorProxy>();
|
||||
var allBoundsStoreItems = new List<BoundsStoreItem>();
|
||||
for (int i = 100; i < data.Length; i++)
|
||||
{
|
||||
byte b = data[i];
|
||||
if (b == 0)
|
||||
break;
|
||||
if (b == 0xA)
|
||||
{
|
||||
lastn = i;
|
||||
string line = sb.ToString();
|
||||
lines.Add(line);
|
||||
switch (line)
|
||||
{
|
||||
case "<fileDates>":
|
||||
indates = true;
|
||||
break;
|
||||
case "</fileDates>":
|
||||
indates = false;
|
||||
break;
|
||||
case "<module>":
|
||||
break;
|
||||
case "</module>":
|
||||
break;
|
||||
case "fwMapDataStore":
|
||||
ms.Position = i + 1;
|
||||
modlen = br.ReadUInt32();
|
||||
structcount = modlen / 64;
|
||||
lspos = i + (int)modlen + 5;
|
||||
while (ms.Position<lspos)
|
||||
{
|
||||
allMapNodes.Add(new MapDataStoreNode(br));
|
||||
}
|
||||
//if (allMapNodes.Count != structcount)
|
||||
//{ }//test fail due to variable length struct
|
||||
i += (int)(modlen + 4);
|
||||
break;
|
||||
case "CInteriorProxy":
|
||||
ms.Position = i + 1;
|
||||
modlen = br.ReadUInt32();
|
||||
structcount = modlen / 104;
|
||||
lspos = i + (int)modlen + 5;
|
||||
while (ms.Position < lspos)
|
||||
{
|
||||
allCInteriorProxies.Add(new CInteriorProxy(br));
|
||||
}
|
||||
if (allCInteriorProxies.Count != structcount)
|
||||
{ }//all pass here
|
||||
i += (int)(modlen + 4);
|
||||
break;
|
||||
case "BoundsStore":
|
||||
ms.Position = i + 1;
|
||||
modlen = br.ReadUInt32();
|
||||
structcount = modlen / 32;
|
||||
lspos = i + (int)modlen + 5;
|
||||
while (ms.Position < lspos)
|
||||
{
|
||||
allBoundsStoreItems.Add(new BoundsStoreItem(br));
|
||||
}
|
||||
if (allBoundsStoreItems.Count != structcount)
|
||||
{ }//all pass here
|
||||
i += (int)(modlen + 4);
|
||||
break;
|
||||
default:
|
||||
if (!indates)
|
||||
{ } //just testing
|
||||
else
|
||||
{
|
||||
dates.Add(new CacheFileDate(line));//eg: 2740459947 130680580712018938 8944
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
sb.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append((char)b);
|
||||
}
|
||||
}
|
||||
FileDates = dates.ToArray();
|
||||
AllMapNodes = allMapNodes.ToArray();
|
||||
AllCInteriorProxies = allCInteriorProxies.ToArray();
|
||||
AllBoundsStoreItems = allBoundsStoreItems.ToArray();
|
||||
|
||||
MapNodeDict = new Dictionary<uint, MapDataStoreNode>();
|
||||
var rootMapNodes = new List<MapDataStoreNode>();
|
||||
foreach (var mapnode in AllMapNodes)
|
||||
{
|
||||
MapNodeDict[mapnode.Name] = mapnode;
|
||||
if (mapnode.ParentName == 0)
|
||||
{
|
||||
rootMapNodes.Add(mapnode);
|
||||
}
|
||||
if (mapnode.UnkExtra != null)
|
||||
{ }//notsure what to do with this
|
||||
}
|
||||
foreach (var mapnode in AllMapNodes)
|
||||
{
|
||||
MapDataStoreNode pnode;
|
||||
if (MapNodeDict.TryGetValue(mapnode.ParentName, out pnode))
|
||||
{
|
||||
pnode.AddChildToList(mapnode);
|
||||
}
|
||||
else if ((mapnode.ParentName != 0))
|
||||
{ }
|
||||
}
|
||||
foreach (var mapnode in AllMapNodes)
|
||||
{
|
||||
mapnode.ChildrenListToArray();
|
||||
}
|
||||
RootMapNodes = rootMapNodes.ToArray();
|
||||
|
||||
|
||||
|
||||
BoundsStoreDict = new Dictionary<MetaHash, BoundsStoreItem>();
|
||||
foreach (BoundsStoreItem item in AllBoundsStoreItems)
|
||||
{
|
||||
BoundsStoreItem mbsi = null;
|
||||
if (BoundsStoreDict.TryGetValue(item.Name, out mbsi))
|
||||
{ }
|
||||
BoundsStoreDict[item.Name] = item;
|
||||
}
|
||||
|
||||
//InteriorProxyDict = new Dictionary<MetaHash, CInteriorProxy>();
|
||||
foreach (CInteriorProxy prx in AllCInteriorProxies)
|
||||
{
|
||||
//CInteriorProxy mprx = null;
|
||||
//if (InteriorProxyDict.TryGetValue(prx.Name, out mprx))
|
||||
//{ }
|
||||
//InteriorProxyDict[prx.Name] = prx;//can't do this! multiples with same name different pos
|
||||
|
||||
|
||||
MapDataStoreNode mnode = null;
|
||||
if (MapNodeDict.TryGetValue(prx.Parent, out mnode))
|
||||
{
|
||||
mnode.AddInteriorToList(prx);
|
||||
}
|
||||
else
|
||||
{ }
|
||||
}
|
||||
foreach (var mapnode in AllMapNodes)
|
||||
{
|
||||
mapnode.InteriorProxyListToArray();
|
||||
}
|
||||
|
||||
|
||||
br.Dispose();
|
||||
ms.Dispose();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void LoadXml(string xml)
|
||||
{
|
||||
}
|
||||
|
||||
public string GetXml()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine(MetaXmlBase.XmlHeader);
|
||||
sb.AppendLine(string.Format("<CacheDatFile version=\"{0}\">", Version));
|
||||
sb.AppendLine(" <fileDates>");
|
||||
if (FileDates != null)
|
||||
{
|
||||
foreach (var date in FileDates)
|
||||
{
|
||||
sb.AppendLine(string.Format(" <fileDate>{0}</fileDate>", date.ToCacheFileString()));
|
||||
}
|
||||
}
|
||||
sb.AppendLine(" </fileDates>");
|
||||
sb.AppendLine(" <module type=\"fwMapDataStore\">");
|
||||
if (AllMapNodes != null)
|
||||
{
|
||||
foreach (var mapnode in AllMapNodes)
|
||||
{
|
||||
sb.AppendLine(" <Item>");
|
||||
sb.AppendLine(string.Format(" <name>{0}</name>", mapnode.Name.ToCleanString()));
|
||||
sb.AppendLine(string.Format(" <parent>{0}</parent>", mapnode.ParentName.ToCleanString()));
|
||||
sb.AppendLine(string.Format(" <contentFlags value=\"{0}\" />", mapnode.ContentFlags.ToString()));
|
||||
sb.AppendLine(string.Format(" <streamingExtentsMin {0} />", FloatUtil.GetVector3XmlString(mapnode.streamingExtentsMin)));
|
||||
sb.AppendLine(string.Format(" <streamingExtentsMax {0} />", FloatUtil.GetVector3XmlString(mapnode.streamingExtentsMax)));
|
||||
sb.AppendLine(string.Format(" <entitiesExtentsMin {0} />", FloatUtil.GetVector3XmlString(mapnode.entitiesExtentsMin)));
|
||||
sb.AppendLine(string.Format(" <entitiesExtentsMax {0} />", FloatUtil.GetVector3XmlString(mapnode.entitiesExtentsMax)));
|
||||
sb.AppendLine(string.Format(" <flags unk1=\"{0}\" unk2=\"{1}\" unk3=\"{2}\" />", mapnode.Unk1, mapnode.Unk2, mapnode.Unk3));
|
||||
sb.AppendLine(" </Item>");
|
||||
}
|
||||
}
|
||||
sb.AppendLine(" </module>");
|
||||
sb.AppendLine(" <module type=\"CInteriorProxy\">");
|
||||
if (AllCInteriorProxies != null)
|
||||
{
|
||||
foreach (var intprox in AllCInteriorProxies)
|
||||
{
|
||||
sb.AppendLine(" <Item>");
|
||||
sb.AppendLine(string.Format(" <name>{0}</name>", intprox.Name.ToCleanString()));
|
||||
sb.AppendLine(string.Format(" <parent>{0}</parent>", intprox.Parent.ToCleanString()));
|
||||
sb.AppendLine(string.Format(" <position {0} />", FloatUtil.GetVector3XmlString(intprox.Position)));
|
||||
sb.AppendLine(string.Format(" <rotation {0} />", FloatUtil.GetQuaternionXmlString(intprox.Orientation)));
|
||||
sb.AppendLine(string.Format(" <aabbMin {0} />", FloatUtil.GetVector3XmlString(intprox.BBMin)));
|
||||
sb.AppendLine(string.Format(" <aabbMax {0} />", FloatUtil.GetVector3XmlString(intprox.BBMax)));
|
||||
sb.AppendLine(string.Format(" <unknowns1 unk01=\"{0}\" unk03=\"{1}\" />", intprox.Unk01, intprox.Unk03));
|
||||
sb.AppendLine(string.Format(" <unknowns2 unk11=\"{0}\" unk12=\"{1}\" unk13=\"{1}\" unk14=\"{1}\" />", intprox.Unk11, intprox.Unk12, intprox.Unk13, intprox.Unk14));
|
||||
sb.AppendLine(string.Format(" <unknowns3 unk15=\"{0}\" unk16=\"{1}\" unk17=\"{1}\" unk18=\"{1}\" />", intprox.Unk15, intprox.Unk16, intprox.Unk17, intprox.Unk18));
|
||||
sb.AppendLine(" </Item>");
|
||||
}
|
||||
}
|
||||
sb.AppendLine(" </module>");
|
||||
sb.AppendLine(" <module type=\"BoundsStore\">");
|
||||
if (AllBoundsStoreItems != null)
|
||||
{
|
||||
foreach (var bndstore in AllBoundsStoreItems)
|
||||
{
|
||||
sb.AppendLine(" <Item>");
|
||||
sb.AppendLine(string.Format(" <name>{0}</name>", bndstore.Name.ToCleanString()));
|
||||
sb.AppendLine(string.Format(" <aabbMin {0} />", FloatUtil.GetVector3XmlString(bndstore.Min)));
|
||||
sb.AppendLine(string.Format(" <aabbMax {0} />", FloatUtil.GetVector3XmlString(bndstore.Max)));
|
||||
sb.AppendLine(string.Format(" <layer value=\"{0}\" />", bndstore.Layer));
|
||||
sb.AppendLine(" </Item>");
|
||||
}
|
||||
}
|
||||
sb.AppendLine(" </module>");
|
||||
sb.AppendLine("</CacheDatFile>");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (FileEntry != null)
|
||||
{
|
||||
return FileEntry.ToString();
|
||||
}
|
||||
return base.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class CacheFileDate
|
||||
{
|
||||
public MetaHash FileName { get; set; } //"resource_surrogate:/%s.rpf"
|
||||
public DateTime TimeStamp { get; set; }
|
||||
public uint FileID { get; set; }
|
||||
|
||||
public CacheFileDate(string line)
|
||||
{
|
||||
string[] parts = line.Split(' ');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
FileName = new MetaHash(uint.Parse(parts[0]));
|
||||
TimeStamp = DateTime.FromFileTimeUtc(long.Parse(parts[1]));
|
||||
FileID = uint.Parse(parts[2]);
|
||||
}
|
||||
else
|
||||
{ } //testing
|
||||
}
|
||||
|
||||
public string ToCacheFileString()
|
||||
{
|
||||
return FileName.Hash.ToString() + " " + TimeStamp.ToFileTimeUtc().ToString() + " " + FileID.ToString();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return FileName.ToString() + ", " + TimeStamp.ToString() + ", " + FileID.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class BoundsStoreItem
|
||||
{
|
||||
public MetaHash Name { get; set; }
|
||||
public Vector3 Min { get; set; }
|
||||
public Vector3 Max { get; set; }
|
||||
public uint Layer { get; set; }
|
||||
|
||||
public BoundsStoreItem(Bounds b)
|
||||
{
|
||||
Name = 0;
|
||||
Min = b.BoundingBoxMin;
|
||||
Max = b.BoundingBoxMax;
|
||||
Layer = 0;
|
||||
}
|
||||
public BoundsStoreItem(BinaryReader br)
|
||||
{
|
||||
Name = new MetaHash(br.ReadUInt32());
|
||||
Min = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
Max = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
Layer = br.ReadUInt32();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name.ToString() + ", " +
|
||||
Min.ToString() + ", " +
|
||||
Max.ToString() + ", " +
|
||||
Layer.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class CInteriorProxy
|
||||
{
|
||||
public uint Unk01 { get; set; }
|
||||
public uint Unk02 { get; set; }
|
||||
public uint Unk03 { get; set; }
|
||||
public MetaHash Name { get; set; }
|
||||
public MetaHash Parent { get; set; }
|
||||
public Vector3 Position { get; set; }
|
||||
public Quaternion Orientation { get; set; }
|
||||
public Vector3 BBMin { get; set; }
|
||||
public Vector3 BBMax { get; set; }
|
||||
public float Unk11 { get; set; }
|
||||
public uint Unk12 { get; set; }
|
||||
public float Unk13 { get; set; }
|
||||
public uint Unk14 { get; set; }
|
||||
public float Unk15 { get; set; }
|
||||
public uint Unk16 { get; set; }
|
||||
public uint Unk17 { get; set; }
|
||||
public uint Unk18 { get; set; }
|
||||
|
||||
public CInteriorProxy(BinaryReader br)
|
||||
{
|
||||
Unk01 = br.ReadUInt32();
|
||||
Unk02 = br.ReadUInt32();
|
||||
Unk03 = br.ReadUInt32();
|
||||
Name = new MetaHash(br.ReadUInt32());
|
||||
Parent = new MetaHash(br.ReadUInt32());
|
||||
Position = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
Orientation = new Quaternion(br.ReadSingle(), br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
BBMin = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
BBMax = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
Unk11 = br.ReadSingle();
|
||||
Unk12 = br.ReadUInt32();
|
||||
Unk13 = br.ReadSingle();
|
||||
Unk14 = br.ReadUInt32();
|
||||
Unk15 = br.ReadSingle();
|
||||
Unk16 = br.ReadUInt32();
|
||||
Unk17 = br.ReadUInt32();//could be float
|
||||
Unk18 = br.ReadUInt32();
|
||||
|
||||
|
||||
|
||||
switch (Unk01)
|
||||
{
|
||||
case 0: //v_cashdepot
|
||||
case 19: //dt1_02_carpark
|
||||
case 20: //dt1_03_carpark
|
||||
case 21: //dt1_05_carpark
|
||||
case 4: //dt1_rd1_tun
|
||||
case 14: //id1_11_tunnel1_int
|
||||
case 24: //v_firedept
|
||||
case 3: //id2_21_a_tun1
|
||||
case 22: //po1_08_warehouseint1
|
||||
case 11: //sc1_rd_inttun1
|
||||
case 10: //sc1_rd_inttun2b_end
|
||||
case 18: //bt1_04_carpark
|
||||
case 16: //v_hanger
|
||||
case 1: //ap1_03_lisapark_subway
|
||||
case 13: //kt1_03_carpark_int
|
||||
case 5: //sm20_tun1
|
||||
case 2: //vbca_tunnel1
|
||||
case 15: //cs1_12_tunnel01_int
|
||||
case 6: //cs1_14brailway1
|
||||
case 9: //cs2_roadsb_tunnel_01
|
||||
case 7: //cs3_03railtunnel_int1
|
||||
case 8: //cs4_rwayb_tunnelint
|
||||
case 12: //ch1_roadsdint_tun1
|
||||
case 100: //hei_int_mph_carrierhang3
|
||||
case 47: //xm_x17dlc_int_base_ent
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (Unk02 != 0)
|
||||
{ }
|
||||
|
||||
switch (Unk03)
|
||||
{
|
||||
case 6: //v_cashdepot
|
||||
case 2: //dt1_02_carpark
|
||||
case 8: //v_fib01
|
||||
case 4: //v_fib03
|
||||
case 0: //v_fib04
|
||||
case 7: //v_clotheslo
|
||||
case 1: //v_gun
|
||||
case 3: //v_genbank
|
||||
case 11: //v_hospital
|
||||
case 5: //v_shop_247
|
||||
case 32: //v_abattoir
|
||||
case 13: //v_franklins
|
||||
case 15: //v_michael
|
||||
case 18: //v_faceoffice
|
||||
case 29: //v_recycle
|
||||
case 9: //v_stadium
|
||||
case 54: //v_farmhouse
|
||||
case 12: //v_ranch
|
||||
case 26: //hei_gta_milo_bar
|
||||
case 17: //hei_gta_milo_bedrm
|
||||
case 14: //hei_gta_milo_bridge
|
||||
case 48: //apa_mpapa_yacht
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ((Unk12 == 0) || (Unk12 > 0xFFFFFF))
|
||||
{ }
|
||||
|
||||
switch (Unk14)
|
||||
{
|
||||
case 1:
|
||||
case 0:
|
||||
case 580:
|
||||
case 355: //sm_smugdlc_int_01
|
||||
case 579: //xm_x17dlc_int_01
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (Unk16)
|
||||
{
|
||||
case 1:
|
||||
case 32758: //0x7FF6
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
switch (Unk17) //could be a float..!
|
||||
{
|
||||
case 9:
|
||||
break;
|
||||
case 0x415CBC04: //13.7959f
|
||||
case 0x7B81AC94:
|
||||
case 0x40FE3224: //7.94362f v_gun
|
||||
case 0x41515774: //13.0839f v_gun
|
||||
case 0x414E7B34: //12.9051f bkr_biker_dlc_int_03
|
||||
case 0x41389C14: //11.5381f imp_impexp_int_01
|
||||
case 0x4177B664: //15.482f gr_grdlc_int_01
|
||||
case 0xCE0404F4: // sm_smugdlc_int_01
|
||||
break;
|
||||
default:
|
||||
//string str = JenkIndex.GetString(Unk17);
|
||||
break;
|
||||
}
|
||||
switch(Unk18)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 32758: //0x7FF6
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Unk01.ToString() + ", " +
|
||||
Unk02.ToString() + ", " +
|
||||
Unk03.ToString() + ", " +
|
||||
Name.ToString() + ", " +
|
||||
Parent.ToString() + ", " +
|
||||
Position.ToString() + ", " +
|
||||
Orientation.ToString() + ", " +
|
||||
BBMin.ToString() + ", " +
|
||||
BBMax.ToString() + ", " +
|
||||
Unk11.ToString() + ", " +
|
||||
Unk12.ToString() + ", " +
|
||||
Unk13.ToString() + ", " +
|
||||
Unk14.ToString() + ", " +
|
||||
Unk15.ToString() + ", " +
|
||||
Unk16.ToString() + ", " +
|
||||
Unk17.ToString() + ", " +
|
||||
Unk18.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class MapDataStoreNode
|
||||
{
|
||||
public MetaHash Name { get; set; }
|
||||
public MetaHash ParentName { get; set; }
|
||||
public uint ContentFlags { get; set; }
|
||||
public Vector3 streamingExtentsMin { get; set; }
|
||||
public Vector3 streamingExtentsMax { get; set; }
|
||||
public Vector3 entitiesExtentsMin { get; set; }
|
||||
public Vector3 entitiesExtentsMax { get; set; }
|
||||
public byte Unk1 { get; set; }
|
||||
public byte Unk2 { get; set; }
|
||||
public byte Unk3 { get; set; }
|
||||
public byte Unk4 { get; set; }
|
||||
|
||||
public MapDataStoreNodeExtra UnkExtra { get; set; }
|
||||
|
||||
public MapDataStoreNode[] Children { get; set; }
|
||||
private List<MapDataStoreNode> ChildrenList; //used when building the array
|
||||
|
||||
public CInteriorProxy[] InteriorProxies { get; set; }
|
||||
private List<CInteriorProxy> InteriorProxyList;
|
||||
|
||||
public MapDataStoreNode(YmapFile ymap)
|
||||
{
|
||||
Name = ymap._CMapData.name;
|
||||
ParentName = ymap._CMapData.parent;
|
||||
ContentFlags = ymap._CMapData.contentFlags;
|
||||
streamingExtentsMin = ymap._CMapData.streamingExtentsMin;
|
||||
streamingExtentsMax = ymap._CMapData.streamingExtentsMax;
|
||||
entitiesExtentsMin = ymap._CMapData.entitiesExtentsMin;
|
||||
entitiesExtentsMax = ymap._CMapData.entitiesExtentsMax;
|
||||
}
|
||||
public MapDataStoreNode(BinaryReader br)
|
||||
{
|
||||
Name = new MetaHash(br.ReadUInt32());
|
||||
ParentName = new MetaHash(br.ReadUInt32());
|
||||
ContentFlags = br.ReadUInt32();
|
||||
streamingExtentsMin = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
streamingExtentsMax = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
entitiesExtentsMin = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
entitiesExtentsMax = new Vector3(br.ReadSingle(), br.ReadSingle(), br.ReadSingle());
|
||||
Unk1 = br.ReadByte(); //HD flag? (critical, long, strm)
|
||||
Unk2 = br.ReadByte(); //lod flag? - primary map files
|
||||
Unk3 = br.ReadByte(); //slod flag?
|
||||
Unk4 = br.ReadByte();
|
||||
|
||||
if (Unk1 != 0)
|
||||
{ }
|
||||
if (Unk2 != 0)
|
||||
{ }
|
||||
if (Unk3 != 0)
|
||||
{ }
|
||||
if (Unk4 != 0)
|
||||
{ } //no hits here now..
|
||||
|
||||
if (Unk4 == 0xFE)
|
||||
{
|
||||
//this seems to never be hit anymore...
|
||||
UnkExtra = new MapDataStoreNodeExtra(br);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void AddChildToList(MapDataStoreNode child)
|
||||
{
|
||||
if (ChildrenList == null)
|
||||
{
|
||||
ChildrenList = new List<MapDataStoreNode>();
|
||||
}
|
||||
ChildrenList.Add(child);
|
||||
}
|
||||
public void ChildrenListToArray()
|
||||
{
|
||||
if (ChildrenList != null)
|
||||
{
|
||||
Children = ChildrenList.ToArray();
|
||||
ChildrenList = null; //plz get this GC
|
||||
}
|
||||
}
|
||||
public void AddInteriorToList(CInteriorProxy iprx)
|
||||
{
|
||||
if (InteriorProxyList == null)
|
||||
{
|
||||
InteriorProxyList = new List<CInteriorProxy>();
|
||||
}
|
||||
InteriorProxyList.Add(iprx);
|
||||
}
|
||||
public void InteriorProxyListToArray()
|
||||
{
|
||||
if (InteriorProxyList != null)
|
||||
{
|
||||
InteriorProxies = InteriorProxyList.ToArray();
|
||||
InteriorProxyList = null; //plz get this GC
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name.ToString() + ", " +
|
||||
ParentName.ToString() + ", " +
|
||||
ContentFlags.ToString() + ", " +
|
||||
streamingExtentsMin.ToString() + ", " +
|
||||
streamingExtentsMax.ToString() + ", " +
|
||||
entitiesExtentsMin.ToString() + ", " +
|
||||
entitiesExtentsMax.ToString();// + ", " +
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class MapDataStoreNodeExtra
|
||||
{
|
||||
public uint Unk01; //0
|
||||
public byte[] Unk02; //1 - 16 (60 bytes)
|
||||
public uint Unk03;//16
|
||||
public uint Unk04;
|
||||
public uint Unk05;
|
||||
public uint Unk06;
|
||||
public uint Unk07;
|
||||
public uint Unk08;
|
||||
public uint Unk09;
|
||||
public uint Unk10;
|
||||
|
||||
public string Unk02str
|
||||
{
|
||||
get
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (Unk02 != null)
|
||||
{
|
||||
for (int i = 0; i < Unk02.Length; i++)
|
||||
{
|
||||
if (Unk02[i] == 0) break;
|
||||
sb.Append((char)Unk02[i]);
|
||||
}
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public MapDataStoreNodeExtra(BinaryReader br)
|
||||
{
|
||||
Unk01 = br.ReadUInt32();
|
||||
|
||||
Unk02 = new byte[60];
|
||||
for (int i = 0; i < 60; i++)
|
||||
{
|
||||
Unk02[i] = br.ReadByte();
|
||||
}
|
||||
Unk03 = br.ReadUInt32();
|
||||
Unk04 = br.ReadUInt32();
|
||||
Unk05 = br.ReadUInt32();
|
||||
Unk06 = br.ReadUInt32();
|
||||
Unk07 = br.ReadUInt32();
|
||||
Unk08 = br.ReadUInt32();
|
||||
Unk09 = br.ReadUInt32();
|
||||
Unk10 = br.ReadUInt32();
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Unk01.ToString() + ", " + Unk02str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class CutFile : PackedFile
|
||||
{
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
public PsoFile Pso { get; set; }
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
//MemoryStream ms = new MemoryStream(data);
|
||||
|
||||
FileEntry = entry;
|
||||
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
|
||||
if (PsoFile.IsPSO(ms))
|
||||
{
|
||||
Pso = new PsoFile();
|
||||
Pso.Load(ms);
|
||||
|
||||
//PsoTypes.EnsurePsoTypes(Pso);
|
||||
|
||||
var root = PsoTypes.GetRootEntry(Pso);
|
||||
if (root != null)
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,431 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class DlcContentFile
|
||||
{
|
||||
|
||||
public List<DlcContentDataFile> dataFiles { get; set; } = new List<DlcContentDataFile>();
|
||||
public List<DlcContentChangeSet> contentChangeSets { get; set; } = new List<DlcContentChangeSet>();
|
||||
|
||||
public RpfFile DlcFile { get; set; } //used by GameFileCache
|
||||
public Dictionary<string, DlcExtraFolderMountFile> ExtraMounts { get; set; } = new Dictionary<string, DlcExtraFolderMountFile>();
|
||||
public Dictionary<string, DlcContentDataFile> RpfDataFiles { get; set; } = new Dictionary<string, DlcContentDataFile>();
|
||||
|
||||
public DlcExtraTitleUpdateFile ExtraTitleUpdates { get; set; }
|
||||
|
||||
public void Load(XmlDocument doc)
|
||||
{
|
||||
|
||||
var root = doc.DocumentElement;
|
||||
|
||||
dataFiles.Clear();
|
||||
contentChangeSets.Clear();
|
||||
|
||||
foreach (XmlNode node in root.ChildNodes)
|
||||
{
|
||||
switch (node.Name)
|
||||
{
|
||||
case "disabledFiles":
|
||||
foreach (XmlNode disabledFile in node.ChildNodes)
|
||||
{ } //nothing to see here..
|
||||
break;
|
||||
case "includedXmlFiles":
|
||||
foreach (XmlNode includedXmlFile in node.ChildNodes)
|
||||
{ } //nothing to see here..
|
||||
break;
|
||||
case "includedDataFiles":
|
||||
foreach (XmlNode includedDataFile in node.ChildNodes)
|
||||
{ } //nothing to see here..
|
||||
break;
|
||||
case "dataFiles":
|
||||
foreach (XmlNode dataFile in node.ChildNodes)
|
||||
{
|
||||
if (dataFile.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
dataFiles.Add(new DlcContentDataFile(dataFile));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "contentChangeSets":
|
||||
foreach (XmlNode contentChangeSet in node.ChildNodes)
|
||||
{
|
||||
if (contentChangeSet.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
contentChangeSets.Add(new DlcContentChangeSet(contentChangeSet));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "patchFiles":
|
||||
foreach (XmlNode patchFile in node.ChildNodes)
|
||||
{ } //nothing to see here..
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void LoadDicts(DlcSetupFile setupfile, RpfManager rpfman, GameFileCache gfc)
|
||||
{
|
||||
ExtraMounts.Clear();
|
||||
RpfDataFiles.Clear();
|
||||
|
||||
foreach (var datafile in dataFiles)
|
||||
{
|
||||
string dfn = GameFileCache.GetDlcPlatformPath(datafile.filename).ToLowerInvariant();
|
||||
if (datafile.fileType == "EXTRA_FOLDER_MOUNT_DATA")
|
||||
{
|
||||
string efmdxmlpath = datafile.filename.Replace(setupfile.deviceName + ":", DlcFile.Path).Replace('/', '\\');
|
||||
efmdxmlpath = gfc.GetDlcPatchedPath(efmdxmlpath);
|
||||
XmlDocument efmdxml = rpfman.GetFileXml(efmdxmlpath);
|
||||
|
||||
DlcExtraFolderMountFile efmf = new DlcExtraFolderMountFile();
|
||||
efmf.Load(efmdxml);
|
||||
|
||||
ExtraMounts[dfn] = efmf;
|
||||
}
|
||||
if (datafile.fileType == "EXTRA_TITLE_UPDATE_DATA")
|
||||
{
|
||||
string etudxmlpath = datafile.filename.Replace(setupfile.deviceName + ":", DlcFile.Path).Replace('/', '\\');
|
||||
etudxmlpath = gfc.GetDlcPatchedPath(etudxmlpath);
|
||||
XmlDocument etudxml = rpfman.GetFileXml(etudxmlpath);
|
||||
|
||||
DlcExtraTitleUpdateFile etuf = new DlcExtraTitleUpdateFile();
|
||||
etuf.Load(etudxml);
|
||||
|
||||
ExtraTitleUpdates = etuf;
|
||||
}
|
||||
if (datafile.fileType == "RPF_FILE")
|
||||
{
|
||||
RpfDataFiles[dfn] = datafile;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return dataFiles.Count.ToString() + " dataFiles, " + contentChangeSets.Count.ToString() + " contentChangeSets";
|
||||
}
|
||||
}
|
||||
|
||||
public class DlcContentDataFile
|
||||
{
|
||||
public string filename { get; set; }
|
||||
public string fileType { get; set; }
|
||||
public string contents { get; set; }
|
||||
public string installPartition { get; set; }
|
||||
public bool overlay { get; set; }
|
||||
public bool disabled { get; set; }
|
||||
public bool persistent { get; set; }
|
||||
public bool loadCompletely { get; set; }
|
||||
public bool locked { get; set; }
|
||||
|
||||
public DlcContentDataFile(XmlNode node)
|
||||
{
|
||||
Load(node);
|
||||
}
|
||||
public void Load(XmlNode node)
|
||||
{
|
||||
foreach (XmlNode child in node.ChildNodes)
|
||||
{
|
||||
switch (child.Name)
|
||||
{
|
||||
case "filename":
|
||||
filename = child.InnerText;
|
||||
break;
|
||||
case "fileType":
|
||||
fileType = child.InnerText;
|
||||
break;
|
||||
case "contents":
|
||||
contents = child.InnerText;
|
||||
break;
|
||||
case "installPartition":
|
||||
installPartition = child.InnerText;
|
||||
break;
|
||||
case "overlay":
|
||||
overlay = Xml.GetBoolAttribute(child, "value");
|
||||
break;
|
||||
case "disabled":
|
||||
disabled = Xml.GetBoolAttribute(child, "value");
|
||||
break;
|
||||
case "persistent":
|
||||
persistent = Xml.GetBoolAttribute(child, "value");
|
||||
break;
|
||||
case "loadCompletely":
|
||||
loadCompletely = Xml.GetBoolAttribute(child, "value");
|
||||
break;
|
||||
case "locked":
|
||||
locked = Xml.GetBoolAttribute(child, "value");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return filename + ": " + fileType + ": " + contents + ": " + installPartition +
|
||||
(overlay ? ", overlay" : "") +
|
||||
(disabled ? ", disabled" : "") +
|
||||
(persistent ? ", persistent" : "") +
|
||||
(loadCompletely ? ", loadCompletely" : "") +
|
||||
(locked ? ", locked" : "");
|
||||
}
|
||||
}
|
||||
|
||||
public class DlcContentChangeSet
|
||||
{
|
||||
public string changeSetName { get; set; }
|
||||
public List<string> filesToInvalidate { get; set; }
|
||||
public List<string> filesToDisable { get; set; }
|
||||
public List<string> filesToEnable { get; set; }
|
||||
public List<string> txdToLoad { get; set; }
|
||||
public List<string> txdToUnload { get; set; }
|
||||
public List<string> residentResources { get; set; }
|
||||
public List<string> unregisterResources { get; set; }
|
||||
public List<DlcContentChangeSet> mapChangeSetData { get; set; }
|
||||
public string associatedMap { get; set; }
|
||||
public bool requiresLoadingScreen { get; set; }
|
||||
public string loadingScreenContext { get; set; }
|
||||
public bool useCacheLoader { get; set; }
|
||||
public DlcContentChangeSetExecutionConditions executionConditions { get; set; }
|
||||
|
||||
public DlcContentChangeSet(XmlNode node)
|
||||
{
|
||||
Load(node);
|
||||
}
|
||||
public void Load(XmlNode node)
|
||||
{
|
||||
foreach (XmlNode child in node.ChildNodes)
|
||||
{
|
||||
switch (child.Name)
|
||||
{
|
||||
case "changeSetName":
|
||||
changeSetName = child.InnerText;
|
||||
break;
|
||||
case "filesToInvalidate":
|
||||
filesToInvalidate = GetChildStringArray(child);
|
||||
if (filesToInvalidate != null)
|
||||
{ }
|
||||
break;
|
||||
case "filesToDisable":
|
||||
filesToDisable = GetChildStringArray(child);
|
||||
if (filesToDisable != null)
|
||||
{ }
|
||||
break;
|
||||
case "filesToEnable":
|
||||
filesToEnable = GetChildStringArray(child);
|
||||
if (filesToEnable != null)
|
||||
{ }
|
||||
break;
|
||||
case "txdToLoad":
|
||||
txdToLoad = GetChildStringArray(child);
|
||||
if (txdToLoad != null)
|
||||
{ }
|
||||
break;
|
||||
case "txdToUnload":
|
||||
txdToUnload = GetChildStringArray(child);
|
||||
if (txdToUnload != null)
|
||||
{ }
|
||||
break;
|
||||
case "residentResources":
|
||||
residentResources = GetChildStringArray(child);
|
||||
if (residentResources != null)
|
||||
{ }
|
||||
break;
|
||||
case "unregisterResources":
|
||||
unregisterResources = GetChildStringArray(child);
|
||||
if (unregisterResources != null)
|
||||
{ }
|
||||
break;
|
||||
case "mapChangeSetData":
|
||||
mapChangeSetData = new List<DlcContentChangeSet>();
|
||||
foreach (XmlNode mapChangeSetDataItem in child.ChildNodes)
|
||||
{
|
||||
mapChangeSetData.Add(new DlcContentChangeSet(mapChangeSetDataItem));
|
||||
}
|
||||
break;
|
||||
case "associatedMap":
|
||||
associatedMap = child.InnerText;
|
||||
break;
|
||||
case "requiresLoadingScreen":
|
||||
requiresLoadingScreen = Xml.GetBoolAttribute(child, "value");
|
||||
break;
|
||||
case "loadingScreenContext":
|
||||
loadingScreenContext = child.InnerText;
|
||||
break;
|
||||
case "useCacheLoader":
|
||||
useCacheLoader = Xml.GetBoolAttribute(child, "value");
|
||||
break;
|
||||
case "executionConditions":
|
||||
executionConditions = new DlcContentChangeSetExecutionConditions(child);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<string> GetChildStringArray(XmlNode node)
|
||||
{
|
||||
if (!node.HasChildNodes) return null;
|
||||
var result = new List<string>();
|
||||
foreach (XmlNode child in node.ChildNodes)
|
||||
{
|
||||
if (child.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
result.Add(child.InnerText);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (changeSetName != null) ? changeSetName : (associatedMap != null) ? associatedMap : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DlcContentChangeSetExecutionConditions
|
||||
{
|
||||
public string activeChangesetConditions { get; set; }
|
||||
public string genericConditions { get; set; }
|
||||
|
||||
public DlcContentChangeSetExecutionConditions(XmlNode node)
|
||||
{
|
||||
Load(node);
|
||||
}
|
||||
public void Load(XmlNode node)
|
||||
{
|
||||
foreach (XmlNode child in node.ChildNodes)
|
||||
{
|
||||
if (child.NodeType != XmlNodeType.Element) continue;
|
||||
switch (child.Name)
|
||||
{
|
||||
case "activeChangesetConditions":
|
||||
activeChangesetConditions = child.InnerText;
|
||||
break;
|
||||
case "genericConditions":
|
||||
genericConditions = child.InnerText;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (string.IsNullOrEmpty(activeChangesetConditions) ? "" : activeChangesetConditions + ", ") + (string.IsNullOrEmpty(genericConditions) ? "" : genericConditions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class DlcExtraFolderMountFile
|
||||
{
|
||||
public List<DlcExtraFolderMount> FolderMounts { get; set; } = new List<DlcExtraFolderMount>();
|
||||
|
||||
public void Load(XmlDocument doc)
|
||||
{
|
||||
var root = doc.DocumentElement;
|
||||
|
||||
XmlNodeList mountitems = doc.SelectNodes("SExtraFolderMountData/FolderMounts/Item");
|
||||
FolderMounts.Clear();
|
||||
for (int i = 0; i < mountitems.Count; i++)
|
||||
{
|
||||
var mount = new DlcExtraFolderMount();
|
||||
mount.Init(mountitems[i]);
|
||||
FolderMounts.Add(mount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(" + FolderMounts.Count.ToString() + " FolderMounts)";
|
||||
}
|
||||
}
|
||||
|
||||
public class DlcExtraFolderMount
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string platform { get; set; }
|
||||
public string path { get; set; }
|
||||
public string mountAs { get; set; }
|
||||
|
||||
public void Init(XmlNode node)
|
||||
{
|
||||
type = Xml.GetStringAttribute(node, "type");
|
||||
platform = Xml.GetStringAttribute(node, "platform");
|
||||
path = Xml.GetChildInnerText(node, "path");
|
||||
mountAs = Xml.GetChildInnerText(node, "mountAs");
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return type + ": " + path + " - " + mountAs + ((platform != null) ? (" (" + platform + ")") : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class DlcExtraTitleUpdateFile
|
||||
{
|
||||
public List<DlcExtraTitleUpdateMount> Mounts { get; set; } = new List<DlcExtraTitleUpdateMount>();
|
||||
|
||||
public void Load(XmlDocument doc)
|
||||
{
|
||||
var root = doc.DocumentElement;
|
||||
|
||||
XmlNodeList mountitems = doc.SelectNodes("SExtraTitleUpdateData/Mounts/Item");
|
||||
Mounts.Clear();
|
||||
for (int i = 0; i < mountitems.Count; i++)
|
||||
{
|
||||
var mount = new DlcExtraTitleUpdateMount();
|
||||
mount.Init(mountitems[i]);
|
||||
Mounts.Add(mount);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(" + Mounts.Count.ToString() + " Mounts)";
|
||||
}
|
||||
}
|
||||
|
||||
public class DlcExtraTitleUpdateMount
|
||||
{
|
||||
public string type { get; set; }
|
||||
public string deviceName { get; set; }
|
||||
public string path { get; set; }
|
||||
|
||||
public void Init(XmlNode node)
|
||||
{
|
||||
type = Xml.GetStringAttribute(node, "type");
|
||||
deviceName = Xml.GetChildInnerText(node, "deviceName");
|
||||
path = Xml.GetChildInnerText(node, "path");
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return type + ": " + deviceName + " - " + path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class DlcSetupFile
|
||||
{
|
||||
public string deviceName { get; set; }
|
||||
public string datFile { get; set; }
|
||||
public string nameHash { get; set; }
|
||||
public List<DlcSetupContentChangesetGroup> contentChangeSetGroups { get; set; }
|
||||
public string type { get; set; }
|
||||
public string timeStamp { get; set; }
|
||||
public int order { get; set; }
|
||||
public int minorOrder { get; set; }
|
||||
public int subPackCount { get; set; }
|
||||
public bool isLevelPack { get; set; }
|
||||
|
||||
public RpfFile DlcFile { get; set; } //used by GameFileCache
|
||||
public DlcContentFile ContentFile { get; set; }
|
||||
|
||||
public void Load(XmlDocument doc)
|
||||
{
|
||||
|
||||
var root = doc.DocumentElement;
|
||||
deviceName = Xml.GetChildInnerText(root, "deviceName");
|
||||
datFile = Xml.GetChildInnerText(root, "datFile");
|
||||
nameHash = Xml.GetChildInnerText(root, "nameHash");
|
||||
type = Xml.GetChildInnerText(root, "type");
|
||||
timeStamp = Xml.GetChildInnerText(root, "timeStamp");
|
||||
order = Xml.GetIntAttribute(root.SelectSingleNode("order"), "value");
|
||||
minorOrder = Xml.GetIntAttribute(root.SelectSingleNode("minorOrder"), "value");
|
||||
subPackCount = Xml.GetIntAttribute(root.SelectSingleNode("subPackCount"), "value");
|
||||
isLevelPack = Xml.GetBoolAttribute(root.SelectSingleNode("isLevelPack"), "value");
|
||||
|
||||
contentChangeSetGroups = new List<DlcSetupContentChangesetGroup>();
|
||||
var groups = root.SelectNodes("contentChangeSetGroups/Item");
|
||||
foreach (XmlNode node in groups)
|
||||
{
|
||||
var group = new DlcSetupContentChangesetGroup();
|
||||
group.Load(node);
|
||||
contentChangeSetGroups.Add(group);
|
||||
}
|
||||
|
||||
if (root.ChildNodes.Count > 15)
|
||||
{ }
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return deviceName + ", " + datFile + ", " + nameHash + ", " + type + ", " + order.ToString() + ", " + ((contentChangeSetGroups != null) ? contentChangeSetGroups.Count.ToString() : "0") + " groups, " + timeStamp;
|
||||
}
|
||||
}
|
||||
|
||||
public class DlcSetupContentChangesetGroup
|
||||
{
|
||||
public string NameHash { get; set; }
|
||||
public List<string> ContentChangeSets { get; set; }
|
||||
|
||||
public void Load(XmlNode node)
|
||||
{
|
||||
if (node.ChildNodes.Count != 2)
|
||||
{ }
|
||||
NameHash = Xml.GetChildInnerText(node, "NameHash");
|
||||
ContentChangeSets = new List<string>();
|
||||
var changesets = node.SelectNodes("ContentChangeSets/Item");
|
||||
foreach (XmlNode changeset in changesets)
|
||||
{
|
||||
ContentChangeSets.Add(changeset.InnerText);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return NameHash + " (" + ((ContentChangeSets != null) ? ContentChangeSets.Count.ToString() : "0") + " changesets)";
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,158 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class GtxdFile : GameFile, PackedFile
|
||||
{
|
||||
|
||||
public RbfFile Rbf { get; set; }
|
||||
|
||||
|
||||
public Dictionary<string, string> CMapParentTxds { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public GtxdFile() : base(null, GameFileType.Gtxd)
|
||||
{
|
||||
}
|
||||
public GtxdFile(RpfFileEntry entry) : base(entry, GameFileType.Gtxd)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
RpfFileEntry = entry;
|
||||
Name = entry.Name;
|
||||
FilePath = Name;
|
||||
|
||||
|
||||
if (entry.NameLower.EndsWith(".ymt"))
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
if (RbfFile.IsRBF(ms))
|
||||
{
|
||||
Rbf = new RbfFile();
|
||||
var rbfstruct = Rbf.Load(ms);
|
||||
|
||||
if (rbfstruct.Name == "CMapParentTxds")
|
||||
{
|
||||
LoadMapParentTxds(rbfstruct);
|
||||
}
|
||||
|
||||
Loaded = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
//not an RBF file...
|
||||
}
|
||||
}
|
||||
else if (entry.NameLower.EndsWith(".meta"))
|
||||
{
|
||||
string xml = Encoding.UTF8.GetString(data);
|
||||
LoadMapParentTxds(xml);
|
||||
Loaded = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void LoadMapParentTxds(RbfStructure rbfstruct)
|
||||
{
|
||||
|
||||
CMapParentTxds = new Dictionary<string, string>();
|
||||
//StringBuilder sblist = new StringBuilder();
|
||||
foreach (var child in rbfstruct.Children)
|
||||
{
|
||||
var childstruct = child as RbfStructure;
|
||||
if ((childstruct != null) && (childstruct.Name == "txdRelationships"))
|
||||
{
|
||||
foreach (var txdrel in childstruct.Children)
|
||||
{
|
||||
var txdrelstruct = txdrel as RbfStructure;
|
||||
if ((txdrelstruct != null) && (txdrelstruct.Name == "item"))
|
||||
{
|
||||
string parentstr = string.Empty;
|
||||
string childstr = string.Empty;
|
||||
foreach (var item in txdrelstruct.Children)
|
||||
{
|
||||
var itemstruct = item as RbfStructure;
|
||||
if ((itemstruct != null))
|
||||
{
|
||||
var strbytes = itemstruct.Children[0] as RbfBytes;
|
||||
string thisstr = string.Empty;
|
||||
if (strbytes != null)
|
||||
{
|
||||
thisstr = Encoding.ASCII.GetString(strbytes.Value).Replace("\0", "");
|
||||
}
|
||||
switch (item.Name)
|
||||
{
|
||||
case "parent":
|
||||
parentstr = thisstr;
|
||||
break;
|
||||
case "child":
|
||||
childstr = thisstr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
|
||||
{
|
||||
if (!CMapParentTxds.ContainsKey(childstr))
|
||||
{
|
||||
CMapParentTxds.Add(childstr, parentstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
//sblist.AppendLine(childstr + ": " + parentstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//string alltxdmap = sblist.ToString();
|
||||
//if (!string.IsNullOrEmpty(alltxdmap))
|
||||
//{
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void LoadMapParentTxds(string xml)
|
||||
{
|
||||
XmlDocument xmldoc = new XmlDocument();
|
||||
xmldoc.LoadXml(xml); //maybe better load xml.ToLower() and use "cmapparenttxds/txdrelationships/item" as xpath?
|
||||
XmlNodeList items = xmldoc.SelectNodes("CMapParentTxds/txdRelationships/Item | CMapParentTxds/txdRelationships/item");
|
||||
|
||||
CMapParentTxds = new Dictionary<string, string>();
|
||||
for (int i = 0; i < items.Count; i++)
|
||||
{
|
||||
string parentstr = Xml.GetChildInnerText(items[i], "parent");
|
||||
string childstr = Xml.GetChildInnerText(items[i], "child");
|
||||
|
||||
if ((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
|
||||
{
|
||||
if (!CMapParentTxds.ContainsKey(childstr))
|
||||
{
|
||||
CMapParentTxds.Add(childstr, parentstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class Gxt2File : PackedFile
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
public uint EntryCount { get; set; }
|
||||
public Gxt2Entry[] TextEntries { get; set; }
|
||||
//public Dictionary<uint, string> Dict { get; set; }
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
FileEntry = entry;
|
||||
//Dict = new Dictionary<uint, string>();
|
||||
|
||||
using (BinaryReader br = new BinaryReader(new MemoryStream(data)))
|
||||
{
|
||||
uint gxt2 = br.ReadUInt32(); //"GXT2" - 1196971058
|
||||
if (gxt2 != 1196971058)
|
||||
{ return; }
|
||||
|
||||
EntryCount = br.ReadUInt32();
|
||||
TextEntries = new Gxt2Entry[EntryCount];
|
||||
for (uint i = 0; i < EntryCount; i++)
|
||||
{
|
||||
var e = new Gxt2Entry();
|
||||
e.Hash = br.ReadUInt32();
|
||||
e.Offset = br.ReadUInt32();
|
||||
TextEntries[i] = e;
|
||||
}
|
||||
|
||||
gxt2 = br.ReadUInt32(); //another "GXT2"
|
||||
if (gxt2 != 1196971058)
|
||||
{ return; }
|
||||
|
||||
uint endpos = br.ReadUInt32();
|
||||
|
||||
List<byte> buf = new List<byte>();
|
||||
|
||||
for (uint i = 0; i < EntryCount; i++)
|
||||
{
|
||||
var e = TextEntries[i];
|
||||
br.BaseStream.Position = e.Offset;
|
||||
|
||||
buf.Clear();
|
||||
byte b = br.ReadByte();
|
||||
while ((b != 0) && (br.BaseStream.Position<endpos))
|
||||
{
|
||||
buf.Add(b);
|
||||
b = br.ReadByte();
|
||||
}
|
||||
e.Text = Encoding.UTF8.GetString(buf.ToArray());
|
||||
|
||||
//Dict[e.Hash] = e.Text;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class Gxt2Entry
|
||||
{
|
||||
public uint Hash { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
public string Text { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Convert.ToString(Hash, 16).ToUpper().PadLeft(8, '0') + ": " + Text;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static class GlobalText
|
||||
{
|
||||
public static Dictionary<uint, string> Index = new Dictionary<uint, string>();
|
||||
private static object syncRoot = new object();
|
||||
|
||||
public static volatile bool FullIndexBuilt = false;
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
Index.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Ensure(string str)
|
||||
{
|
||||
uint hash = JenkHash.GenHash(str);
|
||||
if (hash == 0) return true;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.ContainsKey(hash))
|
||||
{
|
||||
Index.Add(hash, str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Ensure(string str, uint hash)
|
||||
{
|
||||
if (hash == 0) return true;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.ContainsKey(hash))
|
||||
{
|
||||
Index.Add(hash, str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetString(uint hash)
|
||||
{
|
||||
string res;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.TryGetValue(hash, out res))
|
||||
{
|
||||
res = hash.ToString();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public static string TryGetString(uint hash)
|
||||
{
|
||||
string res;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.TryGetValue(hash, out res))
|
||||
{
|
||||
res = string.Empty;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class JPsoFile : PackedFile
|
||||
{
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
public PsoFile Pso { get; set; }
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
//MemoryStream ms = new MemoryStream(data);
|
||||
|
||||
FileEntry = entry;
|
||||
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
|
||||
if (PsoFile.IsPSO(ms))
|
||||
{
|
||||
Pso = new PsoFile();
|
||||
Pso.Load(ms);
|
||||
|
||||
//PsoTypes.EnsurePsoTypes(Pso);
|
||||
|
||||
var root = PsoTypes.GetRootEntry(Pso);
|
||||
if (root != null)
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
|
||||
public static class StatsNames
|
||||
{
|
||||
public static Dictionary<uint, string> Index = new Dictionary<uint, string>();
|
||||
private static object syncRoot = new object();
|
||||
|
||||
public static volatile bool FullIndexBuilt = false;
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
lock (syncRoot)
|
||||
{
|
||||
Index.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static bool Ensure(string str)
|
||||
{
|
||||
uint hash = JenkHash.GenHash(str);
|
||||
if (hash == 0) return true;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.ContainsKey(hash))
|
||||
{
|
||||
Index.Add(hash, str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool Ensure(string str, uint hash)
|
||||
{
|
||||
if (hash == 0) return true;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.ContainsKey(hash))
|
||||
{
|
||||
Index.Add(hash, str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetString(uint hash)
|
||||
{
|
||||
string res;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.TryGetValue(hash, out res))
|
||||
{
|
||||
res = hash.ToString();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public static string TryGetString(uint hash)
|
||||
{
|
||||
string res;
|
||||
lock (syncRoot)
|
||||
{
|
||||
if (!Index.TryGetValue(hash, out res))
|
||||
{
|
||||
res = string.Empty;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YbnFile : GameFile, PackedFile
|
||||
{
|
||||
public Bounds Bounds { get; set; }
|
||||
|
||||
public YbnFile() : base(null, GameFileType.Ybn)
|
||||
{
|
||||
}
|
||||
public YbnFile(RpfFileEntry entry) : base(entry, GameFileType.Ybn)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
|
||||
Bounds = rd.ReadBlock<Bounds>();
|
||||
|
||||
Bounds.OwnerName = entry.Name;
|
||||
|
||||
Loaded = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class YcdFile : GameFile, PackedFile
|
||||
{
|
||||
public ClipDictionary ClipDictionary { get; set; }
|
||||
|
||||
public Dictionary<MetaHash, ClipMapEntry> ClipMap { get; set; }
|
||||
public Dictionary<MetaHash, AnimationMapEntry> AnimMap { get; set; }
|
||||
|
||||
public ClipMapEntry[] ClipMapEntries { get; set; }
|
||||
public AnimationMapEntry[] AnimMapEntries { get; set; }
|
||||
|
||||
|
||||
public YcdFile() : base(null, GameFileType.Ycd)
|
||||
{
|
||||
}
|
||||
public YcdFile(RpfFileEntry entry) : base(entry, GameFileType.Ycd)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
//Hash = entry.ShortNameHash;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
|
||||
ClipDictionary = rd.ReadBlock<ClipDictionary>();
|
||||
|
||||
ClipMap = new Dictionary<MetaHash, ClipMapEntry>();
|
||||
AnimMap = new Dictionary<MetaHash, AnimationMapEntry>();
|
||||
if (ClipDictionary != null)
|
||||
{
|
||||
if ((ClipDictionary.Clips != null) && (ClipDictionary.Clips.data_items != null))
|
||||
{
|
||||
foreach (var cme in ClipDictionary.Clips.data_items)
|
||||
{
|
||||
if (cme != null)
|
||||
{
|
||||
ClipMap[cme.Hash] = cme;
|
||||
var nxt = cme.Next;
|
||||
while (nxt != null)
|
||||
{
|
||||
ClipMap[nxt.Hash] = nxt;
|
||||
nxt = nxt.Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((ClipDictionary.Animations != null) && (ClipDictionary.Animations.Animations != null) && (ClipDictionary.Animations.Animations.data_items != null))
|
||||
{
|
||||
foreach (var ame in ClipDictionary.Animations.Animations.data_items)
|
||||
{
|
||||
if (ame != null)
|
||||
{
|
||||
AnimMap[ame.Hash] = ame;
|
||||
var nxt = ame.NextEntry;
|
||||
while (nxt != null)
|
||||
{
|
||||
AnimMap[nxt.Hash] = nxt;
|
||||
nxt = nxt.NextEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var cme in ClipMap.Values)
|
||||
{
|
||||
var clip = cme.Clip;
|
||||
if (clip == null) continue;
|
||||
clip.Ycd = this;
|
||||
if (string.IsNullOrEmpty(clip.Name)) continue;
|
||||
string name = clip.Name.Replace('\\', '/');
|
||||
var slidx = name.LastIndexOf('/');
|
||||
if ((slidx >= 0) && (slidx < name.Length - 1))
|
||||
{
|
||||
name = name.Substring(slidx + 1);
|
||||
}
|
||||
var didx = name.LastIndexOf('.');
|
||||
if ((didx > 0) && (didx < name.Length))
|
||||
{
|
||||
name = name.Substring(0, didx);
|
||||
}
|
||||
clip.ShortName = name;
|
||||
name = name.ToLowerInvariant();
|
||||
JenkIndex.Ensure(name);
|
||||
|
||||
|
||||
//if (name.EndsWith("_uv_0")) //hash for these entries match string with this removed, +1
|
||||
//{
|
||||
//}
|
||||
//if (name.EndsWith("_uv_1")) //same as above, but +2
|
||||
//{
|
||||
//}
|
||||
|
||||
}
|
||||
foreach (var ame in AnimMap.Values)
|
||||
{
|
||||
var anim = ame.Animation;
|
||||
if (anim == null) continue;
|
||||
anim.Ycd = this;
|
||||
}
|
||||
|
||||
|
||||
ClipMapEntries = ClipMap.Values.ToArray();
|
||||
AnimMapEntries = AnimMap.Values.ToArray();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YddFile : GameFile, PackedFile
|
||||
{
|
||||
public DrawableDictionary DrawableDict { get; set; }
|
||||
|
||||
public Dictionary<uint, Drawable> Dict { get; set; }
|
||||
public Drawable[] Drawables { get; set; }
|
||||
|
||||
public YddFile() : base(null, GameFileType.Ydd)
|
||||
{
|
||||
}
|
||||
public YddFile(RpfFileEntry entry) : base(entry, GameFileType.Ydd)
|
||||
{
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
DrawableDict = rd.ReadBlock<DrawableDictionary>();
|
||||
|
||||
//MemoryUsage = 0; //uses decompressed filesize now...
|
||||
//if (DrawableDict != null)
|
||||
//{
|
||||
// MemoryUsage += DrawableDict.MemoryUsage;
|
||||
//}
|
||||
|
||||
if ((DrawableDict != null) &&
|
||||
(DrawableDict.Drawables != null) &&
|
||||
(DrawableDict.Drawables.data_items != null) &&
|
||||
(DrawableDict.Hashes != null))
|
||||
{
|
||||
Dict = new Dictionary<uint, Drawable>();
|
||||
var drawables = DrawableDict.Drawables.data_items;
|
||||
var hashes = DrawableDict.Hashes;
|
||||
for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
|
||||
{
|
||||
var drawable = drawables[i];
|
||||
var hash = hashes[i];
|
||||
Dict[hash] = drawable;
|
||||
drawable.Owner = this;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
|
||||
{
|
||||
var drawable = drawables[i];
|
||||
var hash = hashes[i];
|
||||
if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
|
||||
{
|
||||
string hstr = JenkIndex.TryGetString(hash);
|
||||
if (!string.IsNullOrEmpty(hstr))
|
||||
{
|
||||
drawable.Name = hstr;
|
||||
}
|
||||
else
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
Drawables = Dict.Values.ToArray();
|
||||
|
||||
}
|
||||
|
||||
Loaded = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YdrFile : GameFile, PackedFile
|
||||
{
|
||||
public Drawable Drawable { get; set; }
|
||||
|
||||
public YdrFile() : base(null, GameFileType.Ydr)
|
||||
{
|
||||
}
|
||||
public YdrFile(RpfFileEntry entry) : base(entry, GameFileType.Ydr)
|
||||
{
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
//MemoryUsage = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Drawable = rd.ReadBlock<Drawable>();
|
||||
Drawable.Owner = this;
|
||||
//MemoryUsage += Drawable.MemoryUsage; //uses decompressed filesize now...
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string err = ex.ToString();
|
||||
}
|
||||
|
||||
Loaded = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YftFile : GameFile, PackedFile
|
||||
{
|
||||
public FragType Fragment { get; set; }
|
||||
|
||||
public YftFile() : base(null, GameFileType.Yft)
|
||||
{
|
||||
}
|
||||
public YftFile(RpfFileEntry entry) : base(entry, GameFileType.Yft)
|
||||
{
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
Fragment = rd.ReadBlock<FragType>();
|
||||
|
||||
if (Fragment.Drawable != null)
|
||||
{
|
||||
Fragment.Drawable.Owner = this;
|
||||
}
|
||||
if (Fragment.Unknown_F8h_Data != null)
|
||||
{
|
||||
Fragment.Unknown_F8h_Data.Owner = this;
|
||||
}
|
||||
|
||||
Loaded = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,187 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YmfFile : PackedFile
|
||||
{
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
|
||||
public Meta Meta { get; set; }
|
||||
public PsoFile Pso { get; set; }
|
||||
public RbfFile Rbf { get; set; }
|
||||
|
||||
public YmfMapDataGroup[] MapDataGroups { get; set; }
|
||||
public CImapDependency[] imapDependencies { get; set; }
|
||||
public YmfImapDependency2[] imapDependencies2 { get; set; }
|
||||
public YmfItypDependency2[] itypDependencies2 { get; set; }
|
||||
public CHDTxdAssetBinding[] HDTxdAssetBindings { get; set; }
|
||||
public YmfInterior[] Interiors { get; set; }
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
FileEntry = entry;
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
if (RbfFile.IsRBF(ms))
|
||||
{
|
||||
Rbf = new RbfFile();
|
||||
Rbf.Load(ms);
|
||||
|
||||
//x64j.rpf\\levels\\gta5\\_citye\\indust_01\\id1_props.rpf\\_manifest.ymf
|
||||
//x64j.rpf\\levels\\gta5\\_citye\\indust_02\\id2_props.rpf\\_manifest.ymf
|
||||
//x64q.rpf\\levels\\gta5\\_hills\\country_01\\cs1_railwyc.rpf\\_manifest.ymf
|
||||
//all just HDTxd bindings
|
||||
|
||||
return;
|
||||
}
|
||||
if (PsoFile.IsPSO(ms))
|
||||
{
|
||||
Pso = new PsoFile();
|
||||
Pso.Load(ms);
|
||||
|
||||
//PsoTypes.EnsurePsoTypes(Pso);
|
||||
|
||||
ProcessPSO();
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
Meta = rd.ReadBlock<Meta>();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void ProcessPSO()
|
||||
{
|
||||
|
||||
//See x64m.rpf\levels\gta5\_cityw\venice_01\venice_metadata.rpf\_manifest.ymf
|
||||
//for TIMED YMAP stuff!!!!
|
||||
//check CMapDataGroup.HoursOnOff
|
||||
|
||||
|
||||
var d = PsoTypes.GetRootItem<CPackFileMetaData>(Pso);
|
||||
|
||||
MapDataGroups = PsoTypes.GetObjectArray<YmfMapDataGroup, CMapDataGroup>(Pso, d.MapDataGroups);
|
||||
|
||||
imapDependencies = PsoTypes.GetItemArray<CImapDependency>(Pso, d.imapDependencies);
|
||||
|
||||
imapDependencies2 = PsoTypes.GetObjectArray<YmfImapDependency2, CImapDependencies>(Pso, d.imapDependencies_2);
|
||||
|
||||
itypDependencies2 = PsoTypes.GetObjectArray<YmfItypDependency2, CItypDependencies>(Pso, d.itypDependencies_2);
|
||||
|
||||
HDTxdAssetBindings = PsoTypes.GetItemArray<CHDTxdAssetBinding>(Pso, d.HDTxdBindingArray);
|
||||
|
||||
Interiors = PsoTypes.GetObjectArray<YmfInterior, Unk_741495440>(Pso, d.Interiors);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (FileEntry != null) ? FileEntry.Path : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmfMapDataGroup : PsoClass<CMapDataGroup>
|
||||
{
|
||||
public CMapDataGroup DataGroup { get; set; } //ymap name
|
||||
public MetaHash[] Bounds { get; set; }
|
||||
public MetaHash[] WeatherTypes { get; set; }
|
||||
public MetaHash Name { get; set; }
|
||||
public ushort Flags { get; set; }
|
||||
public uint HoursOnOff { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return DataGroup.ToString();
|
||||
}
|
||||
|
||||
public override void Init(PsoFile pso, ref CMapDataGroup v)
|
||||
{
|
||||
DataGroup = v;
|
||||
Bounds = PsoTypes.GetHashArray(pso, v.Bounds);
|
||||
WeatherTypes = PsoTypes.GetHashArray(pso, v.WeatherTypes);
|
||||
Name = v.Name;
|
||||
Flags = v.Flags;
|
||||
HoursOnOff = v.HoursOnOff;
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmfImapDependency2 : PsoClass<CImapDependencies>
|
||||
{
|
||||
public CImapDependencies Dep { get; set; }
|
||||
public MetaHash[] itypDepArray { get; set; }//ybn hashes?
|
||||
|
||||
public override void Init(PsoFile pso, ref CImapDependencies v)
|
||||
{
|
||||
Dep = v;
|
||||
itypDepArray = PsoTypes.GetHashArray(pso, v.itypDepArray);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Dep.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmfItypDependency2 : PsoClass<CItypDependencies>
|
||||
{
|
||||
public CItypDependencies Dep { get; set; }
|
||||
public MetaHash[] itypDepArray { get; set; }//ytyp hashes?
|
||||
|
||||
public override void Init(PsoFile pso, ref CItypDependencies v)
|
||||
{
|
||||
Dep = v;
|
||||
itypDepArray = PsoTypes.GetHashArray(pso, v.itypDepArray);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Dep.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmfInterior : PsoClass<Unk_741495440>
|
||||
{
|
||||
public Unk_741495440 Interior { get; set; }
|
||||
public MetaHash[] Bounds { get; set; }//ybn hashes?
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Interior.ToString();
|
||||
}
|
||||
|
||||
public override void Init(PsoFile pso, ref Unk_741495440 v)
|
||||
{
|
||||
Interior = v;
|
||||
Bounds = PsoTypes.GetHashArray(pso, v.Bounds);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
using CodeWalker.World;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmtFile : GameFile, PackedFile
|
||||
{
|
||||
|
||||
public Meta Meta { get; set; }
|
||||
public PsoFile Pso { get; set; }
|
||||
public RbfFile Rbf { get; set; }
|
||||
|
||||
public YmtFileFormat FileFormat { get; set; } = YmtFileFormat.Unknown;
|
||||
public YmtFileContentType ContentType { get; set; } = YmtFileContentType.None;
|
||||
|
||||
|
||||
public Dictionary<string,string> CMapParentTxds { get; set; }
|
||||
|
||||
public YmtScenarioPointManifest CScenarioPointManifest { get; set; }
|
||||
|
||||
public MCScenarioPointRegion CScenarioPointRegion { get; set; }
|
||||
public ScenarioRegion ScenarioRegion { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
//fields used by the editor:
|
||||
public bool HasChanged { get; set; } = false;
|
||||
public List<string> SaveWarnings = null;
|
||||
|
||||
public YmtFile() : base(null, GameFileType.Ymt)
|
||||
{
|
||||
}
|
||||
public YmtFile(RpfFileEntry entry) : base(entry, GameFileType.Ymt)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void LoadRSC(byte[] data)
|
||||
{
|
||||
//direct load from a raw, compressed ymt resource file (openIV-compatible format)
|
||||
|
||||
RpfResourceFileEntry resentry = new RpfResourceFileEntry();
|
||||
|
||||
//hopefully this format has an RSC7 header...
|
||||
uint rsc7 = BitConverter.ToUInt32(data, 0);
|
||||
if (rsc7 == 0x37435352) //RSC7 header present!
|
||||
{
|
||||
int version = BitConverter.ToInt32(data, 4);
|
||||
resentry.SystemFlags = BitConverter.ToUInt32(data, 8);
|
||||
resentry.GraphicsFlags = BitConverter.ToUInt32(data, 12);
|
||||
if (data.Length > 16)
|
||||
{
|
||||
int newlen = data.Length - 16; //trim the header from the data passed to the next step.
|
||||
byte[] newdata = new byte[newlen];
|
||||
Buffer.BlockCopy(data, 16, newdata, 0, newlen);
|
||||
data = newdata;
|
||||
}
|
||||
else
|
||||
{
|
||||
data = null; //shouldn't happen... empty..
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//direct load from file without the rpf header..
|
||||
//assume it's in resource meta format
|
||||
resentry.SystemFlags = RpfResourceFileEntry.GetFlagsFromSize(data.Length, 0);
|
||||
resentry.GraphicsFlags = RpfResourceFileEntry.GetFlagsFromSize(0, 2); //graphics type 2 for ymt/meta
|
||||
}
|
||||
|
||||
var oldresentry = RpfFileEntry as RpfResourceFileEntry;
|
||||
if (oldresentry != null) //update the existing entry with the new one
|
||||
{
|
||||
oldresentry.SystemFlags = resentry.SystemFlags;
|
||||
oldresentry.GraphicsFlags = resentry.GraphicsFlags;
|
||||
resentry.Name = oldresentry.Name;
|
||||
resentry.NameHash = oldresentry.NameHash;
|
||||
resentry.NameLower = oldresentry.NameLower;
|
||||
resentry.ShortNameHash = oldresentry.ShortNameHash;
|
||||
}
|
||||
else
|
||||
{
|
||||
RpfFileEntry = resentry; //just stick it in there for later...
|
||||
}
|
||||
|
||||
data = ResourceBuilder.Decompress(data);
|
||||
|
||||
|
||||
Load(data, resentry);
|
||||
|
||||
//Loaded = true;
|
||||
}
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
RpfFileEntry = entry;
|
||||
Name = entry.Name;
|
||||
FilePath = Name;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry != null)
|
||||
{
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
Meta = rd.ReadBlock<Meta>();
|
||||
|
||||
var rootblock = Meta.GetRootBlock();
|
||||
if (rootblock != null)
|
||||
{
|
||||
if (rootblock.StructureNameHash == MetaName.CScenarioPointRegion)
|
||||
{
|
||||
LoadScenarioPointRegion(Meta, rootblock);
|
||||
}
|
||||
}
|
||||
|
||||
Loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
|
||||
if (RbfFile.IsRBF(ms))
|
||||
{
|
||||
Rbf = new RbfFile();
|
||||
var rbfstruct = Rbf.Load(ms);
|
||||
|
||||
if (rbfstruct.Name == "CMapParentTxds")
|
||||
{
|
||||
LoadMapParentTxds(rbfstruct);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loaded = true;
|
||||
return;
|
||||
}
|
||||
if (PsoFile.IsPSO(ms))
|
||||
{
|
||||
Pso = new PsoFile();
|
||||
Pso.Load(ms);
|
||||
|
||||
//PsoTypes.EnsurePsoTypes(Pso);
|
||||
|
||||
var root = PsoTypes.GetRootEntry(Pso);
|
||||
if (root != null)
|
||||
{
|
||||
if (root.NameHash == MetaName.CScenarioPointManifest)
|
||||
{
|
||||
LoadScenarioPointManifest(Pso);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loaded = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void LoadMapParentTxds(RbfStructure rbfstruct)
|
||||
{
|
||||
FileFormat = YmtFileFormat.RBF;
|
||||
ContentType = YmtFileContentType.MapParentTxds;
|
||||
|
||||
CMapParentTxds = new Dictionary<string, string>();
|
||||
//StringBuilder sblist = new StringBuilder();
|
||||
foreach(var child in rbfstruct.Children)
|
||||
{
|
||||
var childstruct = child as RbfStructure;
|
||||
if ((childstruct != null) && (childstruct.Name == "txdRelationships"))
|
||||
{
|
||||
foreach (var txdrel in childstruct.Children)
|
||||
{
|
||||
var txdrelstruct = txdrel as RbfStructure;
|
||||
if ((txdrelstruct != null) && (txdrelstruct.Name == "item"))
|
||||
{
|
||||
string parentstr = string.Empty;
|
||||
string childstr = string.Empty;
|
||||
foreach(var item in txdrelstruct.Children)
|
||||
{
|
||||
var itemstruct = item as RbfStructure;
|
||||
if ((itemstruct != null))
|
||||
{
|
||||
var strbytes = itemstruct.Children[0] as RbfBytes;
|
||||
string thisstr = string.Empty;
|
||||
if (strbytes != null)
|
||||
{
|
||||
thisstr = Encoding.ASCII.GetString(strbytes.Value).Replace("\0", "");
|
||||
}
|
||||
switch (item.Name)
|
||||
{
|
||||
case "parent":
|
||||
parentstr = thisstr;
|
||||
break;
|
||||
case "child":
|
||||
childstr = thisstr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if((!string.IsNullOrEmpty(parentstr)) && (!string.IsNullOrEmpty(childstr)))
|
||||
{
|
||||
if (!CMapParentTxds.ContainsKey(childstr))
|
||||
{
|
||||
CMapParentTxds.Add(childstr, parentstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
//sblist.AppendLine(childstr + ": " + parentstr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//string alltxdmap = sblist.ToString();
|
||||
//if (!string.IsNullOrEmpty(alltxdmap))
|
||||
//{
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
private void LoadScenarioPointManifest(PsoFile pso)
|
||||
{
|
||||
FileFormat = YmtFileFormat.PSO;
|
||||
ContentType = YmtFileContentType.ScenarioPointManifest;
|
||||
|
||||
CScenarioPointManifest = new YmtScenarioPointManifest();
|
||||
CScenarioPointManifest.Load(pso);
|
||||
|
||||
}
|
||||
|
||||
private void LoadScenarioPointRegion(Meta meta, MetaDataBlock rootblock)
|
||||
{
|
||||
FileFormat = YmtFileFormat.RSC;
|
||||
ContentType = YmtFileContentType.ScenarioPointRegion;
|
||||
|
||||
var cdata = MetaTypes.ConvertData<CScenarioPointRegion>(rootblock.Data);
|
||||
|
||||
CScenarioPointRegion = new MCScenarioPointRegion();
|
||||
CScenarioPointRegion.Ymt = this;
|
||||
CScenarioPointRegion.Load(meta, cdata);
|
||||
|
||||
|
||||
ScenarioRegion = new ScenarioRegion();
|
||||
ScenarioRegion.Load(this);
|
||||
|
||||
//string stypes = MetaTypes.GetTypesInitString(meta);
|
||||
//if (!string.IsNullOrEmpty(stypes))
|
||||
//{ }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public byte[] Save()
|
||||
{
|
||||
|
||||
switch (ContentType)
|
||||
{
|
||||
case YmtFileContentType.MapParentTxds: return SaveMapParentTxds();
|
||||
case YmtFileContentType.ScenarioPointManifest: return SaveScenarioPointManifest();
|
||||
case YmtFileContentType.ScenarioPointRegion: return SaveScenarioPointRegion();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
private byte[] SaveMapParentTxds()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] SaveScenarioPointManifest()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
private byte[] SaveScenarioPointRegion()
|
||||
{
|
||||
if (ScenarioRegion != null)
|
||||
{
|
||||
return ScenarioRegion.Save();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private void LogSaveWarning(string w)
|
||||
{
|
||||
if (SaveWarnings == null) SaveWarnings = new List<string>();
|
||||
SaveWarnings.Add(w);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return RpfFileEntry.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum YmtFileFormat
|
||||
{
|
||||
Unknown = 0,
|
||||
RSC = 1,
|
||||
PSO = 2,
|
||||
RBF = 3,
|
||||
}
|
||||
public enum YmtFileContentType
|
||||
{
|
||||
None = 0,
|
||||
MapParentTxds = 1,
|
||||
ScenarioPointManifest = 2,
|
||||
ScenarioPointRegion = 3,
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YmtScenarioPointManifest
|
||||
{
|
||||
public CScenarioPointManifest _Data;
|
||||
public CScenarioPointManifest Data { get { return _Data; } set { _Data = value; } }
|
||||
|
||||
public CScenarioPointRegionDef[] RegionDefs { get; set; }
|
||||
public CScenarioPointGroup[] Groups { get; set; }
|
||||
public MetaHash[] InteriorNames { get; set; }
|
||||
|
||||
|
||||
public void Load(PsoFile pso)
|
||||
{
|
||||
Data = PsoTypes.GetRootItem<CScenarioPointManifest>(pso);
|
||||
RegionDefs = PsoTypes.ConvertDataArray<CScenarioPointRegionDef>(pso, _Data.RegionDefs);
|
||||
Groups = PsoTypes.ConvertDataArray<CScenarioPointGroup>(pso, _Data.Groups);
|
||||
InteriorNames = PsoTypes.GetHashArray(pso, _Data.InteriorNames);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,405 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using SharpDX;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YnvFile : GameFile, PackedFile, BasePathData
|
||||
{
|
||||
public NavMesh Nav { get; set; }
|
||||
|
||||
public List<Vector3> Vertices { get; set; }
|
||||
public List<ushort> Indices { get; set; }
|
||||
public List<YnvPoly> Polys { get; set; }
|
||||
|
||||
|
||||
public EditorVertex[] TriangleVerts { get; set; }
|
||||
public Vector4[] NodePositions { get; set; }
|
||||
|
||||
|
||||
//fields used by the editor:
|
||||
public bool HasChanged { get; set; } = false;
|
||||
public List<string> SaveWarnings = null;
|
||||
|
||||
|
||||
|
||||
public int AreaID
|
||||
{
|
||||
get
|
||||
{
|
||||
return (int)(Nav?.AreaID ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public YnvFile() : base(null, GameFileType.Ynv)
|
||||
{
|
||||
}
|
||||
public YnvFile(RpfFileEntry entry) : base(entry, GameFileType.Ynv)
|
||||
{
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
|
||||
Nav = rd.ReadBlock<NavMesh>();
|
||||
|
||||
|
||||
if ((Nav != null) && (Nav.SectorTree != null))
|
||||
{
|
||||
if (Nav.Vertices != null)
|
||||
{
|
||||
Vector3 posoffset = Nav.SectorTree.AABBMin.XYZ();
|
||||
Vector3 aabbsize = Nav.AABBSize;
|
||||
|
||||
var verts = Nav.Vertices.GetFullList();
|
||||
Vertices = new List<Vector3>(verts.Count);
|
||||
for (int i = 0; i < verts.Count; i++)
|
||||
{
|
||||
var ov = verts[i].ToVector3();
|
||||
Vertices.Add(posoffset + ov * aabbsize);
|
||||
}
|
||||
}
|
||||
if (Nav.Indices != null)
|
||||
{
|
||||
Indices = Nav.Indices.GetFullList();
|
||||
}
|
||||
if (Nav.Polys != null)
|
||||
{
|
||||
var polys = Nav.Polys.GetFullList();
|
||||
Polys = new List<YnvPoly>(polys.Count);
|
||||
for (int i = 0; i < polys.Count; i++)
|
||||
{
|
||||
YnvPoly poly = new YnvPoly();
|
||||
poly.Init(this, polys[i]);
|
||||
poly.Index = i;
|
||||
Polys.Add(poly);
|
||||
|
||||
|
||||
//calc poly center.
|
||||
if ((Indices == null) || (Vertices == null))
|
||||
{ continue; }
|
||||
var vc = Vertices.Count;
|
||||
var ic = poly._RawData.IndexCount;
|
||||
var startid = poly._RawData.IndexID;
|
||||
var endid = startid + ic;
|
||||
if (startid >= Indices.Count)
|
||||
{ continue; }
|
||||
if (endid > Indices.Count)
|
||||
{ continue; }
|
||||
Vector3 pcenter = Vector3.Zero;
|
||||
float pcount = 0.0f;
|
||||
for (int id = startid; id < endid; id++)
|
||||
{
|
||||
var ind = Indices[id];
|
||||
if(ind>=vc)
|
||||
{ continue; }
|
||||
|
||||
pcenter += Vertices[ind];
|
||||
pcount += 1.0f;
|
||||
}
|
||||
poly.Position = pcenter * (1.0f / pcount);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
UpdateAllNodePositions();
|
||||
|
||||
UpdateTriangleVertices();
|
||||
|
||||
|
||||
Loaded = true;
|
||||
LoadQueued = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public bool RemovePoly(YnvPoly poly)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public void UpdateAllNodePositions()
|
||||
{
|
||||
if (Nav == null) return;
|
||||
if (Nav.Portals == null) return;
|
||||
|
||||
int cnt = Nav.Portals?.Length ?? 0;
|
||||
if (cnt <= 0)
|
||||
{
|
||||
NodePositions = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 posoffset = Nav.SectorTree.AABBMin.XYZ();
|
||||
Vector3 aabbsize = Nav.AABBSize;
|
||||
|
||||
var np = new Vector4[cnt];
|
||||
for (int i = 0; i < cnt; i++)
|
||||
{
|
||||
var portal = Nav.Portals[i];
|
||||
var pv = portal.Position1.ToVector3();
|
||||
//var pv = portal.Position2.ToVector3();
|
||||
np[i] = new Vector4(posoffset + pv * aabbsize, 1.0f);
|
||||
}
|
||||
NodePositions = np;
|
||||
}
|
||||
|
||||
public void UpdateTriangleVertices()
|
||||
{
|
||||
if (Nav == null) return;
|
||||
if (Nav.Polys == null) return;
|
||||
if (Nav.Vertices == null) return;
|
||||
|
||||
//need position and colour for each vertex.
|
||||
//render as a triangle list... (no indices needed)
|
||||
|
||||
//go through the nav mesh polys and generate verts to render...
|
||||
|
||||
if ((Vertices == null) || (Vertices.Count == 0)) return;
|
||||
if ((Indices == null) || (Indices.Count == 0)) return;
|
||||
if ((Polys == null) || (Polys.Count == 0)) return;
|
||||
|
||||
|
||||
int vc = Vertices.Count;
|
||||
|
||||
List<EditorVertex> rverts = new List<EditorVertex>();
|
||||
foreach (var ypoly in Polys)
|
||||
{
|
||||
var poly = ypoly.RawData;
|
||||
var colour = ypoly.GetColour();
|
||||
var colourval = (uint)colour.ToRgba();
|
||||
|
||||
var ic = poly.IndexCount;
|
||||
var startid = poly.IndexID;
|
||||
var endid = startid + ic;
|
||||
if (startid >= Indices.Count)
|
||||
{ continue; }
|
||||
if (endid > Indices.Count)
|
||||
{ continue; }
|
||||
|
||||
|
||||
if(ic<3)
|
||||
{ continue; }//not enough verts to make a triangle...
|
||||
|
||||
if (ic > 15)
|
||||
{ }
|
||||
|
||||
|
||||
EditorVertex p0 = new EditorVertex();
|
||||
EditorVertex p1 = new EditorVertex();
|
||||
EditorVertex p2 = new EditorVertex();
|
||||
p0.Colour = colourval;
|
||||
p1.Colour = colourval;
|
||||
p2.Colour = colourval;
|
||||
|
||||
var startind = Indices[startid];
|
||||
if (startind >= vc)
|
||||
{ continue; }
|
||||
|
||||
p0.Position = Vertices[startind];
|
||||
|
||||
//build triangles for the poly.
|
||||
int tricount = ic - 2;
|
||||
for (int t = 0; t < tricount; t++)
|
||||
{
|
||||
int tid = startid + t;
|
||||
int ind1 = Indices[tid + 1];
|
||||
int ind2 = Indices[tid + 2];
|
||||
if ((ind1 >= vc) || (ind2 >= vc))
|
||||
{ continue; }
|
||||
|
||||
p1.Position = Vertices[ind1];
|
||||
p2.Position = Vertices[ind2];
|
||||
|
||||
rverts.Add(p0);
|
||||
rverts.Add(p1);
|
||||
rverts.Add(p2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TriangleVerts = rverts.ToArray();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public EditorVertex[] GetPathVertices()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public EditorVertex[] GetTriangleVertices()
|
||||
{
|
||||
return TriangleVerts;
|
||||
}
|
||||
public Vector4[] GetNodePositions()
|
||||
{
|
||||
return NodePositions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class YnvPoly
|
||||
{
|
||||
public NavMeshPoly _RawData;
|
||||
|
||||
public YnvFile Ynv { get; set; }
|
||||
public NavMeshPoly RawData { get { return _RawData; } set { _RawData = value; } }
|
||||
|
||||
public ushort AreaID { get { return _RawData.AreaID; } }
|
||||
public bool B00_AvoidUnk { get { return (_RawData.Unknown_00h & 1) > 0; } }
|
||||
public bool B01_AvoidUnk { get { return (_RawData.Unknown_00h & 2) > 0; } }
|
||||
public bool B02_IsFootpath { get { return (_RawData.Unknown_00h & 4) > 0; } }
|
||||
public bool B03_IsUnderground { get { return (_RawData.Unknown_00h & 8) > 0; } }
|
||||
//public bool B04_Unused { get { return (_RawData.Unknown_00h & 16) > 0; } }
|
||||
//public bool B05_Unused { get { return (_RawData.Unknown_00h & 32) > 0; } }
|
||||
public bool B06_SteepSlope { get { return (_RawData.Unknown_00h & 64) > 0; } }
|
||||
public bool B07_IsWater { get { return (_RawData.Unknown_00h & 128) > 0; } }
|
||||
public bool B08_UndergroundUnk1 { get { return (_RawData.Unknown_24h.Value & 1) > 0; } }
|
||||
public bool B09_UndergroundUnk2 { get { return (_RawData.Unknown_24h.Value & 2) > 0; } }
|
||||
public bool B10_UndergroundUnk3 { get { return (_RawData.Unknown_24h.Value & 4) > 0; } }
|
||||
public bool B11_UndergroundUnk4 { get { return (_RawData.Unknown_24h.Value & 8) > 0; } }
|
||||
//public bool B12_Unused { get { return (_RawData.Unknown_24h.Value & 16) > 0; } }
|
||||
public bool B13_HasPathNode { get { return (_RawData.Unknown_24h.Value & 32) > 0; } }
|
||||
public bool B14_IsInterior { get { return (_RawData.Unknown_24h.Value & 64) > 0; } }
|
||||
public bool B15_InteractionUnk { get { return (_RawData.Unknown_24h.Value & 128) > 0; } }
|
||||
//public bool B16_Unused { get { return (_RawData.Unknown_24h.Value & 256) > 0; } }
|
||||
public bool B17_IsFlatGround { get { return (_RawData.Unknown_24h.Value & 512) > 0; } }
|
||||
public bool B18_IsRoad { get { return (_RawData.Unknown_24h.Value & 1024) > 0; } }
|
||||
public bool B19_IsCellEdge { get { return (_RawData.Unknown_24h.Value & 2048) > 0; } }
|
||||
public bool B20_IsTrainTrack { get { return (_RawData.Unknown_24h.Value & 4096) > 0; } }
|
||||
public bool B21_IsShallowWater { get { return (_RawData.Unknown_24h.Value & 8192) > 0; } }
|
||||
public bool B22_FootpathUnk1 { get { return (_RawData.Unknown_24h.Value & 16384) > 0; } }
|
||||
public bool B23_FootpathUnk2 { get { return (_RawData.Unknown_24h.Value & 32768) > 0; } }
|
||||
public bool B24_FootpathMall { get { return (_RawData.Unknown_24h.Value & 65536) > 0; } }
|
||||
public bool B25_SlopeSouth { get { return (_RawData.Unknown_28h.Value & 65536) > 0; } }
|
||||
public bool B26_SlopeSouthEast { get { return (_RawData.Unknown_28h.Value & 131072) > 0; } }
|
||||
public bool B27_SlopeEast { get { return (_RawData.Unknown_28h.Value & 262144) > 0; } }
|
||||
public bool B28_SlopeNorthEast { get { return (_RawData.Unknown_28h.Value & 524288) > 0; } }
|
||||
public bool B29_SlopeNorth { get { return (_RawData.Unknown_28h.Value & 1048576) > 0; } }
|
||||
public bool B30_SlopeNorthWest { get { return (_RawData.Unknown_28h.Value & 2097152) > 0; } }
|
||||
public bool B31_SlopeWest { get { return (_RawData.Unknown_28h.Value & 4194304) > 0; } }
|
||||
public bool B32_SlopeSouthWest { get { return (_RawData.Unknown_28h.Value & 8388608) > 0; } }
|
||||
public bool B33_PortalUnk1 { get { return (_RawData.PartUnk2 & 1) > 0; } }
|
||||
public bool B34_PortalUnk2 { get { return (_RawData.PartUnk2 & 2) > 0; } }
|
||||
public bool B35_PortalUnk3 { get { return (_RawData.PartUnk2 & 4) > 0; } }
|
||||
public bool B36_PortalUnk4 { get { return (_RawData.PartUnk2 & 8) > 0; } }
|
||||
public byte HeuristicXUnk { get { return (byte)_RawData.Unknown_28h_8a; } }
|
||||
public byte HeuristicYUnk { get { return (byte)_RawData.Unknown_28h_8b; } }
|
||||
|
||||
|
||||
public Vector3 Position { get; set; }
|
||||
public int Index { get; set; }
|
||||
|
||||
|
||||
public void Init(YnvFile ynv, NavMeshPoly poly)
|
||||
{
|
||||
Ynv = ynv;
|
||||
RawData = poly;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public Color4 GetColour()
|
||||
{
|
||||
var colour = new Color4();
|
||||
var u0 = _RawData.Unknown_00h;
|
||||
if ((u0 & 1) > 0) colour.Red += 0.01f;//avoid? loiter?
|
||||
if ((u0 & 2) > 0) colour.Red += 0.01f; //avoid?
|
||||
if ((u0 & 4) > 0) colour.Green += 0.25f; //ped/footpath
|
||||
if ((u0 & 8) > 0) colour.Green += 0.02f; //underground?
|
||||
////if ((u0 & 16) > 0) colour.Red += 1.0f; //not used?
|
||||
////if ((u0 & 32) > 0) colour.Green += 1.0f;//not used?
|
||||
if ((u0 & 64) > 0) colour.Red += 0.25f; //steep slope
|
||||
if ((u0 & 128) > 0) colour.Blue += 0.25f; //water
|
||||
|
||||
var u2 = _RawData.Unknown_24h.Value;
|
||||
//colour.Green = (u2 & 15) / 15.0f; //maybe underground amount..?
|
||||
//if ((u2 & 1) > 0) colour.Blue += 1.0f; //peds interact with something? underground?
|
||||
//if ((u2 & 2) > 0) colour.Green += 1.0f;//underneath something?
|
||||
//if ((u2 & 4) > 0) colour.Red += 0.5f;//peds interact with something..? underground?
|
||||
//if ((u2 & 8) > 0) colour.Red += 0.5f; //underground?
|
||||
//if ((u2 & 16) > 0) colour.Red += 1.0f; //not used..
|
||||
//if ((u2 & 32) > 0) colour.Green += 1.0f;//use path node?
|
||||
if ((u2 & 64) > 0) colour.Blue += 0.1f; //is interior?
|
||||
//if ((u2 & 128) > 0) colour.Red += 1.0f; //interacting areas? veg branches, roofs, vents, worker areas?
|
||||
//if ((u2 & 256) > 0) colour.Green += 1.0f; //not used?
|
||||
if ((u2 & 512) > 0) colour.Green += 0.1f;//is flat ground? ped-navigable?
|
||||
if ((u2 & 1024) > 0) colour.Blue += 0.03f;//is a road
|
||||
//if ((u2 & 2048) > 0) colour.Green += 1.0f; //poly is on a cell edge
|
||||
if ((u2 & 4096) > 0) colour.Green += 0.75f; //is a train track
|
||||
if ((u2 & 8192) > 0) colour.Blue += 0.75f;//shallow water/moving water
|
||||
if ((u2 & 16384) > 0) colour.Red += 0.2f; //footpaths/beach - peds walking?
|
||||
if ((u2 & 32768) > 0) colour.Blue += 0.2f; //footpaths - special?
|
||||
if ((u2 & 65536) > 0) colour.Green = 0.2f;//footpaths - mall areas? eg mall, vinewood blvd
|
||||
//if (u2 >= 131072) { }//other bits unused
|
||||
|
||||
var u5 = _RawData.Unknown_28h.Value; //32 bits
|
||||
//colour.Red = poly.Unknown_28h_8a / 255.0f; //heuristic vals..?
|
||||
//colour.Green = poly.Unknown_28h_8b / 255.0f; //heuristic vals..?
|
||||
//if ((u5 & 65536) > 0) colour.Red += 1.0f; //slope facing -Y (south)
|
||||
//if ((u5 & 131072) > 0) colour.Blue += 1.0f; //slope facing +X,-Y (southeast)
|
||||
//if ((u5 & 262144) > 0) colour.Green += 1.0f; //slope facing +X (east)
|
||||
//if ((u5 & 524288) > 0) colour.Red += 1.0f; //slope facing +X,+Y (northeast)
|
||||
//if ((u5 & 1048576) > 0) colour.Green += 1.0f; //slope facing +Y (north)
|
||||
//if ((u5 & 2097152) > 0) colour.Blue += 1.0f; //slope facing -X,+Y (northwest)
|
||||
//if ((u5 & 4194304) > 0) colour.Green += 1.0f; //slope facing -X (west)
|
||||
//if ((u5 & 8388608) > 0) colour.Red += 1.0f; //slope facing -X,-Y (southwest)
|
||||
//if (u5 >= 16777216) { } //other bits unused
|
||||
|
||||
var u1 = _RawData.PartUnk2;
|
||||
//if ((u1 & 1) > 0) colour.Red += 1.0f; //portal - don't interact?
|
||||
//if ((u1 & 2) > 0) colour.Green += 1.0f; //portal - ladder/fence interaction?
|
||||
//if ((u1 & 4) > 0) colour.Blue += 1.0f; //portal - fence interaction / go away from?
|
||||
//if ((u1 & 8) > 0) colour.Red += 1.0f;//something file-specific? portal index related?
|
||||
|
||||
|
||||
|
||||
colour.Alpha = 0.75f;
|
||||
|
||||
return colour;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AreaID.ToString() + ", " + Index.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YptFile : GameFile, PackedFile
|
||||
{
|
||||
public ParticleEffectsList PtfxList { get; set; }
|
||||
|
||||
public Dictionary<uint, Drawable> DrawableDict { get; set; }
|
||||
|
||||
public string ErrorMessage { get; set; }
|
||||
|
||||
|
||||
public YptFile() : base(null, GameFileType.Ypt)
|
||||
{
|
||||
}
|
||||
public YptFile(RpfFileEntry entry) : base(entry, GameFileType.Ypt)
|
||||
{
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
//MemoryUsage = 0;
|
||||
|
||||
try
|
||||
{
|
||||
PtfxList = rd.ReadBlock<ParticleEffectsList>();
|
||||
//Drawable.Owner = this;
|
||||
//MemoryUsage += Drawable.MemoryUsage; //uses decompressed filesize now...
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ErrorMessage = ex.ToString();
|
||||
}
|
||||
|
||||
|
||||
var dDict = PtfxList?.DrawableDictionary;
|
||||
|
||||
if ((dDict != null) &&
|
||||
(dDict.Drawables != null) &&
|
||||
(dDict.Drawables.data_items != null) &&
|
||||
(dDict.Hashes != null))
|
||||
{
|
||||
DrawableDict = new Dictionary<uint, Drawable>();
|
||||
var drawables = dDict.Drawables.data_items;
|
||||
var hashes = dDict.Hashes;
|
||||
for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
|
||||
{
|
||||
var drawable = drawables[i];
|
||||
var hash = hashes[i];
|
||||
DrawableDict[hash] = drawable;
|
||||
drawable.Owner = this;
|
||||
}
|
||||
|
||||
for (int i = 0; (i < drawables.Length) && (i < hashes.Length); i++)
|
||||
{
|
||||
var drawable = drawables[i];
|
||||
var hash = hashes[i];
|
||||
if ((drawable.Name == null) || (drawable.Name.EndsWith("#dd")))
|
||||
{
|
||||
string hstr = JenkIndex.TryGetString(hash);
|
||||
if (!string.IsNullOrEmpty(hstr))
|
||||
{
|
||||
drawable.Name = hstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
drawable.Name = "0x" + hash.ToString("X").PadLeft(8, '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loaded = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YtdFile : GameFile, PackedFile
|
||||
{
|
||||
public TextureDictionary TextureDict { get; set; }
|
||||
|
||||
|
||||
public YtdFile() : base(null, GameFileType.Ytd)
|
||||
{
|
||||
}
|
||||
public YtdFile(RpfFileEntry entry) : base(entry, GameFileType.Ytd)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
|
||||
TextureDict = rd.ReadBlock<TextureDictionary>();
|
||||
|
||||
//MemoryUsage = 0; //uses decompressed file size now..
|
||||
//if (TextureDict != null)
|
||||
//{
|
||||
// MemoryUsage += TextureDict.MemoryUsage;
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class YtypFile : PackedFile
|
||||
{
|
||||
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
|
||||
public Meta Meta { get; set; }
|
||||
public PsoFile Pso { get; set; }
|
||||
public RbfFile Rbf { get; set; }
|
||||
|
||||
|
||||
public uint NameHash { get; set; }
|
||||
public string[] Strings { get; set; }
|
||||
|
||||
|
||||
public CMapTypes CMapTypes { get; set; }
|
||||
|
||||
public Archetype[] AllArchetypes { get; set; }
|
||||
|
||||
public MetaWrapper[] Extensions { get; set; }
|
||||
|
||||
public CCompositeEntityType[] CompositeEntityTypes { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (FileEntry != null) ? FileEntry.Name : string.Empty;
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
FileEntry = entry;
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
if (RbfFile.IsRBF(ms))
|
||||
{
|
||||
Rbf = new RbfFile();
|
||||
Rbf.Load(ms);
|
||||
}
|
||||
else if (PsoFile.IsPSO(ms))
|
||||
{
|
||||
Pso = new PsoFile();
|
||||
Pso.Load(ms);
|
||||
//PsoTypes.EnsurePsoTypes(Pso);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
Meta = rd.ReadBlock<Meta>();
|
||||
|
||||
|
||||
CMapTypes = MetaTypes.GetTypedData<CMapTypes>(Meta, MetaName.CMapTypes);
|
||||
|
||||
|
||||
List<Archetype> allarchs = new List<Archetype>();
|
||||
|
||||
var ptrs = MetaTypes.GetPointerArray(Meta, CMapTypes.archetypes);
|
||||
if (ptrs != null)
|
||||
{
|
||||
for (int i = 0; i < ptrs.Length; i++)
|
||||
{
|
||||
var ptr = ptrs[i];
|
||||
var offset = ptr.Offset;
|
||||
var block = Meta.GetBlock(ptr.BlockID);
|
||||
if (block == null)
|
||||
{ continue; }
|
||||
if ((offset < 0) || (block.Data == null) || (offset >= block.Data.Length))
|
||||
{ continue; }
|
||||
|
||||
Archetype a = null;
|
||||
switch (block.StructureNameHash)
|
||||
{
|
||||
case MetaName.CBaseArchetypeDef:
|
||||
var basearch = PsoTypes.ConvertDataRaw<CBaseArchetypeDef>(block.Data, offset);
|
||||
a = new Archetype();
|
||||
a.Init(this, ref basearch);
|
||||
a.Extensions = MetaTypes.GetExtensions(Meta, basearch.extensions);
|
||||
break;
|
||||
case MetaName.CTimeArchetypeDef:
|
||||
var timearch = PsoTypes.ConvertDataRaw<CTimeArchetypeDef>(block.Data, offset);
|
||||
var ta = new TimeArchetype();
|
||||
ta.Init(this, ref timearch);
|
||||
ta.Extensions = MetaTypes.GetExtensions(Meta, timearch._BaseArchetypeDef.extensions);
|
||||
a = ta;
|
||||
break;
|
||||
case MetaName.CMloArchetypeDef:
|
||||
var mloarch = PsoTypes.ConvertDataRaw<CMloArchetypeDef>(block.Data, offset);
|
||||
var ma = new MloArchetype();
|
||||
ma.Init(this, ref mloarch);
|
||||
ma.Extensions = MetaTypes.GetExtensions(Meta, mloarch._BaseArchetypeDef.extensions);
|
||||
|
||||
ma.LoadChildren(Meta);
|
||||
|
||||
a = ma;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (a != null)
|
||||
{
|
||||
allarchs.Add(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
AllArchetypes = allarchs.ToArray();
|
||||
|
||||
|
||||
Extensions = MetaTypes.GetExtensions(Meta, CMapTypes.extensions);
|
||||
if (Extensions != null)
|
||||
{ }
|
||||
|
||||
|
||||
//AudioEmitters = MetaTypes.GetTypedDataArray<CExtensionDefAudioEmitter>(Meta, MetaName.CExtensionDefAudioEmitter);
|
||||
//if (AudioEmitters != null)
|
||||
//{ }
|
||||
|
||||
//CEntityDefs = MetaTypes.GetTypedDataArray<CEntityDef>(Meta, MetaName.CEntityDef);
|
||||
|
||||
|
||||
CompositeEntityTypes = MetaTypes.ConvertDataArray<CCompositeEntityType>(Meta, MetaName.CCompositeEntityType, CMapTypes.compositeEntityTypes);
|
||||
if (CompositeEntityTypes != null)
|
||||
{ }
|
||||
|
||||
NameHash = CMapTypes.name;
|
||||
if (NameHash == 0)
|
||||
{
|
||||
int ind = entry.NameLower.LastIndexOf('.');
|
||||
if (ind > 0)
|
||||
{
|
||||
NameHash = JenkHash.GenHash(entry.NameLower.Substring(0, ind));
|
||||
}
|
||||
else
|
||||
{
|
||||
NameHash = JenkHash.GenHash(entry.NameLower);
|
||||
}
|
||||
}
|
||||
|
||||
Strings = MetaTypes.GetStrings(Meta);
|
||||
if (Strings != null)
|
||||
{
|
||||
foreach (string str in Strings)
|
||||
{
|
||||
JenkIndex.Ensure(str); //just shove them in there
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//foreach (var block in Meta.DataBlocks)
|
||||
//{
|
||||
// switch(block.StructureNameHash)
|
||||
// {
|
||||
// case MetaName.CMapTypes:
|
||||
// case MetaName.CTimeArchetypeDef:
|
||||
// case MetaName.CBaseArchetypeDef:
|
||||
// case MetaName.CMloArchetypeDef:
|
||||
// case MetaName.CMloTimeCycleModifier:
|
||||
// case MetaName.CMloRoomDef:
|
||||
// case MetaName.CMloPortalDef:
|
||||
// case MetaName.CMloEntitySet:
|
||||
// case MetaName.CEntityDef:
|
||||
// case MetaName.CExtensionDefParticleEffect:
|
||||
// case MetaName.CExtensionDefAudioCollisionSettings:
|
||||
// case MetaName.CExtensionDefSpawnPoint:
|
||||
// case MetaName.CExtensionDefSpawnPointOverride:
|
||||
// case MetaName.CExtensionDefExplosionEffect:
|
||||
// case MetaName.CExtensionDefAudioEmitter:
|
||||
// case MetaName.CExtensionDefLadder:
|
||||
// case MetaName.CExtensionDefBuoyancy:
|
||||
// case MetaName.CExtensionDefExpression:
|
||||
// case MetaName.CExtensionDefLightShaft:
|
||||
// case MetaName.CExtensionDefLightEffect:
|
||||
// case MetaName.CExtensionDefDoor:
|
||||
// case MetaName.CExtensionDefWindDisturbance:
|
||||
// case MetaName.CExtensionDefProcObject:
|
||||
// case MetaName.CLightAttrDef:
|
||||
// case MetaName.STRING:
|
||||
// case MetaName.POINTER:
|
||||
// case MetaName.UINT:
|
||||
// case MetaName.VECTOR4:
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
//MetaTypes.ParseMetaData(Meta);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YvrFile : GameFile, PackedFile
|
||||
{
|
||||
public VehicleRecordList Records { get; set; }
|
||||
|
||||
public YvrFile() : base(null, GameFileType.Yvr)
|
||||
{
|
||||
}
|
||||
public YvrFile(RpfFileEntry entry) : base(entry, GameFileType.Yvr)
|
||||
{
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
//MemoryUsage = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Records = rd.ReadBlock<VehicleRecordList>();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string err = ex.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loaded = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YwrFile : GameFile, PackedFile
|
||||
{
|
||||
public WaypointRecordList Waypoints { get; set; }
|
||||
|
||||
public YwrFile() : base(null, GameFileType.Ywr)
|
||||
{
|
||||
}
|
||||
public YwrFile(RpfFileEntry entry) : base(entry, GameFileType.Ywr)
|
||||
{
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
Name = entry.Name;
|
||||
RpfFileEntry = entry;
|
||||
|
||||
|
||||
RpfResourceFileEntry resentry = entry as RpfResourceFileEntry;
|
||||
if (resentry == null)
|
||||
{
|
||||
throw new Exception("File entry wasn't a resource! (is it binary data?)");
|
||||
}
|
||||
|
||||
ResourceDataReader rd = new ResourceDataReader(resentry, data);
|
||||
|
||||
//MemoryUsage = 0;
|
||||
|
||||
try
|
||||
{
|
||||
Waypoints = rd.ReadBlock<WaypointRecordList>();
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
string err = ex.ToString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loaded = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public abstract class GameFile : Cacheable<GameFileCacheKey>
|
||||
{
|
||||
public volatile bool Loaded = false;
|
||||
public volatile bool LoadQueued = false;
|
||||
public RpfFileEntry RpfFileEntry { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string FilePath { get; set; } //used by the project form.
|
||||
public GameFileType Type { get; set; }
|
||||
|
||||
|
||||
|
||||
public GameFile(RpfFileEntry entry, GameFileType type)
|
||||
{
|
||||
RpfFileEntry = entry;
|
||||
Type = type;
|
||||
MemoryUsage = (entry != null) ? entry.GetFileSize() : 0;
|
||||
if (entry is RpfResourceFileEntry)
|
||||
{
|
||||
var resent = entry as RpfResourceFileEntry;
|
||||
var newuse = resent.SystemSize + resent.GraphicsSize;
|
||||
MemoryUsage = newuse;
|
||||
}
|
||||
else if (entry is RpfBinaryFileEntry)
|
||||
{
|
||||
var binent = entry as RpfBinaryFileEntry;
|
||||
var newuse = binent.FileUncompressedSize;
|
||||
if (newuse > MemoryUsage)
|
||||
{
|
||||
MemoryUsage = newuse;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return (string.IsNullOrEmpty(Name)) ? JenkIndex.GetString(Key.Hash) : Name;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public enum GameFileType : int
|
||||
{
|
||||
Ydd = 0,
|
||||
Ydr = 1,
|
||||
Yft = 2,
|
||||
Ymap = 3,
|
||||
Ymf = 4,
|
||||
Ymt = 5,
|
||||
Ytd = 6,
|
||||
Ytyp = 7,
|
||||
Ybn = 8,
|
||||
Ycd = 9,
|
||||
Ypt = 10,
|
||||
Ynd = 11,
|
||||
Ynv = 12,
|
||||
Rel = 13,
|
||||
Ywr = 14,
|
||||
Yvr = 15,
|
||||
Gtxd = 16,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public struct GameFileCacheKey
|
||||
{
|
||||
public uint Hash { get; set; }
|
||||
public GameFileType Type { get; set; }
|
||||
|
||||
public GameFileCacheKey(uint hash, GameFileType type)
|
||||
{
|
||||
Hash = hash;
|
||||
Type = type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,301 @@
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class Archetype
|
||||
{
|
||||
public virtual MetaName Type => MetaName.CBaseArchetypeDef;
|
||||
|
||||
public CBaseArchetypeDef _BaseArchetypeDef;
|
||||
public CBaseArchetypeDef BaseArchetypeDef { get { return _BaseArchetypeDef; } set { _BaseArchetypeDef = value; } }
|
||||
|
||||
public MetaHash Hash { get; set; }
|
||||
public YtypFile Ytyp { get; set; }
|
||||
public MetaHash DrawableDict { get; set; }
|
||||
public MetaHash TextureDict { get; set; }
|
||||
public MetaHash ClipDict { get; set; }
|
||||
public Vector3 BBMin { get; set; }
|
||||
public Vector3 BBMax { get; set; }
|
||||
public Vector3 BSCenter { get; set; }
|
||||
public float BSRadius { get; set; }
|
||||
public float LodDist { get; set; }
|
||||
public MetaWrapper[] Extensions { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BaseArchetypeDef.name.ToString();
|
||||
}
|
||||
}
|
||||
public string AssetName
|
||||
{
|
||||
get
|
||||
{
|
||||
return _BaseArchetypeDef.assetName.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void InitVars(ref CBaseArchetypeDef arch)
|
||||
{
|
||||
BaseArchetypeDef = arch;
|
||||
Hash = arch.assetName;
|
||||
if (Hash.Hash == 0) Hash = arch.name;
|
||||
DrawableDict = arch.drawableDictionary;
|
||||
TextureDict = arch.textureDictionary;
|
||||
ClipDict = arch.clipDictionary;
|
||||
BBMin = arch.bbMin;
|
||||
BBMax = arch.bbMax;
|
||||
BSCenter = arch.bsCentre;
|
||||
BSRadius = arch.bsRadius;
|
||||
LodDist = arch.lodDist;
|
||||
}
|
||||
|
||||
public void Init(YtypFile ytyp, ref CBaseArchetypeDef arch)
|
||||
{
|
||||
Ytyp = ytyp;
|
||||
InitVars(ref arch);
|
||||
}
|
||||
|
||||
public virtual bool IsActive(float hour)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return _BaseArchetypeDef.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class TimeArchetype : Archetype
|
||||
{
|
||||
public override MetaName Type => MetaName.CTimeArchetypeDef;
|
||||
|
||||
public CTimeArchetypeDefData _TimeArchetypeDef;
|
||||
public CTimeArchetypeDefData TimeArchetypeDef { get { return _TimeArchetypeDef; } set { _TimeArchetypeDef = value; } }
|
||||
|
||||
|
||||
public uint TimeFlags { get; set; }
|
||||
public bool[] ActiveHours { get; set; }
|
||||
public string[] ActiveHoursText { get; set; }
|
||||
public bool ExtraFlag { get; set; }
|
||||
|
||||
|
||||
public void Init(YtypFile ytyp, ref CTimeArchetypeDef arch)
|
||||
{
|
||||
Ytyp = ytyp;
|
||||
InitVars(ref arch._BaseArchetypeDef);
|
||||
TimeArchetypeDef = arch.TimeArchetypeDef;
|
||||
|
||||
TimeFlags = _TimeArchetypeDef.timeFlags;
|
||||
ActiveHours = new bool[24];
|
||||
ActiveHoursText = new string[24];
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
bool v = ((TimeFlags >> i) & 1) == 1;
|
||||
ActiveHours[i] = v;
|
||||
|
||||
int nxth = (i < 23) ? (i + 1) : 0;
|
||||
string hrs = string.Format("{0:00}:00 - {1:00}:00", i, nxth);
|
||||
ActiveHoursText[i] = (hrs + (v ? " - On" : " - Off"));
|
||||
}
|
||||
ExtraFlag = ((TimeFlags >> 24) & 1) == 1;
|
||||
}
|
||||
|
||||
public override bool IsActive(float hour)
|
||||
{
|
||||
if (ActiveHours == null) return true;
|
||||
int h = ((int)hour) % 24;
|
||||
if ((h < 0) || (h > 23)) return true;
|
||||
return ActiveHours[h];
|
||||
}
|
||||
}
|
||||
|
||||
public class MloArchetype : Archetype
|
||||
{
|
||||
public override MetaName Type => MetaName.CMloArchetypeDef;
|
||||
|
||||
public CMloArchetypeDefData _MloArchetypeDef;
|
||||
public CMloArchetypeDefData MloArchetypeDef { get { return _MloArchetypeDef; } set { _MloArchetypeDef = value; } }
|
||||
|
||||
public MCEntityDef[] entities { get; set; }
|
||||
public MCMloRoomDef[] rooms { get; set; }
|
||||
public MCMloPortalDef[] portals { get; set; }
|
||||
public MCMloEntitySet[] entitySets { get; set; }
|
||||
public CMloTimeCycleModifier[] timeCycleModifiers { get; set; }
|
||||
|
||||
public void Init(YtypFile ytyp, ref CMloArchetypeDef arch)
|
||||
{
|
||||
Ytyp = ytyp;
|
||||
InitVars(ref arch._BaseArchetypeDef);
|
||||
MloArchetypeDef = arch.MloArchetypeDef;
|
||||
}
|
||||
|
||||
public void LoadChildren(Meta meta)
|
||||
{
|
||||
var centities = MetaTypes.ConvertDataArray<CEntityDef>(meta, MetaName.CEntityDef, _MloArchetypeDef.entities);
|
||||
if (centities != null)
|
||||
{
|
||||
entities = new MCEntityDef[centities.Length];
|
||||
for (int i = 0; i < centities.Length; i++)
|
||||
{
|
||||
entities[i] = new MCEntityDef(meta, centities[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var crooms = MetaTypes.ConvertDataArray<CMloRoomDef>(meta, MetaName.CMloRoomDef, _MloArchetypeDef.rooms);
|
||||
if (crooms != null)
|
||||
{
|
||||
rooms = new MCMloRoomDef[crooms.Length];
|
||||
for (int i = 0; i < crooms.Length; i++)
|
||||
{
|
||||
rooms[i] = new MCMloRoomDef(meta, crooms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var cportals = MetaTypes.ConvertDataArray<CMloPortalDef>(meta, MetaName.CMloPortalDef, _MloArchetypeDef.portals);
|
||||
if (cportals != null)
|
||||
{
|
||||
portals = new MCMloPortalDef[cportals.Length];
|
||||
for (int i = 0; i < cportals.Length; i++)
|
||||
{
|
||||
portals[i] = new MCMloPortalDef(meta, cportals[i]);
|
||||
}
|
||||
}
|
||||
|
||||
var centitySets = MetaTypes.ConvertDataArray<CMloEntitySet>(meta, MetaName.CMloEntitySet, _MloArchetypeDef.entitySets);
|
||||
if (centitySets != null)
|
||||
{
|
||||
entitySets = new MCMloEntitySet[centitySets.Length];
|
||||
for (int i = 0; i < centitySets.Length; i++)
|
||||
{
|
||||
entitySets[i] = new MCMloEntitySet(meta, centitySets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
timeCycleModifiers = MetaTypes.ConvertDataArray<CMloTimeCycleModifier>(meta, MetaName.CMloTimeCycleModifier, _MloArchetypeDef.timeCycleModifiers);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class MloInstanceData
|
||||
{
|
||||
public YmapEntityDef Owner { get; set; }
|
||||
public CMloInstanceDef _Instance;
|
||||
public CMloInstanceDef Instance { get { return _Instance; } set { _Instance = value; } }
|
||||
public uint[] defaultEntitySets { get; set; }
|
||||
|
||||
public YmapEntityDef[] Entities { get; set; }
|
||||
|
||||
|
||||
public void CreateYmapEntities(YmapEntityDef owner, MloArchetype mloa)
|
||||
{
|
||||
Owner = owner;
|
||||
if (owner == null) return;
|
||||
if (mloa.entities == null) return;
|
||||
var ec = mloa.entities.Length;
|
||||
|
||||
var entlist = new List<YmapEntityDef>();
|
||||
for (int i = 0; i < ec; i++)
|
||||
{
|
||||
YmapEntityDef e = CreateYmapEntity(owner, mloa.entities[i], i);
|
||||
entlist.Add(e);
|
||||
}
|
||||
|
||||
int lasti = ec;
|
||||
|
||||
var entitySets = mloa.entitySets;
|
||||
if (entitySets != null)
|
||||
{
|
||||
for (int i = 0; i < entitySets.Length; i++)
|
||||
{
|
||||
var entitySet = entitySets[i];
|
||||
if (entitySet.Entities != null)
|
||||
{
|
||||
for (int j = 0; j < entitySet.Entities.Length; j++)
|
||||
{
|
||||
YmapEntityDef e = CreateYmapEntity(owner, entitySet.Entities[j], lasti);
|
||||
e.MloEntitySet = entitySet;
|
||||
entlist.Add(e);
|
||||
lasti++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (defaultEntitySets != null)
|
||||
{
|
||||
}
|
||||
|
||||
Entities = entlist.ToArray();
|
||||
}
|
||||
|
||||
private YmapEntityDef CreateYmapEntity(YmapEntityDef owner, MCEntityDef ment, int i)
|
||||
{
|
||||
YmapEntityDef e = new YmapEntityDef(null, i, ref ment._Data);
|
||||
e.Extensions = ment.Extensions;
|
||||
e.MloRefPosition = e.Position;
|
||||
e.MloRefOrientation = e.Orientation;
|
||||
e.MloParent = owner;
|
||||
e.Position = owner.Position + owner.Orientation.Multiply(e.MloRefPosition);
|
||||
e.Orientation = Quaternion.Multiply(owner.Orientation, e.MloRefOrientation);
|
||||
e.UpdateWidgetPosition();
|
||||
e.UpdateWidgetOrientation();
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
public void SetPosition(Vector3 pos)
|
||||
{
|
||||
var cent = _Instance.CEntityDef;
|
||||
cent.position = pos;
|
||||
_Instance.CEntityDef = cent; //TODO: maybe find a better way of doing this...
|
||||
}
|
||||
|
||||
public void SetOrientation(Quaternion ori)
|
||||
{
|
||||
var cent = _Instance.CEntityDef;
|
||||
cent.rotation = new Vector4(ori.X, ori.Y, ori.Z, ori.W); //mlo instances have oppposite orientations to normal entities...
|
||||
_Instance.CEntityDef = cent; //TODO: maybe find a better way of doing this...
|
||||
}
|
||||
|
||||
public void UpdateEntities()
|
||||
{
|
||||
if (Entities == null) return;
|
||||
if (Owner == null) return;
|
||||
|
||||
for (int i = 0; i < Entities.Length; i++)
|
||||
{
|
||||
YmapEntityDef e = Entities[i];
|
||||
e.Position = Owner.Position + Owner.Orientation.Multiply(e.MloRefPosition);
|
||||
e.Orientation = Quaternion.Multiply(Owner.Orientation, e.MloRefOrientation);
|
||||
e.UpdateWidgetPosition();
|
||||
e.UpdateWidgetOrientation();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,446 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class MetaBuilder
|
||||
{
|
||||
|
||||
List<MetaBuilderBlock> Blocks = new List<MetaBuilderBlock>();
|
||||
|
||||
int MaxBlockLength = 0x4000; //TODO: figure what this should be!
|
||||
|
||||
|
||||
public MetaBuilderBlock EnsureBlock(MetaName type)
|
||||
{
|
||||
foreach (var block in Blocks)
|
||||
{
|
||||
if (block.StructureNameHash == type)
|
||||
{
|
||||
if (block.TotalSize < MaxBlockLength)
|
||||
{
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
MetaBuilderBlock b = new MetaBuilderBlock();
|
||||
b.StructureNameHash = type;
|
||||
b.Index = Blocks.Count;
|
||||
Blocks.Add(b);
|
||||
return b;
|
||||
}
|
||||
|
||||
public MetaBuilderPointer AddItem<T>(MetaName type, T item) where T : struct
|
||||
{
|
||||
byte[] data = MetaTypes.ConvertToBytes(item);
|
||||
return AddItem(type, data);
|
||||
}
|
||||
|
||||
public MetaBuilderPointer AddItem(MetaName type, byte[] data)
|
||||
{
|
||||
MetaBuilderBlock block = EnsureBlock(type);
|
||||
int brem = data.Length % 16;
|
||||
if (brem > 0)
|
||||
{
|
||||
int newlen = data.Length - brem + 16;
|
||||
byte[] newdata = new byte[newlen];
|
||||
Buffer.BlockCopy(data, 0, newdata, 0, data.Length);
|
||||
data = newdata; //make sure item size is multiple of 16... so pointers don't need sub offsets!
|
||||
}
|
||||
int idx = block.AddItem(data);
|
||||
MetaBuilderPointer r = new MetaBuilderPointer();
|
||||
r.BlockID = block.Index + 1;
|
||||
r.Offset = (idx * data.Length);
|
||||
r.Length = data.Length;
|
||||
return r;
|
||||
}
|
||||
|
||||
public MetaBuilderPointer AddItemArray<T>(MetaName type, T[] items) where T : struct
|
||||
{
|
||||
byte[] data = MetaTypes.ConvertArrayToBytes(items);
|
||||
return AddItemArray(type, data, items.Length);
|
||||
}
|
||||
|
||||
public MetaBuilderPointer AddItemArray(MetaName type, byte[] data, int length)
|
||||
{
|
||||
MetaBuilderBlock block = EnsureBlock(type);
|
||||
int datalen = data.Length;
|
||||
int newlen = datalen;
|
||||
int lenrem = newlen % 16;
|
||||
if (lenrem != 0)
|
||||
{
|
||||
newlen += (16 - lenrem);
|
||||
}
|
||||
byte[] newdata = new byte[newlen];
|
||||
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
|
||||
int offs = block.TotalSize;
|
||||
int idx = block.AddItem(newdata);
|
||||
MetaBuilderPointer r = new MetaBuilderPointer();
|
||||
r.BlockID = block.Index + 1;
|
||||
r.Offset = offs; //(idx * data.Length);;
|
||||
r.Length = length;
|
||||
return r;
|
||||
}
|
||||
|
||||
public MetaBuilderPointer AddString(string str)
|
||||
{
|
||||
MetaBuilderBlock block = EnsureBlock(MetaName.STRING);
|
||||
byte[] data = Encoding.ASCII.GetBytes(str);
|
||||
int datalen = data.Length;
|
||||
int newlen = datalen;
|
||||
int lenrem = newlen % 16;
|
||||
if (lenrem != 0) //need to pad the data length up to multiple of 16.
|
||||
{
|
||||
newlen += (16 - lenrem);
|
||||
}
|
||||
byte[] newdata = new byte[newlen];
|
||||
Buffer.BlockCopy(data, 0, newdata, 0, datalen);
|
||||
int offs = block.TotalSize;
|
||||
int idx = block.AddItem(newdata);
|
||||
MetaBuilderPointer r = new MetaBuilderPointer();
|
||||
r.BlockID = block.Index + 1;
|
||||
r.Offset = offs;// (idx * data.Length);
|
||||
r.Length = datalen; //actual length of string.
|
||||
return r;
|
||||
}
|
||||
|
||||
public MetaPOINTER AddItemPtr<T>(MetaName type, T item) where T : struct //helper method for AddItem<T>
|
||||
{
|
||||
var ptr = AddItem(type, item);
|
||||
return new MetaPOINTER(ptr.BlockID, ptr.Offset, 0);
|
||||
}
|
||||
|
||||
public MetaPOINTER AddItemPtr(MetaName type, byte[] data)//helper method for AddItem<T>
|
||||
{
|
||||
var ptr = AddItem(type, data);
|
||||
return new MetaPOINTER(ptr.BlockID, ptr.Offset, 0);
|
||||
}
|
||||
|
||||
public Array_Structure AddItemArrayPtr<T>(MetaName type, T[] items) where T : struct //helper method for AddItemArray<T>
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_Structure();
|
||||
var ptr = AddItemArray(type, items);
|
||||
return new Array_Structure(ptr);
|
||||
}
|
||||
|
||||
public Array_Structure AddItemArrayPtr(MetaName type, byte[][] data) //helper method for AddItemArray<T>
|
||||
{
|
||||
if ((data == null) || (data.Length == 0)) return new Array_Structure();
|
||||
|
||||
int len = 0;
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
len += data[i].Length;
|
||||
}
|
||||
|
||||
var newdata = new byte[len];
|
||||
|
||||
int offset = 0;
|
||||
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
Buffer.BlockCopy(data[i], 0, newdata, offset, data[i].Length);
|
||||
offset += data[i].Length;
|
||||
}
|
||||
|
||||
var ptr = AddItemArray(type, newdata, data.Length);
|
||||
return new Array_Structure(ptr);
|
||||
}
|
||||
|
||||
public Array_Vector3 AddPaddedVector3ArrayPtr(SharpDX.Vector4[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_Vector3();
|
||||
var ptr = AddItemArray(MetaName.VECTOR4, items); //padded to vec4...
|
||||
return new Array_Vector3(ptr);
|
||||
}
|
||||
public Array_uint AddHashArrayPtr(MetaHash[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_uint();
|
||||
var ptr = AddItemArray(MetaName.HASH, items);
|
||||
return new Array_uint(ptr);
|
||||
}
|
||||
public Array_uint AddUintArrayPtr(uint[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_uint();
|
||||
var ptr = AddItemArray(MetaName.UINT, items);
|
||||
return new Array_uint(ptr);
|
||||
}
|
||||
public Array_ushort AddUshortArrayPtr(ushort[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_ushort();
|
||||
var ptr = AddItemArray(MetaName.USHORT, items);
|
||||
return new Array_ushort(ptr);
|
||||
}
|
||||
public Array_byte AddByteArrayPtr(byte[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_byte();
|
||||
var ptr = AddItemArray(MetaName.BYTE, items);
|
||||
return new Array_byte(ptr);
|
||||
}
|
||||
public Array_float AddFloatArrayPtr(float[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_float();
|
||||
var ptr = AddItemArray(MetaName.FLOAT, items);
|
||||
return new Array_float(ptr);
|
||||
}
|
||||
public CharPointer AddStringPtr(string str) //helper method for AddString
|
||||
{
|
||||
var ptr = AddString(str);
|
||||
return new CharPointer(ptr);
|
||||
}
|
||||
|
||||
|
||||
public Array_StructurePointer AddPointerArray(MetaPOINTER[] arr)
|
||||
{
|
||||
if ((arr == null) || (arr.Length == 0)) return new Array_StructurePointer();
|
||||
var ptr = AddItemArray(MetaName.POINTER, arr);
|
||||
Array_StructurePointer sp = new Array_StructurePointer();
|
||||
sp.Count1 = (ushort)arr.Length;
|
||||
sp.Count2 = sp.Count1;
|
||||
sp.Pointer = ptr.Pointer;
|
||||
return sp;
|
||||
}
|
||||
|
||||
public Array_StructurePointer AddItemPointerArrayPtr<T>(MetaName type, T[] items) where T : struct
|
||||
{
|
||||
//helper method for creating a pointer array
|
||||
if ((items == null) || (items.Length == 0)) return new Array_StructurePointer();
|
||||
|
||||
MetaPOINTER[] ptrs = new MetaPOINTER[items.Length];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
ptrs[i] = AddItemPtr(type, items[i]);
|
||||
}
|
||||
return AddPointerArray(ptrs);
|
||||
|
||||
//Array_StructurePointer sp = new Array_StructurePointer();
|
||||
//sp.Count1 = (ushort)items.Length;
|
||||
//sp.Count2 = sp.Count1;
|
||||
//for (int i = 0; i < items.Length; i++)
|
||||
//{
|
||||
// var item = items[i];
|
||||
// var meptr = AddItemPtr(type, item);
|
||||
// var mptr = AddItem(MetaName.POINTER, meptr);
|
||||
// if (i == 0)
|
||||
// {
|
||||
// sp.Pointer = mptr.Pointer; //main pointer points to the first item.
|
||||
// }
|
||||
//}
|
||||
//return sp;
|
||||
}
|
||||
|
||||
|
||||
public Array_StructurePointer AddWrapperArrayPtr(MetaWrapper[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_StructurePointer();
|
||||
|
||||
|
||||
MetaPOINTER[] ptrs = new MetaPOINTER[items.Length];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
ptrs[i] = items[i].Save(this);
|
||||
}
|
||||
return AddPointerArray(ptrs);
|
||||
|
||||
//Array_StructurePointer sp = new Array_StructurePointer();
|
||||
//sp.Count1 = (ushort)items.Length;
|
||||
//sp.Count2 = sp.Count1;
|
||||
//for (int i = 0; i < items.Length; i++)
|
||||
//{
|
||||
// var item = items[i];
|
||||
// var meptr = item.Save(this);
|
||||
// var mptr = AddItem(MetaName.POINTER, meptr);
|
||||
// if (i == 0)
|
||||
// {
|
||||
// sp.Pointer = mptr.Pointer; //main pointer points to the first item.
|
||||
// }
|
||||
//}
|
||||
//return sp;
|
||||
}
|
||||
|
||||
public Array_Structure AddWrapperArray(MetaWrapper[] items)
|
||||
{
|
||||
if ((items == null) || (items.Length == 0)) return new Array_Structure();
|
||||
|
||||
var sa = new Array_Structure();
|
||||
sa.Count1 = (ushort)items.Length;
|
||||
sa.Count2 = sa.Count1;
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var item = items[i];
|
||||
var meptr = item.Save(this);
|
||||
if (i == 0)
|
||||
{
|
||||
MetaBuilderPointer mbp = new MetaBuilderPointer();
|
||||
mbp.BlockID = meptr.BlockID;
|
||||
mbp.Offset = meptr.Offset;
|
||||
sa.Pointer = mbp.Pointer;
|
||||
}
|
||||
}
|
||||
return sa;
|
||||
}
|
||||
|
||||
|
||||
public byte[] GetData()
|
||||
{
|
||||
int totlen = 0;
|
||||
for (int i = 0; i < Blocks.Count; i++)
|
||||
{
|
||||
totlen += Blocks[i].TotalSize;
|
||||
}
|
||||
byte[] data = new byte[totlen];
|
||||
int offset = 0;
|
||||
for (int i = 0; i < Blocks.Count; i++)
|
||||
{
|
||||
var block = Blocks[i];
|
||||
for (int j = 0; j < block.Items.Count; j++)
|
||||
{
|
||||
var bdata = block.Items[j];
|
||||
Buffer.BlockCopy(bdata, 0, data, offset, bdata.Length);
|
||||
offset += bdata.Length;
|
||||
}
|
||||
}
|
||||
if (offset != data.Length)
|
||||
{ }
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Dictionary<MetaName, MetaStructureInfo> StructureInfos = new Dictionary<MetaName, MetaStructureInfo>();
|
||||
Dictionary<MetaName, MetaEnumInfo> EnumInfos = new Dictionary<MetaName, MetaEnumInfo>();
|
||||
|
||||
public void AddStructureInfo(MetaName name)
|
||||
{
|
||||
if (!StructureInfos.ContainsKey(name))
|
||||
{
|
||||
MetaStructureInfo si = MetaTypes.GetStructureInfo(name);
|
||||
if (si != null)
|
||||
{
|
||||
StructureInfos[name] = si;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void AddEnumInfo(MetaName name)
|
||||
{
|
||||
if (!EnumInfos.ContainsKey(name))
|
||||
{
|
||||
MetaEnumInfo ei = MetaTypes.GetEnumInfo(name);
|
||||
if (ei != null)
|
||||
{
|
||||
EnumInfos[name] = ei;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Meta GetMeta()
|
||||
{
|
||||
Meta m = new Meta();
|
||||
m.FileVFT = 0x405bc808;
|
||||
m.FileUnknown = 1;
|
||||
m.Unknown_10h = 0x50524430;
|
||||
m.Unknown_14h = 0x0079;
|
||||
|
||||
m.RootBlockIndex = 1; //assume first block is root. todo: make adjustable?
|
||||
|
||||
m.StructureInfos = new ResourceSimpleArray<MetaStructureInfo>();
|
||||
foreach (var si in StructureInfos.Values)
|
||||
{
|
||||
m.StructureInfos.Add(si);
|
||||
}
|
||||
m.StructureInfosCount = (short)m.StructureInfos.Count;
|
||||
|
||||
m.EnumInfos = new ResourceSimpleArray<MetaEnumInfo>();
|
||||
foreach (var ei in EnumInfos.Values)
|
||||
{
|
||||
m.EnumInfos.Add(ei);
|
||||
}
|
||||
m.EnumInfosCount = (short)m.EnumInfos.Count;
|
||||
|
||||
m.DataBlocks = new ResourceSimpleArray<MetaDataBlock>();
|
||||
foreach (var bb in Blocks)
|
||||
{
|
||||
m.DataBlocks.Add(bb.GetMetaDataBlock());
|
||||
}
|
||||
m.DataBlocksCount = (short)m.DataBlocks.Count;
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class MetaBuilderBlock
|
||||
{
|
||||
public MetaName StructureNameHash { get; set; }
|
||||
public List<byte[]> Items { get; set; } = new List<byte[]>();
|
||||
public int TotalSize { get; set; } = 0;
|
||||
public int Index { get; set; } = 0;
|
||||
|
||||
public int AddItem(byte[] item)
|
||||
{
|
||||
int idx = Items.Count;
|
||||
Items.Add(item);
|
||||
TotalSize += item.Length;
|
||||
return idx;
|
||||
}
|
||||
|
||||
public uint BasePointer
|
||||
{
|
||||
get
|
||||
{
|
||||
return (((uint)Index + 1) & 0xFFF);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public MetaDataBlock GetMetaDataBlock()
|
||||
{
|
||||
if (TotalSize <= 0) return null;
|
||||
|
||||
byte[] data = new byte[TotalSize];
|
||||
int offset = 0;
|
||||
for (int j = 0; j < Items.Count; j++)
|
||||
{
|
||||
var bdata = Items[j];
|
||||
Buffer.BlockCopy(bdata, 0, data, offset, bdata.Length);
|
||||
offset += bdata.Length;
|
||||
}
|
||||
|
||||
MetaDataBlock db = new MetaDataBlock();
|
||||
db.StructureNameHash = StructureNameHash;
|
||||
db.DataLength = TotalSize;
|
||||
db.Data = data;
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public struct MetaBuilderPointer
|
||||
{
|
||||
public int BlockID { get; set; } //1-based id
|
||||
public int Offset { get; set; } //byte offset
|
||||
public int Length { get; set; } //for temp use...
|
||||
public uint Pointer
|
||||
{
|
||||
get
|
||||
{
|
||||
uint bidx = (((uint)BlockID) & 0xFFF);
|
||||
uint offs = (((uint)Offset) & 0xFFFFF) << 12;
|
||||
return bidx + offs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,858 @@
|
||||
/*
|
||||
Copyright(c) 2015 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//shamelessly stolen and mangled
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
|
||||
public enum PsoSection : uint
|
||||
{
|
||||
PSIN = 0x5053494E,
|
||||
PMAP = 0x504D4150,
|
||||
PSCH = 0x50534348,
|
||||
PSIG = 0x50534947,
|
||||
STRF = 0x53545246,
|
||||
STRS = 0x53545253,
|
||||
STRE = 0x53545245,
|
||||
CHKS = 0x43484B53,
|
||||
}
|
||||
|
||||
public enum PsoDataType : byte
|
||||
{
|
||||
Bool = 0x00,
|
||||
SByte = 0x01,
|
||||
UByte = 0x02,
|
||||
SShort = 0x03,
|
||||
UShort = 0x04,
|
||||
SInt = 0x05,
|
||||
UInt = 0x06,
|
||||
Float = 0x07,
|
||||
Float2 = 0x08,
|
||||
Float3 = 0x09,
|
||||
Float4 = 0x0a,
|
||||
String = 0x0b,
|
||||
Structure = 0x0c,
|
||||
Array = 0x0d,
|
||||
Enum = 0x0e,
|
||||
Flags = 0x0f,
|
||||
Map = 0x10,
|
||||
Float3a = 0x14,
|
||||
Float4a = 0x15,
|
||||
HFloat = 0x1e,
|
||||
Long = 0x20,
|
||||
}
|
||||
public static class PsoDataTypes
|
||||
{
|
||||
public static string GetCSharpTypeName(PsoDataType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case PsoDataType.Bool: return "bool";
|
||||
case PsoDataType.SByte: return "sbyte";
|
||||
case PsoDataType.UByte: return "byte";
|
||||
case PsoDataType.SShort: return "short";
|
||||
case PsoDataType.UShort: return "ushort";
|
||||
case PsoDataType.SInt: return "int";
|
||||
case PsoDataType.UInt: return "int";
|
||||
case PsoDataType.Float: return "float";
|
||||
case PsoDataType.Float2: return "long";
|
||||
case PsoDataType.String: return "uint"; //hash? NEEDS WORK?
|
||||
case PsoDataType.Enum: return "byte";
|
||||
case PsoDataType.Flags: return "short";
|
||||
case PsoDataType.HFloat: return "short";
|
||||
case PsoDataType.Long: return "long";
|
||||
case PsoDataType.Float3:
|
||||
case PsoDataType.Float4:
|
||||
case PsoDataType.Map:
|
||||
case PsoDataType.Float3a:
|
||||
case PsoDataType.Float4a:
|
||||
case PsoDataType.Structure:
|
||||
case PsoDataType.Array:
|
||||
default:
|
||||
return t.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoFile
|
||||
{
|
||||
public PsoDataSection DataSection { get; set; }
|
||||
public PsoDataMapSection DataMapSection { get; set; }
|
||||
public PsoSchemaSection SchemaSection { get; set; }
|
||||
public PsoSTRFSection STRFSection { get; set; }
|
||||
public PsoSTRSSection STRSSection { get; set; }
|
||||
public PsoPSIGSection PSIGSection { get; set; }
|
||||
public PsoSTRESection STRESection { get; set; }
|
||||
public PsoCHKSSection CHKSSection { get; set; }
|
||||
|
||||
public void Load(string fileName)
|
||||
{
|
||||
using (var stream = new FileStream(fileName, FileMode.Open))
|
||||
Load(stream);
|
||||
}
|
||||
|
||||
public virtual void Load(Stream stream)
|
||||
{
|
||||
stream.Position = 0;
|
||||
|
||||
var reader = new DataReader(stream, Endianess.BigEndian);
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
var identInt = reader.ReadUInt32();
|
||||
var ident = (PsoSection)identInt;
|
||||
var length = reader.ReadInt32();
|
||||
|
||||
reader.Position -= 8;
|
||||
|
||||
var sectionData = reader.ReadBytes(length);
|
||||
var sectionStream = new MemoryStream(sectionData);
|
||||
var sectionReader = new DataReader(sectionStream, Endianess.BigEndian);
|
||||
|
||||
switch (ident)
|
||||
{
|
||||
case PsoSection.PSIN: //0x5053494E "PSIN" - ID / data section
|
||||
DataSection = new PsoDataSection();
|
||||
DataSection.Read(sectionReader);
|
||||
break;
|
||||
case PsoSection.PMAP: //0x504D4150 "PMAP" //data mapping
|
||||
DataMapSection = new PsoDataMapSection();
|
||||
DataMapSection.Read(sectionReader);
|
||||
break;
|
||||
case PsoSection.PSCH: //0x50534348 "PSCH" //schema
|
||||
SchemaSection = new PsoSchemaSection();
|
||||
SchemaSection.Read(sectionReader);
|
||||
break;
|
||||
case PsoSection.STRF: //0x53545246 "STRF" //paths/STRINGS (folder strings?)
|
||||
STRFSection = new PsoSTRFSection();
|
||||
STRFSection.Read(sectionReader);
|
||||
break;
|
||||
case PsoSection.STRS: //0x53545253 "STRS" //names/strings (DES_)
|
||||
STRSSection = new PsoSTRSSection();
|
||||
STRSSection.Read(sectionReader);
|
||||
break;
|
||||
case PsoSection.STRE: //0x53545245 "STRE" //probably encrypted strings.....
|
||||
STRESection = new PsoSTRESection();
|
||||
STRESection.Read(sectionReader);
|
||||
break;
|
||||
case PsoSection.PSIG: //0x50534947 "PSIG" //signature?
|
||||
PSIGSection = new PsoPSIGSection();
|
||||
PSIGSection.Read(sectionReader);
|
||||
break;
|
||||
case PsoSection.CHKS: //0x43484B53 "CHKS" //checksum?
|
||||
CHKSSection = new PsoCHKSSection();
|
||||
CHKSSection.Read(sectionReader);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(string fileName)
|
||||
{
|
||||
using (var stream = new FileStream(fileName, FileMode.Create))
|
||||
Save(stream);
|
||||
}
|
||||
|
||||
public virtual void Save(Stream stream)
|
||||
{
|
||||
var writer = new DataWriter(stream, Endianess.BigEndian);
|
||||
if (DataSection != null) DataSection.Write(writer);
|
||||
if (DataMapSection != null) DataMapSection.Write(writer);
|
||||
if (SchemaSection != null) SchemaSection.Write(writer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public PsoDataMappingEntry GetBlock(int id)
|
||||
{
|
||||
if (DataMapSection == null) return null;
|
||||
if (DataMapSection.Entries == null) return null;
|
||||
PsoDataMappingEntry block = null;
|
||||
var ind = id - 1;
|
||||
var blocks = DataMapSection.Entries;
|
||||
if ((ind >= 0) && (ind < blocks.Length))
|
||||
{
|
||||
block = blocks[ind];
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static bool IsPSO(Stream stream)
|
||||
{
|
||||
//return !IsRBF(stream);
|
||||
|
||||
//1347635534
|
||||
var reader = new DataReader(stream, Endianess.BigEndian);
|
||||
var identInt = reader.ReadUInt32();
|
||||
stream.Position = 0;
|
||||
return ((identInt ) == 1347635534); //"PSIN"
|
||||
|
||||
}
|
||||
|
||||
public static bool IsRBF(Stream stream)
|
||||
{
|
||||
var reader = new DataReader(stream, Endianess.BigEndian);
|
||||
var identInt = reader.ReadUInt32();
|
||||
stream.Position = 0;
|
||||
return ((identInt & 0xFFFFFF00) == 0x52424600);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoDataSection
|
||||
{
|
||||
public uint Ident { get; set; } = 0x5053494E;
|
||||
public int Length { get; private set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadUInt32();
|
||||
Length = reader.ReadInt32();
|
||||
reader.Position -= 8;
|
||||
Data = reader.ReadBytes(Length);
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
writer.Write(Data);
|
||||
writer.Position -= Data.Length;
|
||||
writer.Write((uint)0x5053494E);
|
||||
writer.Write((uint)(Data.Length));
|
||||
writer.Position += Data.Length - 8;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoDataMapSection
|
||||
{
|
||||
public int Ident { get; set; } = 0x504D4150;
|
||||
public int Length { get; private set; }
|
||||
public int RootId { get; set; }
|
||||
public short EntriesCount { get; private set; }
|
||||
public short Unknown_Eh { get; set; } = 0x7070;
|
||||
public PsoDataMappingEntry[] Entries { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadInt32();
|
||||
Length = reader.ReadInt32();
|
||||
RootId = reader.ReadInt32();
|
||||
EntriesCount = reader.ReadInt16();
|
||||
Unknown_Eh = reader.ReadInt16();
|
||||
Entries = new PsoDataMappingEntry[EntriesCount];
|
||||
for (int i = 0; i < EntriesCount; i++)
|
||||
{
|
||||
var entry = new PsoDataMappingEntry();
|
||||
entry.Read(reader);
|
||||
Entries[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
// update...
|
||||
EntriesCount = (short)Entries.Length;
|
||||
Length = 16 + EntriesCount * 16;
|
||||
|
||||
writer.Write(Ident);
|
||||
writer.Write(Length);
|
||||
writer.Write(RootId);
|
||||
writer.Write(EntriesCount);
|
||||
writer.Write(Unknown_Eh);
|
||||
foreach (var entry in Entries)
|
||||
{
|
||||
entry.Write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + EntriesCount.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoDataMappingEntry
|
||||
{
|
||||
public MetaName NameHash { get; set; }
|
||||
public int Offset { get; set; }
|
||||
public int Unknown_8h { get; set; } = 0x00000000;
|
||||
public int Length { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
this.NameHash = (MetaName)reader.ReadUInt32();
|
||||
this.Offset = reader.ReadInt32();
|
||||
this.Unknown_8h = reader.ReadInt32();
|
||||
this.Length = reader.ReadInt32();
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
writer.Write((uint)NameHash);
|
||||
writer.Write(Offset);
|
||||
writer.Write(Unknown_8h);
|
||||
writer.Write(Length);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return NameHash.ToString() + ": " + Offset.ToString() + ": " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoSchemaSection
|
||||
{
|
||||
public int Ident { get; private set; } = 0x50534348;
|
||||
public int Length { get; set; }
|
||||
public uint Count { get; set; }
|
||||
|
||||
public PsoElementIndexInfo[] EntriesIdx { get; set; }
|
||||
public PsoElementInfo[] Entries { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadInt32();
|
||||
Length = reader.ReadInt32();
|
||||
Count = reader.ReadUInt32();
|
||||
|
||||
this.EntriesIdx = new PsoElementIndexInfo[Count];
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var entry = new PsoElementIndexInfo();
|
||||
entry.Read(reader);
|
||||
EntriesIdx[i] = entry;
|
||||
}
|
||||
|
||||
this.Entries = new PsoElementInfo[Count];
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
reader.Position = EntriesIdx[i].Offset;
|
||||
var type = reader.ReadByte();
|
||||
|
||||
reader.Position = EntriesIdx[i].Offset;
|
||||
if (type == 0)
|
||||
{
|
||||
var entry = new PsoStructureInfo();
|
||||
entry.Read(reader);
|
||||
entry.IndexInfo = EntriesIdx[i];
|
||||
Entries[i] = entry;
|
||||
}
|
||||
else if (type == 1)
|
||||
{
|
||||
var entry = new PsoEnumInfo();
|
||||
entry.Read(reader);
|
||||
entry.IndexInfo = EntriesIdx[i];
|
||||
Entries[i] = entry;
|
||||
}
|
||||
else
|
||||
throw new Exception("unknown type!");
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
|
||||
var entriesStream = new MemoryStream();
|
||||
var entriesWriter = new DataWriter(entriesStream, Endianess.BigEndian);
|
||||
for (int i = 0; i < Entries.Length; i++)
|
||||
{
|
||||
EntriesIdx[i].Offset = 12 + 8 * Entries.Length + (int)entriesWriter.Position;
|
||||
Entries[i].Write(entriesWriter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var indexStream = new MemoryStream();
|
||||
var indexWriter = new DataWriter(indexStream, Endianess.BigEndian);
|
||||
foreach (var entry in EntriesIdx)
|
||||
entry.Write(indexWriter);
|
||||
|
||||
|
||||
|
||||
|
||||
writer.Write(Ident);
|
||||
writer.Write((int)(12 + entriesStream.Length + indexStream.Length));
|
||||
writer.Write((int)(Entries.Length));
|
||||
|
||||
// write entries index data
|
||||
var buf1 = new byte[indexStream.Length];
|
||||
indexStream.Position = 0;
|
||||
indexStream.Read(buf1, 0, buf1.Length);
|
||||
writer.Write(buf1);
|
||||
|
||||
// write entries data
|
||||
var buf2 = new byte[entriesStream.Length];
|
||||
entriesStream.Position = 0;
|
||||
entriesStream.Read(buf2, 0, buf2.Length);
|
||||
writer.Write(buf2);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + Count.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoElementIndexInfo
|
||||
{
|
||||
public MetaName NameHash { get; set; }
|
||||
public int Offset { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
this.NameHash = (MetaName)reader.ReadUInt32();
|
||||
this.Offset = reader.ReadInt32();
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
writer.Write((uint)NameHash);
|
||||
writer.Write(Offset);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return NameHash.ToString() + ": " + Offset.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public abstract class PsoElementInfo
|
||||
{
|
||||
public PsoElementIndexInfo IndexInfo { get; set; }
|
||||
|
||||
public abstract void Read(DataReader reader);
|
||||
|
||||
public abstract void Write(DataWriter writer);
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoStructureInfo : PsoElementInfo
|
||||
{
|
||||
public byte Type { get; set; } = 0;
|
||||
public short EntriesCount { get; private set; }
|
||||
public byte Unk { get; set; }
|
||||
public int StructureLength { get; set; }
|
||||
public uint Unk_Ch { get; set; } = 0x00000000;
|
||||
public PsoStructureEntryInfo[] Entries { get; set; }
|
||||
|
||||
public override void Read(DataReader reader)
|
||||
{
|
||||
uint x = reader.ReadUInt32();
|
||||
this.Type = (byte)((x & 0xFF000000) >> 24);
|
||||
this.EntriesCount = (short)(x & 0xFFFF);
|
||||
this.Unk = (byte)((x & 0x00FF0000) >> 16);
|
||||
this.StructureLength = reader.ReadInt32();
|
||||
this.Unk_Ch = reader.ReadUInt32();
|
||||
|
||||
Entries = new PsoStructureEntryInfo[EntriesCount];
|
||||
for (int i = 0; i < EntriesCount; i++)
|
||||
{
|
||||
var entry = new PsoStructureEntryInfo();
|
||||
entry.Read(reader);
|
||||
Entries[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(DataWriter writer)
|
||||
{
|
||||
Type = 0;
|
||||
EntriesCount = (short)Entries.Length;
|
||||
|
||||
uint typeAndEntriesCount = (uint)(Type << 24) | (uint)(Unk << 16) | (ushort)EntriesCount;
|
||||
writer.Write(typeAndEntriesCount);
|
||||
writer.Write(StructureLength);
|
||||
writer.Write(Unk_Ch);
|
||||
|
||||
foreach (var entry in Entries)
|
||||
{
|
||||
entry.Write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return IndexInfo.ToString() + " - " + Type.ToString() + ": " + EntriesCount.ToString();
|
||||
}
|
||||
|
||||
public PsoStructureEntryInfo FindEntry(MetaName name)
|
||||
{
|
||||
if (Entries != null)
|
||||
{
|
||||
foreach (var entry in Entries)
|
||||
{
|
||||
if (entry.EntryNameHash == name) return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public PsoStructureEntryInfo GetEntry(int id)
|
||||
{
|
||||
if ((Entries != null) && (id >= 0) && (id < Entries.Length))
|
||||
{
|
||||
return Entries[id];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoStructureEntryInfo
|
||||
{
|
||||
public MetaName EntryNameHash { get; set; }
|
||||
public PsoDataType Type { get; set; }
|
||||
public byte Unk_5h { get; set; }
|
||||
public ushort DataOffset { get; set; }
|
||||
public uint ReferenceKey { get; set; } // when array -> entry index with type
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
this.EntryNameHash = (MetaName)reader.ReadUInt32();
|
||||
this.Type = (PsoDataType)reader.ReadByte();
|
||||
this.Unk_5h = reader.ReadByte();
|
||||
this.DataOffset = reader.ReadUInt16();
|
||||
this.ReferenceKey = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
writer.Write((uint)EntryNameHash);
|
||||
writer.Write((byte)Type);
|
||||
writer.Write(Unk_5h);
|
||||
writer.Write(DataOffset);
|
||||
writer.Write(ReferenceKey);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if(ReferenceKey!=0)
|
||||
{
|
||||
return EntryNameHash.ToString() + ": " + Type.ToString() + ": " + DataOffset.ToString() + ": " + ((MetaName)ReferenceKey).ToString();
|
||||
}
|
||||
return EntryNameHash.ToString() + ": " + Type.ToString() + ": " + DataOffset.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoEnumInfo : PsoElementInfo
|
||||
{
|
||||
public byte Type { get; private set; } = 1;
|
||||
public int EntriesCount { get; private set; }
|
||||
public PsoEnumEntryInfo[] Entries { get; set; }
|
||||
|
||||
public override void Read(DataReader reader)
|
||||
{
|
||||
uint x = reader.ReadUInt32();
|
||||
this.Type = (byte)((x & 0xFF000000) >> 24);
|
||||
this.EntriesCount = (int)(x & 0x00FFFFFF);
|
||||
|
||||
Entries = new PsoEnumEntryInfo[EntriesCount];
|
||||
for (int i = 0; i < EntriesCount; i++)
|
||||
{
|
||||
var entry = new PsoEnumEntryInfo();
|
||||
entry.Read(reader);
|
||||
Entries[i] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(DataWriter writer)
|
||||
{
|
||||
// update...
|
||||
Type = 1;
|
||||
EntriesCount = Entries.Length;
|
||||
|
||||
uint typeAndEntriesCount = (uint)(Type << 24) | (uint)EntriesCount;
|
||||
writer.Write(typeAndEntriesCount);
|
||||
|
||||
foreach (var entry in Entries)
|
||||
{
|
||||
entry.Write(writer);
|
||||
}
|
||||
}
|
||||
|
||||
public PsoEnumEntryInfo FindEntry(int val)
|
||||
{
|
||||
if (Entries == null) return null;
|
||||
for (int i = 0; i < Entries.Length; i++)
|
||||
{
|
||||
var entry = Entries[i];
|
||||
if (entry.EntryKey == val)
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return IndexInfo.ToString() + " - " + Type.ToString() + ": " + EntriesCount.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoEnumEntryInfo
|
||||
{
|
||||
public MetaName EntryNameHash { get; set; }
|
||||
public int EntryKey { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
this.EntryNameHash = (MetaName)reader.ReadUInt32();
|
||||
this.EntryKey = reader.ReadInt32();
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
writer.Write((uint)EntryNameHash);
|
||||
writer.Write(EntryKey);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return EntryNameHash.ToString() + ": " + EntryKey.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoSTRFSection
|
||||
{
|
||||
public int Ident { get; private set; } = 0x53545246;
|
||||
public int Length { get; set; }
|
||||
public string[] Strings { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadInt32();
|
||||
Length = reader.ReadInt32();
|
||||
List<string> strs = new List<string>();
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
strs.Add(reader.ReadString());
|
||||
}
|
||||
foreach (var str in strs)
|
||||
{
|
||||
JenkIndex.Ensure(str);
|
||||
JenkIndex.Ensure(str.ToLowerInvariant());
|
||||
}
|
||||
Strings = strs.ToArray();
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
|
||||
writer.Write(Ident);
|
||||
writer.Write(Length);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoSTRSSection
|
||||
{
|
||||
public int Ident { get; private set; } = 0x53545253;
|
||||
public int Length { get; set; }
|
||||
public string[] Strings { get; set; }
|
||||
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadInt32();
|
||||
Length = reader.ReadInt32();
|
||||
|
||||
List<string> strs = new List<string>();
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
strs.Add(reader.ReadString());
|
||||
}
|
||||
foreach (var str in strs)
|
||||
{
|
||||
JenkIndex.Ensure(str);
|
||||
JenkIndex.Ensure(str.ToLowerInvariant());
|
||||
}
|
||||
Strings = strs.ToArray();
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
|
||||
writer.Write(Ident);
|
||||
writer.Write(Length);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoSTRESection
|
||||
{
|
||||
public int Ident { get; private set; } = 0x53545245;
|
||||
public int Length { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
//public MetaHash[] Hashes { get; set; }
|
||||
//public byte[] Decr1 { get; set; }
|
||||
//public byte[] Decr2 { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadInt32();
|
||||
Length = reader.ReadInt32();
|
||||
|
||||
if (Length > 8)
|
||||
{
|
||||
Data = reader.ReadBytes(Length - 8);
|
||||
|
||||
//reader.Position = 8;
|
||||
//List<MetaHash> hashes = new List<MetaHash>();
|
||||
//while (reader.Position < reader.Length)
|
||||
//{
|
||||
// hashes.Add(reader.ReadUInt32());
|
||||
//}
|
||||
//Hashes = hashes.ToArray();
|
||||
|
||||
//Decr1 = GTACrypto.DecryptAES(Data);
|
||||
//Decr2 = GTACrypto.DecryptNG(Data, )
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
|
||||
writer.Write(Ident);
|
||||
writer.Write(Length);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoPSIGSection
|
||||
{
|
||||
public int Ident { get; private set; } = 0x50534947;
|
||||
public int Length { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadInt32();
|
||||
Length = reader.ReadInt32();
|
||||
|
||||
if (Length > 8)
|
||||
{
|
||||
Data = reader.ReadBytes(Length - 8);
|
||||
}
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
|
||||
writer.Write(Ident);
|
||||
writer.Write(Length);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class PsoCHKSSection
|
||||
{
|
||||
public int Ident { get; private set; } = 0x43484B53;
|
||||
public int Length { get; set; }
|
||||
public uint FileSize { get; set; }
|
||||
public uint Checksum { get; set; }
|
||||
public uint Unk0 { get; set; } = 0x79707070; // "yppp"
|
||||
|
||||
public void Read(DataReader reader)
|
||||
{
|
||||
Ident = reader.ReadInt32();
|
||||
Length = reader.ReadInt32();
|
||||
|
||||
if (Length != 20)
|
||||
{ return; }
|
||||
|
||||
FileSize = reader.ReadUInt32();
|
||||
Checksum = reader.ReadUInt32();
|
||||
Unk0 = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
public void Write(DataWriter writer)
|
||||
{
|
||||
|
||||
writer.Write(Ident);
|
||||
writer.Write(Length);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Ident.ToString() + ": " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,856 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using TC = System.ComponentModel.TypeConverterAttribute;
|
||||
using EXP = System.ComponentModel.ExpandableObjectConverter;
|
||||
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public static class PsoTypes
|
||||
{
|
||||
//for parsing schema info in PSO files to generate structs for PSO parsing.
|
||||
//equivalent of MetaTypes but for PSO.
|
||||
|
||||
public static Dictionary<MetaName, PsoEnumInfo> EnumDict = new Dictionary<MetaName, PsoEnumInfo>();
|
||||
public static Dictionary<MetaName, PsoStructureInfo> StructDict = new Dictionary<MetaName, PsoStructureInfo>();
|
||||
|
||||
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
StructDict.Clear();
|
||||
}
|
||||
|
||||
public static void EnsurePsoTypes(PsoFile pso)
|
||||
{
|
||||
|
||||
if ((pso.SchemaSection == null) || (pso.SchemaSection.Entries == null) || (pso.SchemaSection.EntriesIdx == null))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < pso.SchemaSection.Entries.Length; i++)
|
||||
{
|
||||
var entry = pso.SchemaSection.Entries[i];
|
||||
var enuminfo = entry as PsoEnumInfo;
|
||||
var structinfo = entry as PsoStructureInfo;
|
||||
|
||||
if (enuminfo != null)
|
||||
{
|
||||
if (!EnumDict.ContainsKey(enuminfo.IndexInfo.NameHash))
|
||||
{
|
||||
EnumDict.Add(enuminfo.IndexInfo.NameHash, enuminfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
PsoEnumInfo oldei = EnumDict[enuminfo.IndexInfo.NameHash];
|
||||
if (!ComparePsoEnumInfos(oldei, enuminfo))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (structinfo != null)
|
||||
{
|
||||
if (!StructDict.ContainsKey(structinfo.IndexInfo.NameHash))
|
||||
{
|
||||
StructDict.Add(structinfo.IndexInfo.NameHash, structinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
PsoStructureInfo oldsi = StructDict[structinfo.IndexInfo.NameHash];
|
||||
if (!ComparePsoStructureInfos(oldsi, structinfo))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static bool ComparePsoEnumInfos(PsoEnumInfo a, PsoEnumInfo b)
|
||||
{
|
||||
//returns true if they are the same.
|
||||
|
||||
if (a.Entries.Length != b.Entries.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.Entries.Length; i++)
|
||||
{
|
||||
if ((a.Entries[i].EntryNameHash != b.Entries[i].EntryNameHash) ||
|
||||
(a.Entries[i].EntryKey != b.Entries[i].EntryKey))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public static bool ComparePsoStructureInfos(PsoStructureInfo a, PsoStructureInfo b)
|
||||
{
|
||||
//returns true if they are the same.
|
||||
|
||||
if (a.Entries.Length != b.Entries.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < a.Entries.Length; i++)
|
||||
{
|
||||
if ((a.Entries[i].EntryNameHash != b.Entries[i].EntryNameHash) ||
|
||||
(a.Entries[i].DataOffset != b.Entries[i].DataOffset) ||
|
||||
(a.Entries[i].Type != b.Entries[i].Type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static string GetTypesString()
|
||||
{
|
||||
StringBuilder sbe = new StringBuilder();
|
||||
StringBuilder sbs = new StringBuilder();
|
||||
|
||||
sbe.AppendLine("//Enum infos");
|
||||
sbs.AppendLine("//Struct infos");
|
||||
|
||||
|
||||
foreach (var kvp in EnumDict)
|
||||
{
|
||||
var ei = kvp.Value;
|
||||
string name = GetSafeName(ei.IndexInfo.NameHash, ei.Type);
|
||||
sbe.AppendLine("public enum " + name + " //Type:" + ei.Type.ToString());
|
||||
sbe.AppendLine("{");
|
||||
foreach (var entry in ei.Entries)
|
||||
{
|
||||
string eename = GetSafeName(entry.EntryNameHash, (uint)entry.EntryKey);
|
||||
sbe.AppendFormat(" {0} = {1},", eename, entry.EntryKey);
|
||||
sbe.AppendLine();
|
||||
}
|
||||
sbe.AppendLine("}");
|
||||
sbe.AppendLine();
|
||||
}
|
||||
|
||||
foreach (var kvp in StructDict)
|
||||
{
|
||||
var si = kvp.Value;
|
||||
string name = GetSafeName(si.IndexInfo.NameHash, si.Type);
|
||||
sbs.AppendLine("public struct " + name + " //" + si.StructureLength.ToString() + " bytes, Type:" + si.Type.ToString());
|
||||
sbs.AppendLine("{");
|
||||
for (int i = 0; i < si.Entries.Length; i++)
|
||||
{
|
||||
var entry = si.Entries[i];
|
||||
|
||||
if ((entry.DataOffset == 0) && (entry.EntryNameHash == MetaName.ARRAYINFO)) //referred to by array
|
||||
{
|
||||
}
|
||||
else
|
||||
{
|
||||
string sename = GetSafeName(entry.EntryNameHash, entry.ReferenceKey);
|
||||
string fmt = " public {0} {1}; //{2} {3}";
|
||||
|
||||
if (entry.Type == PsoDataType.Array)
|
||||
{
|
||||
if (entry.ReferenceKey >= si.Entries.Length)
|
||||
{
|
||||
sbs.AppendFormat(fmt, entry.Type.ToString(), sename, entry.DataOffset, entry.ToString() + " { unexpected key! " + entry.ReferenceKey.ToString() + "}");
|
||||
sbs.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
var structentry = si.Entries[(int)entry.ReferenceKey];
|
||||
var typename = "Array_" + PsoDataTypes.GetCSharpTypeName(structentry.Type);
|
||||
sbs.AppendFormat(fmt, typename, sename, entry.DataOffset, entry.ToString() + " {" + structentry.ToString() + "}");
|
||||
sbs.AppendLine();
|
||||
}
|
||||
}
|
||||
else if (entry.Type == PsoDataType.Structure)
|
||||
{
|
||||
var typename = GetSafeName((MetaName)entry.ReferenceKey, entry.ReferenceKey);
|
||||
sbs.AppendFormat(fmt, typename, sename, entry.DataOffset, entry.ToString());
|
||||
sbs.AppendLine();
|
||||
}
|
||||
else
|
||||
{
|
||||
var typename = PsoDataTypes.GetCSharpTypeName(entry.Type);
|
||||
sbs.AppendFormat(fmt, typename, sename, entry.DataOffset, entry);
|
||||
sbs.AppendLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
sbs.AppendLine("}");
|
||||
sbs.AppendLine();
|
||||
}
|
||||
|
||||
|
||||
sbe.AppendLine();
|
||||
sbe.AppendLine();
|
||||
sbe.AppendLine();
|
||||
sbe.AppendLine();
|
||||
sbe.AppendLine();
|
||||
sbe.Append(sbs.ToString());
|
||||
|
||||
string result = sbe.ToString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static string GetSafeName(MetaName namehash, uint key)
|
||||
{
|
||||
string name = namehash.ToString();
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
name = "Unk_" + key;
|
||||
}
|
||||
if (!char.IsLetter(name[0]))
|
||||
{
|
||||
name = "Unk_" + name;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static T ConvertDataRaw<T>(byte[] data) where T : struct
|
||||
{
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
var r = Marshal.PtrToStructure<T>(h);
|
||||
handle.Free();
|
||||
return r;
|
||||
}
|
||||
public static T ConvertDataRaw<T>(byte[] data, int offset) where T : struct
|
||||
{
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
var r = Marshal.PtrToStructure<T>(h + offset);
|
||||
handle.Free();
|
||||
return r;
|
||||
}
|
||||
public static T ConvertData<T>(byte[] data, int offset) where T : struct, IPsoSwapEnd
|
||||
{
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
var r = Marshal.PtrToStructure<T>(h + offset);
|
||||
handle.Free();
|
||||
r.SwapEnd();
|
||||
return r;
|
||||
}
|
||||
public static T[] ConvertDataArrayRaw<T>(byte[] data, int offset, int count) where T : struct
|
||||
{
|
||||
T[] items = new T[count];
|
||||
int itemsize = Marshal.SizeOf(typeof(T));
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
int off = offset + i * itemsize;
|
||||
items[i] = ConvertDataRaw<T>(data, off);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
public static T GetItem<T>(PsoFile pso, int offset) where T : struct, IPsoSwapEnd
|
||||
{
|
||||
return ConvertData<T>(pso.DataSection.Data, offset);
|
||||
}
|
||||
public static T GetRootItem<T>(PsoFile pso) where T : struct, IPsoSwapEnd
|
||||
{
|
||||
var i = pso.DataMapSection.RootId - 1;
|
||||
var e = pso.DataMapSection.Entries[i];
|
||||
return GetItem<T>(pso, e.Offset);
|
||||
}
|
||||
public static PsoDataMappingEntry GetRootEntry(PsoFile pso)
|
||||
{
|
||||
var i = pso.DataMapSection.RootId - 1;
|
||||
var e = pso.DataMapSection.Entries[i];
|
||||
return e;
|
||||
}
|
||||
|
||||
public static T[] GetItemArrayRaw<T>(PsoFile pso, Array_Structure arr) where T : struct
|
||||
{
|
||||
if ((arr.Count1 > 0) && (arr.Pointer > 0))
|
||||
{
|
||||
var entry = pso.DataMapSection.Entries[(int)arr.PointerDataIndex];
|
||||
return ConvertDataArrayRaw<T>(pso.DataSection.Data, entry.Offset, arr.Count1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static T[] GetItemArray<T>(PsoFile pso, Array_Structure arr) where T : struct, IPsoSwapEnd
|
||||
{
|
||||
if ((arr.Count1 > 0) && (arr.Pointer > 0))
|
||||
{
|
||||
var entry = pso.DataMapSection.Entries[(int)arr.PointerDataIndex];
|
||||
var res = ConvertDataArrayRaw<T>(pso.DataSection.Data, entry.Offset, arr.Count1);
|
||||
if (res != null)
|
||||
{
|
||||
for (int i = 0; i < res.Length; i++)
|
||||
{
|
||||
res[i].SwapEnd();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public static uint[] GetUintArrayRaw(PsoFile pso, Array_uint arr)
|
||||
{
|
||||
byte[] data = pso.DataSection.Data;
|
||||
var entryid = arr.Pointer & 0xFFF;
|
||||
if ((entryid == 0) || (entryid > pso.DataMapSection.EntriesCount))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var entryoffset = (arr.Pointer & 0xFFFFFF) >> 12;
|
||||
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
|
||||
int totoffset = arrentry.Offset + (int)entryoffset;
|
||||
uint[] readdata = ConvertDataArrayRaw<uint>(data, totoffset, arr.Count1);
|
||||
return readdata;
|
||||
}
|
||||
public static uint[] GetUintArray(PsoFile pso, Array_uint arr)
|
||||
{
|
||||
uint[] uints = GetUintArrayRaw(pso, arr);
|
||||
if (uints == null) return null;
|
||||
for (int i = 0; i < uints.Length; i++)
|
||||
{
|
||||
uints[i] = MetaTypes.SwapBytes(uints[i]);
|
||||
}
|
||||
return uints;
|
||||
}
|
||||
|
||||
public static MetaHash[] GetHashArray(PsoFile pso, Array_uint arr)
|
||||
{
|
||||
uint[] uints = GetUintArrayRaw(pso, arr);
|
||||
if (uints == null) return null;
|
||||
MetaHash[] hashes = new MetaHash[uints.Length];
|
||||
for (int n = 0; n < uints.Length; n++)
|
||||
{
|
||||
hashes[n].Hash = MetaTypes.SwapBytes(uints[n]);
|
||||
}
|
||||
return hashes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static float[] GetFloatArrayRaw(PsoFile pso, Array_float arr)
|
||||
{
|
||||
byte[] data = pso.DataSection.Data;
|
||||
var entryid = arr.Pointer & 0xFFF;
|
||||
if ((entryid == 0) || (entryid > pso.DataMapSection.EntriesCount))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var entryoffset = (arr.Pointer & 0xFFFFFF) >> 12;
|
||||
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
|
||||
int totoffset = arrentry.Offset + (int)entryoffset;
|
||||
float[] readdata = ConvertDataArrayRaw<float>(data, totoffset, arr.Count1);
|
||||
return readdata;
|
||||
}
|
||||
public static float[] GetFloatArray(PsoFile pso, Array_float arr)
|
||||
{
|
||||
float[] floats = GetFloatArrayRaw(pso, arr);
|
||||
if (floats == null) return null;
|
||||
for (int i = 0; i < floats.Length; i++)
|
||||
{
|
||||
floats[i] = MetaTypes.SwapBytes(floats[i]);
|
||||
}
|
||||
return floats;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static ushort[] GetUShortArrayRaw(PsoFile pso, Array_Structure arr)
|
||||
{
|
||||
byte[] data = pso.DataSection.Data;
|
||||
var entryid = arr.Pointer & 0xFFF;
|
||||
if ((entryid == 0) || (entryid > pso.DataMapSection.EntriesCount))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var entryoffset = (arr.Pointer & 0xFFFFFF) >> 12;
|
||||
var arrentry = pso.DataMapSection.Entries[(int)entryid - 1];
|
||||
int totoffset = arrentry.Offset + (int)entryoffset;
|
||||
ushort[] readdata = ConvertDataArrayRaw<ushort>(data, totoffset, arr.Count1);
|
||||
return readdata;
|
||||
}
|
||||
public static ushort[] GetUShortArray(PsoFile pso, Array_Structure arr)
|
||||
{
|
||||
ushort[] ushorts = GetUShortArrayRaw(pso, arr);
|
||||
if (ushorts == null) return null;
|
||||
for (int i = 0; i < ushorts.Length; i++)
|
||||
{
|
||||
ushorts[i] = MetaTypes.SwapBytes(ushorts[i]);
|
||||
}
|
||||
return ushorts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static T[] GetObjectArray<T, U>(PsoFile pso, Array_Structure arr) where U : struct, IPsoSwapEnd where T : PsoClass<U>, new()
|
||||
{
|
||||
U[] items = GetItemArray<U>(pso, arr);
|
||||
if (items == null) return null;
|
||||
if (items.Length == 0) return null;
|
||||
T[] result = new T[items.Length];
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
T newitem = new T();
|
||||
newitem.Init(pso, ref items[i]);
|
||||
result[i] = newitem;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] GetByteArray(PsoFile pso, PsoStructureEntryInfo entry, int offset)
|
||||
{
|
||||
var aCount = (entry.ReferenceKey >> 16) & 0x0000FFFF;
|
||||
var aBlockId = (int)entry.ReferenceKey & 0x0000FFFF;
|
||||
var block = pso.GetBlock(aBlockId);
|
||||
if (block == null) return null;
|
||||
|
||||
//block.Offset
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static PsoPOINTER[] GetPointerArray(PsoFile pso, Array_StructurePointer array)
|
||||
{
|
||||
uint count = array.Count1;
|
||||
if (count == 0) return null;
|
||||
|
||||
int ptrsize = Marshal.SizeOf(typeof(MetaPOINTER));
|
||||
int itemsleft = (int)count; //large arrays get split into chunks...
|
||||
uint ptr = array.Pointer;
|
||||
int ptrindex = (int)(ptr & 0xFFF) - 1;
|
||||
int ptroffset = (int)((ptr >> 12) & 0xFFFFF);
|
||||
var ptrblock = (ptrindex < pso.DataMapSection.EntriesCount) ? pso.DataMapSection.Entries[ptrindex] : null;
|
||||
if ((ptrblock == null) || (ptrblock.NameHash != MetaName.PsoPOINTER))
|
||||
{ return null; }
|
||||
|
||||
var offset = ptrblock.Offset;
|
||||
int boffset = offset + ptroffset;
|
||||
|
||||
var ptrs = ConvertDataArrayRaw<PsoPOINTER>(pso.DataSection.Data, boffset, (int)count);
|
||||
if (ptrs != null)
|
||||
{
|
||||
for (int i = 0; i < ptrs.Length; i++)
|
||||
{
|
||||
ptrs[i].SwapEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return ptrs;
|
||||
}
|
||||
|
||||
|
||||
public static T[] ConvertDataArray<T>(PsoFile pso, Array_StructurePointer array) where T : struct, IPsoSwapEnd
|
||||
{
|
||||
uint count = array.Count1;
|
||||
if (count == 0) return null;
|
||||
PsoPOINTER[] ptrs = GetPointerArray(pso, array);
|
||||
if (ptrs == null) return null;
|
||||
if (ptrs.Length < count)
|
||||
{ return null; }
|
||||
|
||||
T[] items = new T[count];
|
||||
int itemsize = Marshal.SizeOf(typeof(T));
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var sptr = ptrs[i];
|
||||
int blocki = sptr.BlockID - 1;
|
||||
int offset = (int)sptr.ItemOffset;// * 16;//block data size...
|
||||
if (blocki >= pso.DataMapSection.EntriesCount)
|
||||
{ continue; }
|
||||
var block = pso.DataMapSection.Entries[blocki];
|
||||
|
||||
if ((offset < 0) || (offset >= block.Length))
|
||||
{ continue; }
|
||||
|
||||
int boffset = block.Offset + offset;
|
||||
|
||||
items[i] = ConvertData<T>(pso.DataSection.Data, boffset);
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static string GetString(PsoFile pso, CharPointer ptr)
|
||||
{
|
||||
if (ptr.Count1 == 0) return null;
|
||||
|
||||
var blocki = (int)ptr.PointerDataId;// (ptr.Pointer & 0xFFF) - 1;
|
||||
var offset = (int)ptr.PointerDataOffset;// (ptr.Pointer >> 12) & 0xFFFFF;
|
||||
|
||||
var block = pso.GetBlock(blocki);
|
||||
if (block == null)
|
||||
{ return null; }
|
||||
|
||||
var length = ptr.Count1;
|
||||
var lastbyte = offset + length;
|
||||
if (lastbyte >= block.Length)
|
||||
{ return null; }
|
||||
|
||||
var data = pso.DataSection?.Data;
|
||||
if (data == null)
|
||||
{ return null; }
|
||||
|
||||
var doffset = block.Offset + offset;
|
||||
|
||||
string s = Encoding.ASCII.GetString(data, doffset, length);
|
||||
|
||||
//if (meta.Strings == null) return null;
|
||||
//if (offset < 0) return null;
|
||||
//if (offset >= meta.Strings.Length) return null;
|
||||
//string s = meta.Strings[offset];
|
||||
|
||||
return s;
|
||||
}
|
||||
public static string GetString(PsoFile pso, DataBlockPointer ptr)
|
||||
{
|
||||
var blocki = (int)ptr.PointerDataId;// (ptr.Pointer & 0xFFF) - 1;
|
||||
var offset = (int)ptr.PointerDataOffset;// (ptr.Pointer >> 12) & 0xFFFFF;
|
||||
|
||||
var block = pso.GetBlock(blocki);
|
||||
if (block == null)
|
||||
{ return null; }
|
||||
|
||||
//var length = ptr.Count1;
|
||||
//var lastbyte = offset + length;
|
||||
//if (lastbyte >= block.Length)
|
||||
//{ return null; }
|
||||
|
||||
var data = pso.DataSection?.Data;
|
||||
if (data == null)
|
||||
{ return null; }
|
||||
|
||||
//var doffset = block.Offset + offset;
|
||||
|
||||
//string s = Encoding.ASCII.GetString(data, doffset, length);
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
var o = block.Offset + offset;
|
||||
char c = (char)data[o];
|
||||
while (c != 0)
|
||||
{
|
||||
sb.Append(c);
|
||||
o++;
|
||||
c = (char)data[o];
|
||||
}
|
||||
var s = sb.ToString();
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public interface IPsoSwapEnd
|
||||
{
|
||||
void SwapEnd();
|
||||
}
|
||||
|
||||
public abstract class PsoClass<T> where T : struct, IPsoSwapEnd
|
||||
{
|
||||
public abstract void Init(PsoFile pso, ref T v);
|
||||
}
|
||||
|
||||
|
||||
public struct PsoChar64
|
||||
{
|
||||
public byte b00, b01, b02, b03, b04, b05, b06, b07, b08, b09,
|
||||
b10, b11, b12, b13, b14, b15, b16, b17, b18, b19,
|
||||
b20, b21, b22, b23, b24, b25, b26, b27, b28, b29,
|
||||
b30, b31, b32, b33, b34, b35, b36, b37, b38, b39,
|
||||
b40, b41, b42, b43, b44, b45, b46, b47, b48, b49,
|
||||
b50, b51, b52, b53, b54, b55, b56, b57, b58, b59,
|
||||
b60, b61, b62, b63;
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
byte[] bytes = new byte[]
|
||||
{
|
||||
b00, b01, b02, b03, b04, b05, b06, b07, b08, b09,
|
||||
b10, b11, b12, b13, b14, b15, b16, b17, b18, b19,
|
||||
b20, b21, b22, b23, b24, b25, b26, b27, b28, b29,
|
||||
b30, b31, b32, b33, b34, b35, b36, b37, b38, b39,
|
||||
b40, b41, b42, b43, b44, b45, b46, b47, b48, b49,
|
||||
b50, b51, b52, b53, b54, b55, b56, b57, b58, b59,
|
||||
b60, b61, b62, b63
|
||||
};
|
||||
return Encoding.ASCII.GetString(bytes).Replace("\0", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[TC(typeof(EXP))] public struct PsoPOINTER : IPsoSwapEnd //8 bytes - pointer to data item
|
||||
{
|
||||
public uint Pointer { get; set; }
|
||||
public uint Unk2 { get; set; }
|
||||
|
||||
public ushort BlockID { get { return (ushort)(Pointer & 0xFFF); } } //1-based ID
|
||||
public uint ItemOffset { get { return ((Pointer>>12) & 0xFFFFF); } } //byte offset
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return BlockID.ToString() + ", " + ItemOffset.ToString() + ", " + Unk2.ToString();
|
||||
}
|
||||
|
||||
public void SwapEnd()
|
||||
{
|
||||
Pointer = MetaTypes.SwapBytes(Pointer);
|
||||
Unk2 = MetaTypes.SwapBytes(Unk2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//Struct infos
|
||||
[TC(typeof(EXP))] public struct CPackFileMetaData : IPsoSwapEnd //96 bytes, Type:0
|
||||
{
|
||||
public Array_Structure MapDataGroups; //0 MapDataGroups: Array: 0 {256: Structure: 0: 3260758307}
|
||||
public Array_Structure HDTxdBindingArray; //16 HDTxdBindingArray: Array: 16: 2 {256: Structure: 0: CHDTxdAssetBinding}
|
||||
public Array_Structure imapDependencies; //32 imapDependencies: Array: 32: 4 {256: Structure: 0: 3501026914}
|
||||
public Array_Structure imapDependencies_2; //48 imapDependencies_2: Array: 48: 6 {256: Structure: 0: 3240050401}
|
||||
public Array_Structure itypDependencies_2; //64 itypDependencies_2: Array: 64: 8 {256: Structure: 0: 1515605584}
|
||||
public Array_Structure Interiors; //80 Interiors: Array: 80: 10 {256: Structure: 0: 741495440}
|
||||
|
||||
public void SwapEnd()
|
||||
{
|
||||
MapDataGroups.SwapEnd();
|
||||
HDTxdBindingArray.SwapEnd();
|
||||
imapDependencies.SwapEnd();
|
||||
imapDependencies_2.SwapEnd();
|
||||
itypDependencies_2.SwapEnd();
|
||||
Interiors.SwapEnd();
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct CMapDataGroup : IPsoSwapEnd //56 bytes, Type:0
|
||||
{
|
||||
public MetaHash Name { get; set; } //0 Name: INT_0Bh: 0
|
||||
public uint Unused0 { get; set; } //4
|
||||
public Array_uint Bounds { get; set; } //8 Bounds//3298223272: Array: 8: 1 {256: INT_0Bh: 0}
|
||||
public ushort Flags { get; set; } //24 Flags: SHORT_0Fh: 24: 2097155
|
||||
public ushort Unused1 { get; set; }//26
|
||||
public uint Unused2 { get; set; }//28
|
||||
public Array_uint WeatherTypes { get; set; } //32 WeatherTypes: Array: 32: 5 {256: INT_0Bh: 0}
|
||||
public uint HoursOnOff { get; set; } //48 HoursOnOff//4190815249: INT_06h: 48
|
||||
public uint Unused3 { get; set; }//52
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name.ToString() + ": ybn:" + Bounds.Count1.ToString() + ", wt:" + WeatherTypes.Count1.ToString() + ", flags:" + Flags.ToString() + ", hours:" + HoursOnOff.ToString();
|
||||
}
|
||||
|
||||
public void SwapEnd()
|
||||
{
|
||||
Name = new MetaHash(MetaTypes.SwapBytes(Name.Hash));
|
||||
var b = Bounds; b.SwapEnd(); Bounds = b;
|
||||
var w = WeatherTypes; w.SwapEnd(); WeatherTypes = w;
|
||||
HoursOnOff = MetaTypes.SwapBytes(HoursOnOff);
|
||||
Flags = MetaTypes.SwapBytes(Flags);
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct CHDTxdAssetBinding : IPsoSwapEnd //132 bytes, Type:0
|
||||
{
|
||||
public byte assetType { get; set; } //0 assetType: BYTE_ENUM_VALUE: 0: 3387532954
|
||||
public byte Unused01 { get; set; }//1
|
||||
public ushort Unused02 { get; set; }//2
|
||||
public PsoChar64 targetAsset { get; set; } //4 targetAsset: INT_0Bh: 4: 4194304
|
||||
public PsoChar64 HDTxd { get; set; } //68 HDTxd: INT_0Bh: 68: 4194304
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return assetType.ToString() + ": " + targetAsset.ToString() + ": " + HDTxd.ToString();
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
//targetAsset.Hash = MetaTypes.SwapBytes(targetAsset.Hash);
|
||||
//HDTxd.Hash = MetaTypes.SwapBytes(HDTxd.Hash);
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct CImapDependency : IPsoSwapEnd //12 bytes, Type:0 // CImapDependency//3501026914
|
||||
{
|
||||
public MetaHash imapName { get; set; } //0 imapName: INT_0Bh: 0
|
||||
public MetaHash itypName { get; set; } //4 itypName//2890158180: INT_0Bh: 4
|
||||
public MetaHash packFileName { get; set; } //8 packFileName//4216494073: INT_0Bh: 8
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return imapName.ToString() + ", " + itypName.ToString() + ", " + packFileName.ToString();
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
imapName = new MetaHash(MetaTypes.SwapBytes(imapName.Hash));
|
||||
itypName = new MetaHash(MetaTypes.SwapBytes(itypName.Hash));
|
||||
packFileName = new MetaHash(MetaTypes.SwapBytes(packFileName.Hash));
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct CImapDependencies : IPsoSwapEnd //24 bytes, Type:0 // CImapDependencies//3240050401 imapDependencies_2
|
||||
{
|
||||
public MetaHash imapName { get; set; } //0 imapName: INT_0Bh: 0 //name hash
|
||||
public ushort manifestFlags { get; set; } //4 manifestFlags//1683136603: SHORT_0Fh: 4: 2097153
|
||||
public ushort Unused0 { get; set; } //6
|
||||
public Array_uint itypDepArray { get; set; } //8 itypDepArray//2410949350: Array: 8: 3 {256: INT_0Bh: 0} //children...
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return imapName.ToString() + ": " + manifestFlags.ToString() + ": " + itypDepArray.ToString();
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
imapName = new MetaHash(MetaTypes.SwapBytes(imapName.Hash));
|
||||
manifestFlags = MetaTypes.SwapBytes(manifestFlags);
|
||||
var d = itypDepArray; d.SwapEnd(); itypDepArray = d;
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct CItypDependencies : IPsoSwapEnd //24 bytes, Type:0 // CItypDependencies//1515605584 itypDependencies_2
|
||||
{
|
||||
public MetaHash itypName { get; set; } //0 itypName//2890158180: INT_0Bh: 0
|
||||
public ushort manifestFlags { get; set; } //4 manifestFlags//1683136603: SHORT_0Fh: 4: 2097153
|
||||
public ushort Unused0 { get; set; } //6
|
||||
public Array_uint itypDepArray { get; set; } //8 itypDepArray//2410949350: Array: 8: 3 {256: INT_0Bh: 0}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return itypName.ToString() + ": " + manifestFlags.ToString() + ": " + itypDepArray.ToString();
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
itypName = new MetaHash(MetaTypes.SwapBytes(itypName.Hash));
|
||||
manifestFlags = MetaTypes.SwapBytes(manifestFlags);
|
||||
var d = itypDepArray; d.SwapEnd(); itypDepArray = d;
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct Unk_741495440 : IPsoSwapEnd //24 bytes, Type:0 // Interiors
|
||||
{
|
||||
public MetaHash Name { get; set; } //0 Name: INT_0Bh: 0
|
||||
public uint Unused0 { get; set; } //4
|
||||
public Array_uint Bounds { get; set; } //8 Bounds//3298223272: Array: 8: 1 {256: INT_0Bh: 0}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return JenkIndex.GetString(Name);
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
Name = new MetaHash(MetaTypes.SwapBytes(Name.Hash));
|
||||
var b = Bounds; b.SwapEnd(); Bounds = b;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[TC(typeof(EXP))] public struct CScenarioPointManifest : IPsoSwapEnd //56 bytes, Type:0
|
||||
{
|
||||
public int VersionNumber { get; set; } //0 VersionNumber: INT_05h: 0
|
||||
public uint Unused0 { get; set; } //4
|
||||
public Array_StructurePointer RegionDefs { get; set; } //8 RegionDefs: Array: 8: 1 {ARRAYINFO: Structure: 0}
|
||||
public Array_StructurePointer Groups { get; set; } //24 Groups: Array: 24: 3 {ARRAYINFO: Structure: 0}
|
||||
public Array_uint InteriorNames { get; set; } //40 InteriorNames: Array: 40: 5 {ARRAYINFO: INT_0Bh: 0}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return VersionNumber.ToString();
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
VersionNumber = MetaTypes.SwapBytes(VersionNumber);
|
||||
var r = RegionDefs; r.SwapEnd(); RegionDefs = r;
|
||||
var g = Groups; g.SwapEnd(); Groups = g;
|
||||
var i = InteriorNames; i.SwapEnd(); InteriorNames = i;
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct CScenarioPointRegionDef : IPsoSwapEnd //64 bytes, Type:0
|
||||
{
|
||||
public MetaHash Name { get; set; } //0 Name: INT_0Bh: 0
|
||||
public uint Unused0 { get; set; } //4
|
||||
public uint Unused1 { get; set; } //8
|
||||
public uint Unused2 { get; set; } //12
|
||||
public rage__spdAABB AABB { get; set; } //16 AABB: Structure: 16: rage__spdAABB
|
||||
public uint Unused3 { get; set; } //48
|
||||
public uint Unused4 { get; set; } //52
|
||||
public uint Unused5 { get; set; } //56
|
||||
public uint Unused6 { get; set; } //60
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name.ToString() + ", " + AABB.ToString();
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
Name = new MetaHash(MetaTypes.SwapBytes(Name.Hash));
|
||||
var aabb = AABB; aabb.SwapEnd(); AABB = aabb;
|
||||
}
|
||||
}
|
||||
|
||||
[TC(typeof(EXP))] public struct CScenarioPointGroup : IPsoSwapEnd //8 bytes, Type:0
|
||||
{
|
||||
public MetaHash Name { get; set; } //0 Name: INT_0Bh: 0
|
||||
public byte EnabledByDefault { get; set; } //4 EnabledByDefault: BYTE_00h: 4
|
||||
public byte Unused0 { get; set; } //5
|
||||
public ushort Unused1 { get; set; } //6
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name.ToString();
|
||||
}
|
||||
public void SwapEnd()
|
||||
{
|
||||
Name = new MetaHash(MetaTypes.SwapBytes(Name.Hash));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
Copyright(c) 2015 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//shamelessly stolen and mangled
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFile
|
||||
{
|
||||
private const int RBF_IDENT = 0x30464252;
|
||||
|
||||
public RbfStructure current { get; set; }
|
||||
public Stack<RbfStructure> stack { get; set; }
|
||||
public List<RbfEntryDescription> descriptors { get; set; }
|
||||
|
||||
public RbfStructure Load(string fileName)
|
||||
{
|
||||
using (var fileStream = new FileStream(fileName, FileMode.Open))
|
||||
{
|
||||
return Load(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
public RbfStructure Load(Stream stream)
|
||||
{
|
||||
stack = new Stack<RbfStructure>();
|
||||
descriptors = new List<RbfEntryDescription>();
|
||||
|
||||
var reader = new DataReader(stream);
|
||||
var ident = reader.ReadInt32();
|
||||
if (ident != RBF_IDENT)
|
||||
throw new Exception("The file identifier does not match.");
|
||||
|
||||
while (reader.Position < reader.Length)
|
||||
{
|
||||
var descriptorIndex = reader.ReadByte();
|
||||
if (descriptorIndex == 0xFF) // close tag
|
||||
{
|
||||
var b = reader.ReadByte();
|
||||
if (b != 0xFF)
|
||||
throw new Exception("Expected 0xFF but was " + b.ToString("X2"));
|
||||
|
||||
if (stack.Count > 0)
|
||||
{
|
||||
current = stack.Pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reader.Position != reader.Length)
|
||||
throw new Exception("Expected end of stream but was not.");
|
||||
return current;
|
||||
}
|
||||
}
|
||||
else if (descriptorIndex == 0xFD) // bytes
|
||||
{
|
||||
var b = reader.ReadByte();
|
||||
if (b != 0xFF)
|
||||
throw new Exception("Expected 0xFF but was " + b.ToString("X2"));
|
||||
|
||||
var dataLength = reader.ReadInt32();
|
||||
var data = reader.ReadBytes(dataLength);
|
||||
|
||||
var bytesValue = new RbfBytes();
|
||||
bytesValue.Value = data;
|
||||
current.Children.Add(bytesValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dataType = reader.ReadByte();
|
||||
if (descriptorIndex == descriptors.Count) // new descriptor + data
|
||||
{
|
||||
var nameLength = reader.ReadInt16();
|
||||
var nameBytes = reader.ReadBytes(nameLength);
|
||||
var name = Encoding.ASCII.GetString(nameBytes);
|
||||
|
||||
var descriptor = new RbfEntryDescription();
|
||||
descriptor.Name = name;
|
||||
descriptor.Type = dataType;
|
||||
descriptors.Add(descriptor);
|
||||
|
||||
ParseElement(reader, descriptors.Count - 1, dataType);
|
||||
}
|
||||
else // existing descriptor + data
|
||||
{
|
||||
if (dataType != descriptors[descriptorIndex].Type)
|
||||
{
|
||||
//throw new Exception("Data type does not match. Expected "
|
||||
// + descriptors[descriptorIndex].Type.ToString() + " but found "
|
||||
// + dataType.ToString() + ". Descriptor: " + descriptors[descriptorIndex].Name);
|
||||
}
|
||||
|
||||
ParseElement(reader, descriptorIndex, dataType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Unexpected end of stream.");
|
||||
}
|
||||
|
||||
private void ParseElement(DataReader reader, int descriptorIndex, byte dataType)
|
||||
{
|
||||
var descriptor = descriptors[descriptorIndex];
|
||||
switch (dataType) //(descriptor.Type)
|
||||
{
|
||||
case 0: // open element...
|
||||
{
|
||||
var structureValue = new RbfStructure();
|
||||
structureValue.Name = descriptor.Name;
|
||||
|
||||
if (current != null)
|
||||
{
|
||||
current.Children.Add(structureValue);
|
||||
stack.Push(current);
|
||||
}
|
||||
|
||||
current = structureValue;
|
||||
|
||||
// 6 bytes
|
||||
var x1 = reader.ReadInt16();
|
||||
var x2 = reader.ReadInt16();
|
||||
var x3 = reader.ReadInt16();
|
||||
//if (x1 != 0)
|
||||
// throw new Exception("unexpected");
|
||||
//if (x2 != 0)
|
||||
// throw new Exception("unexpected");
|
||||
//if (x3 != 0)
|
||||
// throw new Exception("unexpected");
|
||||
break;
|
||||
}
|
||||
case 0x10:
|
||||
{
|
||||
var intValue = new RbfUint32();
|
||||
intValue.Name = descriptor.Name;
|
||||
intValue.Value = reader.ReadUInt32();
|
||||
current.Children.Add(intValue);
|
||||
break;
|
||||
}
|
||||
case 0x20:
|
||||
{
|
||||
var booleanValue = new RbfBoolean();
|
||||
booleanValue.Name = descriptor.Name;
|
||||
booleanValue.Value = true;
|
||||
current.Children.Add(booleanValue);
|
||||
break;
|
||||
}
|
||||
case 0x30:
|
||||
{
|
||||
var booleanValue = new RbfBoolean();
|
||||
booleanValue.Name = descriptor.Name;
|
||||
booleanValue.Value = false;
|
||||
current.Children.Add(booleanValue);
|
||||
break;
|
||||
}
|
||||
case 0x40:
|
||||
{
|
||||
var floatValue = new RbfFloat();
|
||||
floatValue.Name = descriptor.Name;
|
||||
floatValue.Value = reader.ReadSingle();
|
||||
current.Children.Add(floatValue);
|
||||
break;
|
||||
}
|
||||
case 0x50:
|
||||
{
|
||||
var floatVectorValue = new RbfFloat3();
|
||||
floatVectorValue.Name = descriptor.Name;
|
||||
floatVectorValue.X = reader.ReadSingle();
|
||||
floatVectorValue.Y = reader.ReadSingle();
|
||||
floatVectorValue.Z = reader.ReadSingle();
|
||||
current.Children.Add(floatVectorValue);
|
||||
break;
|
||||
}
|
||||
case 0x60:
|
||||
{
|
||||
var valueLength = reader.ReadInt16();
|
||||
var valueBytes = reader.ReadBytes(valueLength);
|
||||
var value = Encoding.ASCII.GetString(valueBytes);
|
||||
var stringValue = new RbfString();
|
||||
stringValue.Name = descriptor.Name;
|
||||
stringValue.Value = value;
|
||||
current.Children.Add(stringValue);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Exception("Unsupported data type.");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsRBF(Stream stream)
|
||||
{
|
||||
var reader = new DataReader(stream);
|
||||
var origpos = stream.Position;
|
||||
var ident = reader.ReadInt32();
|
||||
var isrbf = (ident == RBF_IDENT);
|
||||
stream.Position = origpos;
|
||||
return isrbf;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfEntryDescription
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Type { get; set; }
|
||||
public override string ToString() { return Name + ": " + Type.ToString(); }
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public interface IRbfType
|
||||
{
|
||||
string Name { get; set; }
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBoolean : IRbfType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public bool Value { get; set; }
|
||||
public override string ToString() { return Name + ": " + Value.ToString(); }
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfBytes : IRbfType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public byte[] Value { get; set; }
|
||||
public override string ToString() { return Name + ": " + Value.ToString(); }
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat : IRbfType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public float Value { get; set; }
|
||||
public override string ToString() { return Name + ": " + Value.ToString(); }
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfFloat3 : IRbfType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public float X { get; set; }
|
||||
public float Y { get; set; }
|
||||
public float Z { get; set; }
|
||||
public override string ToString() { return string.Format("{0}: X:{1}, Y:{2}, Z:{3}", Name, X, Y, Z); }
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfString : IRbfType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Value { get; set; }
|
||||
public override string ToString() { return Name + ": " + Value.ToString(); }
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfStructure : IRbfType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public List<IRbfType> Children { get; set; } = new List<IRbfType>();
|
||||
public override string ToString() { return Name + ": {" + Children.Count.ToString() + "}"; }
|
||||
public IRbfType FindChild(string name)
|
||||
{
|
||||
foreach (var child in Children)
|
||||
{
|
||||
if (child == null) continue;
|
||||
if (child.Name == name) return child;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class RbfUint32 : IRbfType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public uint Value { get; set; }
|
||||
public override string ToString() { return Name + ": " + Value.ToString(); }
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,665 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using SharpDX;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class XmlMeta
|
||||
{
|
||||
public static Meta GetMeta(XmlDocument doc)
|
||||
{
|
||||
MetaBuilder mb = new MetaBuilder();
|
||||
|
||||
Traverse(doc.DocumentElement, mb, 0, true);
|
||||
|
||||
var meta = mb.GetMeta();
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
private static byte[] Traverse(XmlNode node, MetaBuilder mb, MetaName type = 0, bool isRoot = false)
|
||||
{
|
||||
if(type == 0)
|
||||
{
|
||||
type = (MetaName)(uint)GetHash(node.Name);
|
||||
}
|
||||
|
||||
var infos = MetaTypes.GetStructureInfo(type);
|
||||
|
||||
if (infos != null)
|
||||
{
|
||||
byte[] data = new byte[infos.StructureSize];
|
||||
var arrayResults = new ArrayResults();
|
||||
|
||||
arrayResults.Structures = new Dictionary<int, Array_Structure>();
|
||||
arrayResults.StructurePointers = new Dictionary<int, Array_StructurePointer>();
|
||||
arrayResults.UInts = new Dictionary<int, Array_uint>();
|
||||
arrayResults.UShorts = new Dictionary<int, Array_ushort>();
|
||||
arrayResults.UBytes = new Dictionary<int, Array_byte>();
|
||||
arrayResults.Floats = new Dictionary<int, Array_float>();
|
||||
arrayResults.Float_XYZs = new Dictionary<int, Array_Vector3>();
|
||||
arrayResults.Hashes = new Dictionary<int, Array_uint>();
|
||||
|
||||
Array.Clear(data, 0, infos.StructureSize);
|
||||
|
||||
MetaStructureEntryInfo_s arrEntry = new MetaStructureEntryInfo_s();
|
||||
|
||||
if (isRoot)
|
||||
{
|
||||
mb.EnsureBlock(type);
|
||||
}
|
||||
|
||||
for (int i = 0; i < infos.Entries.Length; i++)
|
||||
{
|
||||
var entry = infos.Entries[i];
|
||||
|
||||
var cnode = GetEntryNode(node.ChildNodes, entry);
|
||||
|
||||
if (entry.EntryNameHash == MetaName.ARRAYINFO)
|
||||
{
|
||||
arrEntry = entry;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cnode == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (entry.DataType)
|
||||
{
|
||||
case MetaStructureEntryDataType.Array:
|
||||
{
|
||||
TraverseArray(cnode, mb, arrEntry, entry.DataOffset, arrayResults);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.ArrayOfBytes:
|
||||
case MetaStructureEntryDataType.ArrayOfChars:
|
||||
{
|
||||
int offset = entry.DataOffset;
|
||||
var split = Split(cnode.InnerText, 2);
|
||||
|
||||
for (int j = 0; j < split.Length; j++)
|
||||
{
|
||||
byte val = Convert.ToByte(split[j], 16);
|
||||
data[offset] = val;
|
||||
offset += sizeof(byte);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.Boolean:
|
||||
{
|
||||
byte val = (cnode.Attributes["value"].Value == "false") ? (byte)0 : (byte)1;
|
||||
data[entry.DataOffset] = val;
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.ByteEnum:
|
||||
{
|
||||
byte val = Convert.ToByte(cnode.Attributes["value"].Value);
|
||||
data[entry.DataOffset] = val;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case MetaStructureEntryDataType.CharPointer:
|
||||
{
|
||||
if (!string.IsNullOrEmpty(cnode.InnerText))
|
||||
{
|
||||
var ptr = mb.AddStringPtr(cnode.InnerText);
|
||||
var val = MetaTypes.ConvertToBytes(ptr);
|
||||
|
||||
Buffer.BlockCopy(val, 0, data, entry.DataOffset, val.Length);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.DataBlockPointer:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.Float:
|
||||
{
|
||||
float val = FloatUtil.Parse(cnode.Attributes["value"].Value);
|
||||
Write(val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.Float_XYZ:
|
||||
{
|
||||
float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
|
||||
float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
|
||||
float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
|
||||
|
||||
Write(x, data, entry.DataOffset);
|
||||
Write(y, data, entry.DataOffset + sizeof(float));
|
||||
Write(z, data, entry.DataOffset + sizeof(float) * 2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case MetaStructureEntryDataType.Float_XYZW:
|
||||
{
|
||||
float x = FloatUtil.Parse(cnode.Attributes["x"].Value);
|
||||
float y = FloatUtil.Parse(cnode.Attributes["y"].Value);
|
||||
float z = FloatUtil.Parse(cnode.Attributes["z"].Value);
|
||||
float w = FloatUtil.Parse(cnode.Attributes["w"].Value);
|
||||
|
||||
Write(x, data, entry.DataOffset);
|
||||
Write(y, data, entry.DataOffset + sizeof(float));
|
||||
Write(z, data, entry.DataOffset + sizeof(float) * 2);
|
||||
Write(w, data, entry.DataOffset + sizeof(float) * 3);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.Hash:
|
||||
{
|
||||
var hash = GetHash(cnode.InnerText);
|
||||
Write(hash, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.IntEnum:
|
||||
case MetaStructureEntryDataType.IntFlags1:
|
||||
case MetaStructureEntryDataType.IntFlags2:
|
||||
{
|
||||
var _infos = MetaTypes.GetEnumInfo(entry.ReferenceKey);
|
||||
|
||||
mb.AddEnumInfo(_infos.EnumNameHash);
|
||||
|
||||
int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText);
|
||||
Write(val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.ShortFlags:
|
||||
{
|
||||
var _infos = MetaTypes.GetEnumInfo(entry.ReferenceKey);
|
||||
|
||||
mb.AddEnumInfo(_infos.EnumNameHash);
|
||||
|
||||
int val = GetEnumInt(entry.ReferenceKey, cnode.InnerText);
|
||||
Write((short)val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.SignedByte:
|
||||
{
|
||||
var val = Convert.ToSByte(cnode.Attributes["value"].Value);
|
||||
data[entry.DataOffset] = (byte)val;
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.SignedInt:
|
||||
{
|
||||
var val = Convert.ToInt32(cnode.Attributes["value"].Value);
|
||||
Write(val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.SignedShort:
|
||||
{
|
||||
var val = Convert.ToInt16(cnode.Attributes["value"].Value);
|
||||
Write(val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.Structure:
|
||||
{
|
||||
var struc = Traverse(cnode, mb, entry.ReferenceKey);
|
||||
|
||||
if(struc != null)
|
||||
{
|
||||
Buffer.BlockCopy(struc, 0, data, entry.DataOffset, struc.Length);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.StructurePointer:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.UnsignedByte:
|
||||
{
|
||||
var val = Convert.ToByte(cnode.Attributes["value"].Value);
|
||||
data[entry.DataOffset] = val;
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.UnsignedInt:
|
||||
{
|
||||
switch (entry.EntryNameHash)
|
||||
{
|
||||
case MetaName.color:
|
||||
{
|
||||
var val = Convert.ToUInt32(cnode.Attributes["value"].Value, 16);
|
||||
Write(val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
var val = Convert.ToUInt32(cnode.Attributes["value"].Value);
|
||||
Write(val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.UnsignedShort:
|
||||
{
|
||||
var val = Convert.ToUInt16(cnode.Attributes["value"].Value);
|
||||
Write(val, data, entry.DataOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
arrayResults.WriteArrays(data);
|
||||
|
||||
mb.AddStructureInfo(infos.StructureNameHash);
|
||||
|
||||
if (isRoot)
|
||||
{
|
||||
mb.AddItem(type, data);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void TraverseArray(XmlNode node, MetaBuilder mb, MetaStructureEntryInfo_s arrEntry, int offset, ArrayResults results)
|
||||
{
|
||||
switch (arrEntry.DataType)
|
||||
{
|
||||
case MetaStructureEntryDataType.Structure:
|
||||
{
|
||||
results.Structures[offset] = TraverseArrayStructure(node, mb, arrEntry.ReferenceKey);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.StructurePointer:
|
||||
{
|
||||
results.StructurePointers[offset] = TraverseArrayStructurePointer(node, mb);
|
||||
break;
|
||||
}
|
||||
|
||||
case MetaStructureEntryDataType.UnsignedInt:
|
||||
{
|
||||
results.UInts[offset] = TraverseRawUIntArray(node, mb);
|
||||
break;
|
||||
}
|
||||
case MetaStructureEntryDataType.UnsignedShort:
|
||||
{
|
||||
results.UShorts[offset] = TraverseRawUShortArray(node, mb);
|
||||
break;
|
||||
}
|
||||
case MetaStructureEntryDataType.UnsignedByte:
|
||||
{
|
||||
results.UBytes[offset] = TraverseRawUByteArray(node, mb);
|
||||
break;
|
||||
}
|
||||
case MetaStructureEntryDataType.Float:
|
||||
{
|
||||
results.Floats[offset] = TraverseRawFloatArray(node, mb);
|
||||
break;
|
||||
}
|
||||
case MetaStructureEntryDataType.Float_XYZ:
|
||||
{
|
||||
results.Float_XYZs[offset] = TraverseRawVector3Array(node, mb);
|
||||
break;
|
||||
}
|
||||
case MetaStructureEntryDataType.Hash:
|
||||
{
|
||||
results.Hashes[offset] = TraverseHashArray(node, mb);
|
||||
break;
|
||||
}
|
||||
case MetaStructureEntryDataType.CharPointer:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
case MetaStructureEntryDataType.DataBlockPointer:
|
||||
{
|
||||
// TODO
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static Array_Structure TraverseArrayStructure(XmlNode node, MetaBuilder mb, MetaName type)
|
||||
{
|
||||
var strucs = new List<byte[]>();
|
||||
|
||||
foreach (XmlNode cnode in node.ChildNodes)
|
||||
{
|
||||
var struc = Traverse(cnode, mb, type);
|
||||
|
||||
if (struc != null)
|
||||
{
|
||||
strucs.Add(struc);
|
||||
}
|
||||
}
|
||||
|
||||
return mb.AddItemArrayPtr(type, strucs.ToArray());
|
||||
}
|
||||
|
||||
private static Array_StructurePointer TraverseArrayStructurePointer(XmlNode node, MetaBuilder mb)
|
||||
{
|
||||
var ptrs = new List<MetaPOINTER>();
|
||||
|
||||
foreach (XmlNode cnode in node.ChildNodes)
|
||||
{
|
||||
var type = (MetaName)(uint)GetHash(cnode.Attributes["type"].Value);
|
||||
var struc = Traverse(cnode, mb, type);
|
||||
|
||||
if(struc != null)
|
||||
{
|
||||
var ptr = mb.AddItemPtr(type, struc);
|
||||
ptrs.Add(ptr);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return mb.AddPointerArray(ptrs.ToArray());
|
||||
|
||||
}
|
||||
|
||||
private static Array_uint TraverseRawUIntArray(XmlNode node, MetaBuilder mb)
|
||||
{
|
||||
var data = new List<uint>();
|
||||
|
||||
if (node.InnerText != "")
|
||||
{
|
||||
var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
|
||||
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(split[i]))
|
||||
{
|
||||
var val = Convert.ToUInt32(split[i]);
|
||||
data.Add(val);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return mb.AddUintArrayPtr(data.ToArray());
|
||||
}
|
||||
|
||||
private static Array_ushort TraverseRawUShortArray(XmlNode node, MetaBuilder mb)
|
||||
{
|
||||
var data = new List<ushort>();
|
||||
|
||||
if (node.InnerText != "")
|
||||
{
|
||||
var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
|
||||
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(split[i]))
|
||||
{
|
||||
var val = Convert.ToUInt16(split[i]);
|
||||
data.Add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mb.AddUshortArrayPtr(data.ToArray());
|
||||
}
|
||||
|
||||
private static Array_byte TraverseRawUByteArray(XmlNode node, MetaBuilder mb)
|
||||
{
|
||||
var data = new List<byte>();
|
||||
|
||||
if (node.InnerText != "")
|
||||
{
|
||||
var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
|
||||
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(split[i]))
|
||||
{
|
||||
var val = Convert.ToByte(split[i]);
|
||||
data.Add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mb.AddByteArrayPtr(data.ToArray());
|
||||
}
|
||||
|
||||
private static Array_float TraverseRawFloatArray(XmlNode node, MetaBuilder mb)
|
||||
{
|
||||
var data = new List<float>();
|
||||
|
||||
if(node.InnerText != "")
|
||||
{
|
||||
var split = Regex.Split(node.InnerText, @"[\s\r\n\t]");
|
||||
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(split[i]))
|
||||
{
|
||||
var val = Convert.ToSingle(split[i]);
|
||||
data.Add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mb.AddFloatArrayPtr(data.ToArray());
|
||||
}
|
||||
|
||||
private static Array_Vector3 TraverseRawVector3Array(XmlNode node, MetaBuilder mb)
|
||||
{
|
||||
var items = new List<Vector4>();
|
||||
|
||||
foreach (XmlNode cnode in node.ChildNodes)
|
||||
{
|
||||
|
||||
var split = Regex.Split(node.InnerText, @",\s");
|
||||
|
||||
float x = FloatUtil.Parse(split[0]);
|
||||
float y = FloatUtil.Parse(split[1]);
|
||||
float z = FloatUtil.Parse(split[2]);
|
||||
float w = 0f;
|
||||
|
||||
var val = new Vector4(x, y, z, w);
|
||||
|
||||
items.Add(val);
|
||||
break;
|
||||
}
|
||||
|
||||
return mb.AddPaddedVector3ArrayPtr(items.ToArray());
|
||||
}
|
||||
|
||||
private static Array_uint TraverseHashArray(XmlNode node, MetaBuilder mb)
|
||||
{
|
||||
var items = new List<MetaHash>();
|
||||
|
||||
foreach (XmlNode cnode in node.ChildNodes)
|
||||
{
|
||||
var val = GetHash(cnode.InnerText);
|
||||
items.Add(val);
|
||||
}
|
||||
|
||||
return mb.AddHashArrayPtr(items.ToArray());
|
||||
}
|
||||
|
||||
private static void Write(int val, byte[] data, int offset)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(val);
|
||||
Buffer.BlockCopy(bytes, 0, data, offset, sizeof(int));
|
||||
}
|
||||
|
||||
private static void Write(uint val, byte[] data, int offset)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(val);
|
||||
Buffer.BlockCopy(bytes, 0, data, offset, sizeof(uint));
|
||||
}
|
||||
|
||||
private static void Write(short val, byte[] data, int offset)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(val);
|
||||
Buffer.BlockCopy(bytes, 0, data, offset, sizeof(short));
|
||||
}
|
||||
|
||||
private static void Write(ushort val, byte[] data, int offset)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(val);
|
||||
Buffer.BlockCopy(bytes, 0, data, offset, sizeof(ushort));
|
||||
}
|
||||
|
||||
private static void Write(float val, byte[] data, int offset)
|
||||
{
|
||||
byte[] bytes = BitConverter.GetBytes(val);
|
||||
Buffer.BlockCopy(bytes, 0, data, offset, sizeof(float));
|
||||
}
|
||||
|
||||
private static MetaHash GetHash(string str)
|
||||
{
|
||||
if (str.StartsWith("hash_"))
|
||||
{
|
||||
return (MetaHash) Convert.ToUInt32(str.Substring(5), 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
return JenkHash.GenHash(str);
|
||||
}
|
||||
}
|
||||
|
||||
private static XmlNode GetEntryNode(XmlNodeList nodes, MetaStructureEntryInfo_s entry)
|
||||
{
|
||||
foreach (XmlNode node in nodes)
|
||||
{
|
||||
if (GetHash(node.Name) == (uint)entry.EntryNameHash)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string[] Split(string str, int maxChunkSize)
|
||||
{
|
||||
var chunks = new List<String>();
|
||||
|
||||
for (int i = 0; i < str.Length; i += maxChunkSize)
|
||||
{
|
||||
chunks.Add(str.Substring(i, Math.Min(maxChunkSize, str.Length - i)));
|
||||
}
|
||||
|
||||
return chunks.ToArray();
|
||||
}
|
||||
|
||||
private static int GetEnumInt(MetaName type, string enumString)
|
||||
{
|
||||
var enumName = (MetaName)(uint)GetHash(enumString);
|
||||
var infos = MetaTypes.GetEnumInfo(type);
|
||||
|
||||
if (infos == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int j = 0; j < infos.Entries.Length; j++)
|
||||
{
|
||||
var entry = infos.Entries[j];
|
||||
|
||||
if (entry.EntryNameHash == enumName)
|
||||
{
|
||||
return entry.EntryValue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct ArrayResults
|
||||
{
|
||||
public Dictionary<int, Array_Structure> Structures;
|
||||
public Dictionary<int, Array_StructurePointer> StructurePointers;
|
||||
public Dictionary<int, Array_uint> UInts;
|
||||
public Dictionary<int, Array_ushort> UShorts;
|
||||
public Dictionary<int, Array_byte> UBytes;
|
||||
public Dictionary<int, Array_float> Floats;
|
||||
public Dictionary<int, Array_Vector3> Float_XYZs;
|
||||
public Dictionary<int, Array_uint> Hashes;
|
||||
|
||||
public void WriteArrays(byte[] data)
|
||||
{
|
||||
foreach (KeyValuePair<int, Array_Structure> ptr in Structures)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, Array_StructurePointer> ptr in StructurePointers)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, Array_uint> ptr in UInts)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, Array_ushort> ptr in UShorts)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, Array_byte> ptr in UBytes)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, Array_float> ptr in Floats)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, Array_Vector3> ptr in Float_XYZs)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<int, Array_uint> ptr in Hashes)
|
||||
{
|
||||
var _data = MetaTypes.ConvertToBytes(ptr.Value);
|
||||
Buffer.BlockCopy(_data, 0, data, ptr.Key, _data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,812 @@
|
||||
/*
|
||||
Copyright(c) 2016 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//now with enhanced uglification for codewalker
|
||||
|
||||
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMesh : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 368; }
|
||||
}
|
||||
|
||||
|
||||
public NavMeshFlags ContentFlags { get; set; }
|
||||
public uint VersionUnk1 { get; set; } // 0x00010011
|
||||
public uint Unused_018h { get; set; } // 0x00000000
|
||||
public uint Unused_01Ch { get; set; } // 0x00000000
|
||||
public Matrix Transform { get; set; } //(1,0,0,NaN),(0,1,0,NaN),(0,0,1,NaN),(0,0,0,NaN)
|
||||
public Vector3 AABBSize { get; set; }
|
||||
public float AABBUnk { get; set; } // 0x7F800001 //NaN
|
||||
public ulong VerticesPointer { get; set; }
|
||||
public uint Unused_078h { get; set; } // 0x00000000
|
||||
public uint Unused_07Ch { get; set; } // 0x00000000
|
||||
public ulong IndicesPointer { get; set; }
|
||||
public ulong AdjPolysPointer { get; set; }
|
||||
public uint AdjPolysIndicesCount { get; set; }
|
||||
public NavMeshUintArray AdjAreaIDs { get; set; }
|
||||
public ulong PolysPointer { get; set; }
|
||||
public ulong SectorTreePointer { get; set; }
|
||||
public ulong PortalsPointer { get; set; }
|
||||
public ulong PortalLinksPointer { get; set; }
|
||||
public uint VerticesCount { get; set; }
|
||||
public uint PolysCount { get; set; }
|
||||
public uint AreaID { get; set; } // X + Y*100
|
||||
public uint TotalBytes { get; set; }
|
||||
public uint SectorUnkCount { get; set; }
|
||||
public uint PortalsCount { get; set; }
|
||||
public uint PortalLinksCount { get; set; }
|
||||
public uint Unused_154h { get; set; } // 0x00000000
|
||||
public uint Unused_158h { get; set; } // 0x00000000
|
||||
public uint Unused_15Ch { get; set; } // 0x00000000
|
||||
public uint VersionUnk2 { get; set; } //2244687201 (0x85CB3561) for grid ynv's
|
||||
public uint Unused_164h { get; set; } // 0x00000000
|
||||
public uint Unused_168h { get; set; } // 0x00000000
|
||||
public uint Unused_16Ch { get; set; } // 0x00000000
|
||||
|
||||
|
||||
public NavMeshList<NavMeshVertex> Vertices { get; set; }
|
||||
public NavMeshList<ushort> Indices { get; set; }
|
||||
public NavMeshList<NavMeshAdjPoly> AdjPolys { get; set; }
|
||||
public NavMeshList<NavMeshPoly> Polys { get; set; }
|
||||
public NavMeshSector SectorTree { get; set; }
|
||||
public NavMeshPortal[] Portals { get; set; }
|
||||
public ushort[] PortalLinks { get; set; }
|
||||
|
||||
|
||||
|
||||
|
||||
private ResourceSystemStructBlock<NavMeshPortal> PortalsBlock = null;
|
||||
private ResourceSystemStructBlock<ushort> PortalLinksBlock = null;
|
||||
|
||||
|
||||
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
ContentFlags = (NavMeshFlags)reader.ReadUInt32();
|
||||
VersionUnk1 = reader.ReadUInt32();
|
||||
Unused_018h = reader.ReadUInt32();
|
||||
Unused_01Ch = reader.ReadUInt32();
|
||||
Transform = reader.ReadMatrix();
|
||||
AABBSize = reader.ReadVector3();
|
||||
AABBUnk = reader.ReadSingle();
|
||||
VerticesPointer = reader.ReadUInt64();
|
||||
Unused_078h = reader.ReadUInt32();
|
||||
Unused_07Ch = reader.ReadUInt32();
|
||||
IndicesPointer = reader.ReadUInt64();
|
||||
AdjPolysPointer = reader.ReadUInt64();
|
||||
AdjPolysIndicesCount = reader.ReadUInt32();
|
||||
AdjAreaIDs = reader.ReadStruct<NavMeshUintArray>();
|
||||
PolysPointer = reader.ReadUInt64();
|
||||
SectorTreePointer = reader.ReadUInt64();
|
||||
PortalsPointer = reader.ReadUInt64();
|
||||
PortalLinksPointer = reader.ReadUInt64();
|
||||
VerticesCount = reader.ReadUInt32();
|
||||
PolysCount = reader.ReadUInt32();
|
||||
AreaID = reader.ReadUInt32();
|
||||
TotalBytes = reader.ReadUInt32();
|
||||
SectorUnkCount = reader.ReadUInt32();
|
||||
PortalsCount = reader.ReadUInt32();
|
||||
PortalLinksCount = reader.ReadUInt32();
|
||||
Unused_154h = reader.ReadUInt32();
|
||||
Unused_158h = reader.ReadUInt32();
|
||||
Unused_15Ch = reader.ReadUInt32();
|
||||
VersionUnk2 = reader.ReadUInt32();
|
||||
Unused_164h = reader.ReadUInt32();
|
||||
Unused_168h = reader.ReadUInt32();
|
||||
Unused_16Ch = reader.ReadUInt32();
|
||||
|
||||
|
||||
|
||||
Vertices = reader.ReadBlockAt<NavMeshList<NavMeshVertex>>(VerticesPointer);
|
||||
Indices = reader.ReadBlockAt<NavMeshList<ushort>>(IndicesPointer);
|
||||
AdjPolys = reader.ReadBlockAt<NavMeshList<NavMeshAdjPoly>>(AdjPolysPointer);
|
||||
Polys = reader.ReadBlockAt<NavMeshList<NavMeshPoly>>(PolysPointer);
|
||||
SectorTree = reader.ReadBlockAt<NavMeshSector>(SectorTreePointer);
|
||||
Portals = reader.ReadStructsAt<NavMeshPortal>(PortalsPointer, PortalsCount);
|
||||
PortalLinks = reader.ReadUshortsAt(PortalLinksPointer, PortalLinksCount);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
VerticesPointer = (ulong)(Vertices != null ? Vertices.FilePosition : 0);
|
||||
IndicesPointer = (ulong)(Indices != null ? Indices.FilePosition : 0);
|
||||
AdjPolysPointer = (ulong)(AdjPolys != null ? AdjPolys.FilePosition : 0);
|
||||
PolysPointer = (ulong)(Polys != null ? Polys.FilePosition : 0);
|
||||
SectorTreePointer = (ulong)(SectorTree != null ? SectorTree.FilePosition : 0);
|
||||
PortalsPointer = (ulong)(PortalsBlock?.FilePosition ?? 0);
|
||||
PortalLinksPointer = (ulong)(PortalLinksBlock?.FilePosition ?? 0);
|
||||
|
||||
|
||||
|
||||
writer.Write((uint)ContentFlags);
|
||||
writer.Write(VersionUnk1);
|
||||
writer.Write(Unused_018h);
|
||||
writer.Write(Unused_01Ch);
|
||||
writer.Write(Transform);
|
||||
writer.Write(AABBSize);
|
||||
writer.Write(AABBUnk);
|
||||
writer.Write(VerticesPointer);
|
||||
writer.Write(Unused_078h);
|
||||
writer.Write(Unused_07Ch);
|
||||
writer.Write(IndicesPointer);
|
||||
writer.Write(AdjPolysPointer);
|
||||
writer.Write(AdjPolysIndicesCount);
|
||||
writer.WriteStruct(AdjAreaIDs);
|
||||
writer.Write(PolysPointer);
|
||||
writer.Write(SectorTreePointer);
|
||||
writer.Write(PortalsPointer);
|
||||
writer.Write(PortalLinksPointer);
|
||||
writer.Write(VerticesCount);
|
||||
writer.Write(PolysCount);
|
||||
writer.Write(AreaID);
|
||||
writer.Write(TotalBytes);
|
||||
writer.Write(SectorUnkCount);
|
||||
writer.Write(PortalsCount);
|
||||
writer.Write(PortalLinksCount);
|
||||
writer.Write(Unused_154h);
|
||||
writer.Write(Unused_158h);
|
||||
writer.Write(Unused_15Ch);
|
||||
writer.Write(VersionUnk2);
|
||||
writer.Write(Unused_164h);
|
||||
writer.Write(Unused_168h);
|
||||
writer.Write(Unused_16Ch);
|
||||
}
|
||||
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>(base.GetReferences());
|
||||
if (Vertices != null) list.Add(Vertices);
|
||||
if (Indices != null) list.Add(Indices);
|
||||
if (AdjPolys != null) list.Add(AdjPolys);
|
||||
if (Polys != null) list.Add(Polys);
|
||||
if (SectorTree != null) list.Add(SectorTree);
|
||||
|
||||
if ((Portals != null) && (Portals.Length > 0))
|
||||
{
|
||||
PortalsBlock = new ResourceSystemStructBlock<NavMeshPortal>(Portals);
|
||||
list.Add(PortalsBlock);
|
||||
}
|
||||
|
||||
if ((PortalLinks != null) && (PortalLinks.Length > 0))
|
||||
{
|
||||
PortalLinksBlock = new ResourceSystemStructBlock<ushort>(PortalLinks);
|
||||
list.Add(PortalLinksBlock);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(Size: " + FloatUtil.GetVector3String(AABBSize) + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshUintArray
|
||||
{
|
||||
public uint Count { get; set; }
|
||||
public uint v00;
|
||||
public uint v01;
|
||||
public uint v02;
|
||||
public uint v03;
|
||||
public uint v04;
|
||||
public uint v05;
|
||||
public uint v06; // 0x00000000
|
||||
public uint v07; // 0x00000000
|
||||
public uint v08; // 0x00000000
|
||||
public uint v09; // 0x00000000
|
||||
public uint v10; // 0x00000000
|
||||
public uint v11; // 0x00000000
|
||||
public uint v12; // 0x00000000
|
||||
public uint v13; // 0x00000000
|
||||
public uint v14; // 0x00000000
|
||||
public uint v15; // 0x00000000
|
||||
public uint v16; // 0x00000000
|
||||
public uint v17; // 0x00000000
|
||||
public uint v18; // 0x00000000
|
||||
public uint v19; // 0x00000000
|
||||
public uint v20; // 0x00000000
|
||||
public uint v21; // 0x00000000
|
||||
public uint v22; // 0x00000000
|
||||
public uint v23; // 0x00000000
|
||||
public uint v24; // 0x00000000
|
||||
public uint v25; // 0x00000000
|
||||
public uint v26; // 0x00000000
|
||||
public uint v27; // 0x00000000
|
||||
public uint v28; // 0x00000000
|
||||
public uint v29; // 0x00000000
|
||||
public uint v30; // 0x00000000
|
||||
public uint v31; // 0x00000000
|
||||
|
||||
public uint[] RawValues
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]{ v00,v01,v02,v03,v04,v05,v06,v07,v08,v09,
|
||||
v10,v11,v12,v13,v14,v15,v16,v17,v18,v19,
|
||||
v20,v21,v22,v23,v24,v25,v26,v27,v28,v29,
|
||||
v30,v31 };
|
||||
}
|
||||
}
|
||||
|
||||
public uint[] Values
|
||||
{
|
||||
get
|
||||
{
|
||||
uint[] vals = new uint[Count];
|
||||
uint[] rvals = RawValues;
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
vals[i] = rvals[i];
|
||||
}
|
||||
return vals;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(Count: " + Count.ToString() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshList<T> : ResourceSystemBlock where T : struct
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 48; }
|
||||
}
|
||||
|
||||
public uint VFT { get; set; }
|
||||
public uint Unknown_04h { get; set; } // 0x00000001
|
||||
public uint ItemCount { get; set; }
|
||||
public uint Unknown_0Ch { get; set; } // 0x00000000
|
||||
public ulong ListPartsPointer { get; set; }
|
||||
public ulong ListOffsetsPointer { get; set; }
|
||||
public uint ListPartsCount { get; set; }
|
||||
public uint Unknown_24h { get; set; } // 0x00000000
|
||||
public uint Unknown_28h { get; set; } // 0x00000000
|
||||
public uint Unknown_2Ch { get; set; } // 0x00000000
|
||||
|
||||
public ResourceSimpleArray<NavMeshListPart<T>> ListParts { get; set; }
|
||||
public uint[] ListOffsets { get; set; }
|
||||
|
||||
private ResourceSystemStructBlock<uint> ListOffsetsBlock = null;
|
||||
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
VFT = reader.ReadUInt32();
|
||||
Unknown_04h = reader.ReadUInt32();
|
||||
ItemCount = reader.ReadUInt32();
|
||||
Unknown_0Ch = reader.ReadUInt32();
|
||||
ListPartsPointer = reader.ReadUInt64();
|
||||
ListOffsetsPointer = reader.ReadUInt64();
|
||||
ListPartsCount = reader.ReadUInt32();
|
||||
Unknown_24h = reader.ReadUInt32();
|
||||
Unknown_28h = reader.ReadUInt32();
|
||||
Unknown_2Ch = reader.ReadUInt32();
|
||||
|
||||
ListParts = reader.ReadBlockAt<ResourceSimpleArray<NavMeshListPart<T>>>(ListPartsPointer, ListPartsCount);
|
||||
ListOffsets = reader.ReadUintsAt(ListOffsetsPointer, ListPartsCount);
|
||||
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
ListPartsPointer = (ulong)(ListParts != null ? ListParts.FilePosition : 0);
|
||||
ListOffsetsPointer = (ulong)(ListOffsetsBlock?.FilePosition ?? 0);
|
||||
ListPartsCount = (uint)(ListParts != null ? ListParts.Count : 0);
|
||||
|
||||
writer.Write(VFT);
|
||||
writer.Write(Unknown_04h);
|
||||
writer.Write(ItemCount);
|
||||
writer.Write(Unknown_0Ch);
|
||||
writer.Write(ListPartsPointer);
|
||||
writer.Write(ListOffsetsPointer);
|
||||
writer.Write(ListPartsCount);
|
||||
writer.Write(Unknown_24h);
|
||||
writer.Write(Unknown_28h);
|
||||
writer.Write(Unknown_2Ch);
|
||||
}
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
if (ListParts != null) list.Add(ListParts);
|
||||
|
||||
if ((ListOffsets != null) && (ListOffsets.Length > 0))
|
||||
{
|
||||
ListOffsetsBlock = new ResourceSystemStructBlock<uint>(ListOffsets);
|
||||
list.Add(ListOffsetsBlock);
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public List<T> GetFullList()
|
||||
{
|
||||
List<T> list = new List<T>((int)ItemCount);
|
||||
|
||||
if (ListParts != null)
|
||||
{
|
||||
foreach (var part in ListParts)
|
||||
{
|
||||
if (part.Items != null)
|
||||
{
|
||||
list.AddRange(part.Items);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(" + ItemCount.ToString() + " total items, " + ListPartsCount.ToString() + " parts)";
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshListPart<T> : ResourceSystemBlock where T : struct
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 16; }
|
||||
}
|
||||
|
||||
public ulong Pointer { get; set; }
|
||||
public uint Count { get; set; }
|
||||
public uint Unknown_0Ch { get; set; } // 0x00000000
|
||||
|
||||
public T[] Items { get; set; }
|
||||
|
||||
private ResourceSystemStructBlock<T> ItemsBlock = null;
|
||||
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
Pointer = reader.ReadUInt64();
|
||||
Count = reader.ReadUInt32();
|
||||
Unknown_0Ch = reader.ReadUInt32();
|
||||
|
||||
Items = reader.ReadStructsAt<T>(Pointer, Count);
|
||||
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
Pointer = (ulong)(ItemsBlock?.FilePosition ?? 0);
|
||||
Count = (uint)(Items?.Length ?? 0);
|
||||
|
||||
writer.Write(Pointer);
|
||||
writer.Write(Count);
|
||||
writer.Write(Unknown_0Ch);
|
||||
}
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
|
||||
if ((Items != null) && (Items.Length > 0))
|
||||
{
|
||||
ItemsBlock = new ResourceSystemStructBlock<T>(Items);
|
||||
list.Add(ItemsBlock);
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(" + Count.ToString() + " items)";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshVertex
|
||||
{
|
||||
public ushort X { get; set; }
|
||||
public ushort Y { get; set; }
|
||||
public ushort Z { get; set; }
|
||||
|
||||
|
||||
public Vector3 Position { get { return ToVector3(); } set { FromVector3(value); } }
|
||||
|
||||
public Vector3 ToVector3()
|
||||
{
|
||||
const float usmax = ushort.MaxValue;
|
||||
return new Vector3(X / usmax, Y / usmax, Z / usmax);
|
||||
}
|
||||
public void FromVector3(Vector3 v)
|
||||
{
|
||||
const float usmax = ushort.MaxValue;
|
||||
X = (ushort)(v.X * usmax);
|
||||
Y = (ushort)(v.Y * usmax);
|
||||
Z = (ushort)(v.Z * usmax);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return X.ToString() + ", " + Y.ToString() + ", " + Z.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshAABB
|
||||
{
|
||||
public short MinX { get; set; }
|
||||
public short MaxX { get; set; }
|
||||
public short MinY { get; set; }
|
||||
public short MaxY { get; set; }
|
||||
public short MinZ { get; set; }
|
||||
public short MaxZ { get; set; }
|
||||
|
||||
public Vector3 Min { get { return new Vector3(MinX / 4.0f, MinY / 4.0f, MinZ / 4.0f); } }
|
||||
public Vector3 Max { get { return new Vector3(MaxX / 4.0f, MaxY / 4.0f, MaxZ / 4.0f); } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
Vector3 min = Min;
|
||||
Vector3 max = Max;
|
||||
return string.Format("({0}, {1}, {2}) | ({3}, {4}, {5})", min.X, min.Y, min.Z, max.X, max.Y, max.Z);
|
||||
//return string.Format("({0}, {1}, {2}) | ({3}, {4}, {5})", MinX, MinY, MinZ, MaxX, MaxY, MaxZ);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshAdjPoly
|
||||
{
|
||||
public NavMeshAdjPolyPart Unknown_0h { get; set; }
|
||||
public NavMeshAdjPolyPart Unknown_4h { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Unknown_0h.Bin + " | " + Unknown_4h.Bin + " | " +
|
||||
Unknown_0h.ToString() + " | " + Unknown_4h.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshAdjPolyPart
|
||||
{
|
||||
public uint Value { get; set; }
|
||||
|
||||
public string Bin
|
||||
{
|
||||
get
|
||||
{
|
||||
return Convert.ToString(Value, 2).PadLeft(32, '0');
|
||||
}
|
||||
}
|
||||
|
||||
public uint AdjAreaIDInd { get { return (Value >> 0) & 0x1F; } }
|
||||
public uint PolyID { get { return (Value >> 5) & 0x3FFF; } }
|
||||
public uint Unk2 { get { return (Value >> 19) & 0x3; } }
|
||||
public uint Unk3 { get { return (Value >> 21); } }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AdjAreaIDInd.ToString() + ", " + PolyID.ToString() + ", " + Unk2.ToString() + ", " + Unk3.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshPoly
|
||||
{
|
||||
public ushort Unknown_00h { get; set; }
|
||||
public ushort IndexFlags { get; set; }
|
||||
public ushort IndexID { get; set; }
|
||||
public ushort AreaID { get; set; }
|
||||
public uint Unused_08h { get; set; } // 0x00000000
|
||||
public uint Unused_0Ch { get; set; } // 0x00000000
|
||||
public uint Unused_10h { get; set; } // 0x00000000
|
||||
public uint Unused_14h { get; set; } // 0x00000000
|
||||
public NavMeshAABB CellAABB { get; set; }
|
||||
public FlagsUint Unknown_24h { get; set; }
|
||||
public FlagsUint Unknown_28h { get; set; }
|
||||
public ushort PartFlags { get; set; }
|
||||
public ushort PortalID { get; set; }
|
||||
|
||||
|
||||
//public int IndexUnk { get { return (IndexFlags >> 0) & 31; } } //always 0
|
||||
public int IndexCount { get { return (IndexFlags >> 5); } }
|
||||
|
||||
//public int PartUnk1 { get { return (PartFlags >> 0) & 0xF; } } //always 0
|
||||
public int PartID { get { return (PartFlags >> 4) & 0xFF; } }
|
||||
public int PartUnk2 { get { return (PartFlags >> 12) & 0xF; } }
|
||||
|
||||
|
||||
public uint Unknown_28h_16 { get { return ((Unknown_28h.Value & 65535)); } }
|
||||
public uint Unknown_28h_8a { get { return ((Unknown_28h.Value >> 0) & 255); } }
|
||||
public uint Unknown_28h_8b { get { return ((Unknown_28h.Value >> 8) & 255); } }
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return
|
||||
//Unknown_28h.Bin + ", (" + Unknown_28h_8a.ToString() + ", " + Unknown_28h_8b.ToString() + "), " +
|
||||
Unknown_00h.ToString() + ", " +
|
||||
//IndexFlags.ToString() + ", " +
|
||||
IndexCount.ToString() + ", " + //IndexUnk.ToString() + ", " +
|
||||
IndexID.ToString() + ", " + AreaID.ToString() + ", " +
|
||||
CellAABB.ToString() + ", " +
|
||||
Unknown_24h.Hex + ", " +
|
||||
Unknown_28h.Hex + ", " +
|
||||
//PartFlags.ToString() + ", " + //PartUnk1.ToString() + ", " +
|
||||
PartID.ToString() + ", " +
|
||||
PartUnk2.ToString() + ", " +
|
||||
PortalID.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshSector : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 96; }
|
||||
}
|
||||
|
||||
public Vector4 AABBMin { get; set; } //W==NaN
|
||||
public Vector4 AABBMax { get; set; } //W==NaN
|
||||
public NavMeshAABB CellAABB { get; set; }
|
||||
public ulong DataPointer { get; set; }
|
||||
public ulong SubTree1Pointer { get; set; }
|
||||
public ulong SubTree2Pointer { get; set; }
|
||||
public ulong SubTree3Pointer { get; set; }
|
||||
public ulong SubTree4Pointer { get; set; }
|
||||
public uint Unused_54h { get; set; } // 0x00000000
|
||||
public uint Unused_58h { get; set; } // 0x00000000
|
||||
public uint Unused_5Ch { get; set; } // 0x00000000
|
||||
|
||||
public NavMeshSectorData Data { get; set; }
|
||||
public NavMeshSector SubTree1 { get; set; }
|
||||
public NavMeshSector SubTree2 { get; set; }
|
||||
public NavMeshSector SubTree3 { get; set; }
|
||||
public NavMeshSector SubTree4 { get; set; }
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
AABBMin = reader.ReadVector4();
|
||||
AABBMax = reader.ReadVector4();
|
||||
CellAABB = reader.ReadStruct<NavMeshAABB>();
|
||||
DataPointer = reader.ReadUInt64();
|
||||
SubTree1Pointer = reader.ReadUInt64();
|
||||
SubTree2Pointer = reader.ReadUInt64();
|
||||
SubTree3Pointer = reader.ReadUInt64();
|
||||
SubTree4Pointer = reader.ReadUInt64();
|
||||
Unused_54h = reader.ReadUInt32();
|
||||
Unused_58h = reader.ReadUInt32();
|
||||
Unused_5Ch = reader.ReadUInt32();
|
||||
|
||||
Data = reader.ReadBlockAt<NavMeshSectorData>(DataPointer);
|
||||
SubTree1 = reader.ReadBlockAt<NavMeshSector>(SubTree1Pointer);
|
||||
SubTree2 = reader.ReadBlockAt<NavMeshSector>(SubTree2Pointer);
|
||||
SubTree3 = reader.ReadBlockAt<NavMeshSector>(SubTree3Pointer);
|
||||
SubTree4 = reader.ReadBlockAt<NavMeshSector>(SubTree4Pointer);
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
DataPointer = (ulong)(Data != null ? Data.FilePosition : 0);
|
||||
SubTree1Pointer = (ulong)(SubTree1 != null ? SubTree1.FilePosition : 0);
|
||||
SubTree2Pointer = (ulong)(SubTree2 != null ? SubTree2.FilePosition : 0);
|
||||
SubTree3Pointer = (ulong)(SubTree3 != null ? SubTree3.FilePosition : 0);
|
||||
SubTree4Pointer = (ulong)(SubTree4 != null ? SubTree4.FilePosition : 0);
|
||||
|
||||
writer.Write(AABBMin);
|
||||
writer.Write(AABBMax);
|
||||
writer.WriteStruct(CellAABB);
|
||||
writer.Write(DataPointer);
|
||||
writer.Write(SubTree1Pointer);
|
||||
writer.Write(SubTree2Pointer);
|
||||
writer.Write(SubTree3Pointer);
|
||||
writer.Write(SubTree4Pointer);
|
||||
writer.Write(Unused_54h);
|
||||
writer.Write(Unused_58h);
|
||||
writer.Write(Unused_5Ch);
|
||||
}
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
if (Data != null) list.Add(Data);
|
||||
if (SubTree1 != null) list.Add(SubTree1);
|
||||
if (SubTree2 != null) list.Add(SubTree2);
|
||||
if (SubTree3 != null) list.Add(SubTree3);
|
||||
if (SubTree4 != null) list.Add(SubTree4);
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "[Min: "+AABBMin.ToString() + "], [Max:" + AABBMax.ToString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class NavMeshSectorData : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 32; }
|
||||
}
|
||||
|
||||
public uint UnkOffset { get; set; }
|
||||
public uint Unused_04h { get; set; } // 0x00000000
|
||||
public ulong PolyIDsPointer { get; set; }
|
||||
public ulong UnkDataPointer { get; set; }
|
||||
public ushort PolyIDsCount { get; set; }
|
||||
public ushort UnkDataCount { get; set; }
|
||||
public uint Unused_1Ch { get; set; } // 0x00000000
|
||||
|
||||
public ushort[] PolyIDs { get; set; }
|
||||
public NavMeshSectorDataUnk[] UnkData { get; set; }
|
||||
|
||||
private ResourceSystemStructBlock<ushort> PolyIDsBlock = null;
|
||||
private ResourceSystemStructBlock<NavMeshSectorDataUnk> UnkDataBlock = null;
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
UnkOffset = reader.ReadUInt32();
|
||||
Unused_04h = reader.ReadUInt32();
|
||||
PolyIDsPointer = reader.ReadUInt64();
|
||||
UnkDataPointer = reader.ReadUInt64();
|
||||
PolyIDsCount = reader.ReadUInt16();
|
||||
UnkDataCount = reader.ReadUInt16();
|
||||
Unused_1Ch = reader.ReadUInt32();
|
||||
|
||||
PolyIDs = reader.ReadUshortsAt(PolyIDsPointer, PolyIDsCount);
|
||||
UnkData = reader.ReadStructsAt<NavMeshSectorDataUnk>(UnkDataPointer, UnkDataCount);
|
||||
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
PolyIDsPointer = (ulong)(PolyIDsBlock?.FilePosition ?? 0);
|
||||
PolyIDsCount = (ushort)(PolyIDs?.Length ?? 0);
|
||||
UnkDataPointer = (ulong)(UnkDataBlock?.FilePosition ?? 0);
|
||||
UnkDataCount = (ushort)(UnkData?.Length ?? 0);
|
||||
|
||||
|
||||
writer.Write(UnkOffset);
|
||||
writer.Write(Unused_04h);
|
||||
writer.Write(PolyIDsPointer);
|
||||
writer.Write(UnkDataPointer);
|
||||
writer.Write(PolyIDsCount);
|
||||
writer.Write(UnkDataCount);
|
||||
writer.Write(Unused_1Ch);
|
||||
}
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
|
||||
if ((PolyIDs != null) && (PolyIDs.Length > 0))
|
||||
{
|
||||
PolyIDsBlock = new ResourceSystemStructBlock<ushort>(PolyIDs);
|
||||
list.Add(PolyIDsBlock);
|
||||
}
|
||||
if ((UnkData != null) && (UnkData.Length > 0))
|
||||
{
|
||||
UnkDataBlock = new ResourceSystemStructBlock<NavMeshSectorDataUnk>(UnkData);
|
||||
list.Add(UnkDataBlock);
|
||||
}
|
||||
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "(Polys: " + PolyIDsCount.ToString() + ", UnkOffset: " + UnkOffset.ToString() + ", UnkCount: " + UnkDataCount.ToString() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshSectorDataUnk
|
||||
{
|
||||
public ushort Unknown_0h { get; set; }
|
||||
public ushort Unknown_2h { get; set; }
|
||||
public ushort Unknown_4h { get; set; }
|
||||
public ushort Unknown_6h { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Unknown_0h.ToString() + ", " + Unknown_2h.ToString() + ", " + Unknown_4h.ToString() + ", " + Unknown_6h.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NavMeshPortal
|
||||
{
|
||||
public uint Unknown_00h { get; set; }
|
||||
public NavMeshVertex Position1 { get; set; }
|
||||
public NavMeshVertex Position2 { get; set; }
|
||||
public ushort Unknown_10h { get; set; }
|
||||
public ushort Unknown_12h { get; set; }
|
||||
public ushort Unknown_14h { get; set; }
|
||||
public ushort Unknown_16h { get; set; }
|
||||
public ushort Unknown_18h { get; set; }
|
||||
public ushort Unknown_1Ah { get; set; }
|
||||
//public NavMeshAABB AABB1 { get; set; }
|
||||
//public NavMeshAABB AABB2 { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Unknown_00h.ToString() + ", " +
|
||||
Position1.ToString() + ", " + Position2.ToString() + ", " +
|
||||
Unknown_10h.ToString() + ", " + Unknown_12h.ToString() + ", " +
|
||||
Unknown_14h.ToString() + ", " + Unknown_16h.ToString() + ", " +
|
||||
Unknown_18h.ToString() + ", " + Unknown_1Ah.ToString();
|
||||
//AABB1.ToString() + ", " + AABB2.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Flags] public enum NavMeshFlags : uint
|
||||
{
|
||||
Vertices = 1,
|
||||
Portals = 2,
|
||||
Vehicle = 4,
|
||||
Unknown8 = 8,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
Copyright(c) 2016 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//mangled to fit
|
||||
|
||||
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class NodeDictionary : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 112; }
|
||||
}
|
||||
|
||||
public ulong NodesPointer { get; set; }
|
||||
public uint NodesCount { get; set; }
|
||||
public uint NodesCountVehicle { get; set; }
|
||||
public uint NodesCountPed { get; set; }
|
||||
public uint Unk24 { get; set; } // 0x00000000
|
||||
public ulong LinksPtr { get; set; }
|
||||
public uint LinksCount { get; set; }
|
||||
public uint Unk34 { get; set; } // 0x00000000
|
||||
public ulong JunctionsPtr { get; set; }
|
||||
public ulong JunctionHeightmapBytesPtr { get; set; }
|
||||
public uint Unk48 { get; set; } = 1; // 0x00000001
|
||||
public uint Unk4C { get; set; } // 0x00000000
|
||||
public ulong JunctionRefsPtr { get; set; }
|
||||
public ushort JunctionRefsCount0 { get; set; }
|
||||
public ushort JunctionRefsCount1 { get; set; } // same as JunctionRefsCount0
|
||||
public uint Unk5C { get; set; } // 0x00000000
|
||||
public uint JunctionsCount { get; set; } // same as JunctionRefsCount0
|
||||
public uint JunctionHeightmapBytesCount { get; set; }
|
||||
public uint Unk68 { get; set; } // 0x00000000
|
||||
public uint Unk6C { get; set; } // 0x00000000
|
||||
|
||||
public Node[] Nodes { get; set; }
|
||||
public NodeLink[] Links { get; set; }
|
||||
public NodeJunction[] Junctions { get; set; }
|
||||
public byte[] JunctionHeightmapBytes { get; set; }
|
||||
public NodeJunctionRef[] JunctionRefs { get; set; }
|
||||
|
||||
|
||||
private ResourceSystemStructBlock<Node> NodesBlock = null;
|
||||
private ResourceSystemStructBlock<NodeLink> LinksBlock = null;
|
||||
private ResourceSystemStructBlock<NodeJunction> JunctionsBlock = null;
|
||||
private ResourceSystemStructBlock<byte> JunctionHeightmapBytesBlock = null;
|
||||
private ResourceSystemStructBlock<NodeJunctionRef> JunctionRefsBlock = null;
|
||||
|
||||
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
this.NodesPointer = reader.ReadUInt64();
|
||||
this.NodesCount = reader.ReadUInt32();
|
||||
this.NodesCountVehicle = reader.ReadUInt32();
|
||||
this.NodesCountPed = reader.ReadUInt32();
|
||||
this.Unk24 = reader.ReadUInt32();
|
||||
this.LinksPtr = reader.ReadUInt64();
|
||||
this.LinksCount = reader.ReadUInt32();
|
||||
this.Unk34 = reader.ReadUInt32();
|
||||
this.JunctionsPtr = reader.ReadUInt64();
|
||||
this.JunctionHeightmapBytesPtr = reader.ReadUInt64();
|
||||
this.Unk48 = reader.ReadUInt32();
|
||||
this.Unk4C = reader.ReadUInt32();
|
||||
this.JunctionRefsPtr = reader.ReadUInt64();
|
||||
this.JunctionRefsCount0 = reader.ReadUInt16();
|
||||
this.JunctionRefsCount1 = reader.ReadUInt16();
|
||||
this.Unk5C = reader.ReadUInt32();
|
||||
this.JunctionsCount = reader.ReadUInt32();
|
||||
this.JunctionHeightmapBytesCount = reader.ReadUInt32();
|
||||
this.Unk68 = reader.ReadUInt32();
|
||||
this.Unk6C = reader.ReadUInt32();
|
||||
|
||||
this.Nodes = reader.ReadStructsAt<Node>(this.NodesPointer, this.NodesCount);
|
||||
this.Links = reader.ReadStructsAt<NodeLink>(this.LinksPtr, this.LinksCount);
|
||||
this.Junctions = reader.ReadStructsAt<NodeJunction>(this.JunctionsPtr, this.JunctionsCount);
|
||||
this.JunctionHeightmapBytes = reader.ReadBytesAt(this.JunctionHeightmapBytesPtr, this.JunctionHeightmapBytesCount);
|
||||
this.JunctionRefs = reader.ReadStructsAt<NodeJunctionRef>(this.JunctionRefsPtr, this.JunctionRefsCount1);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
// update structure data
|
||||
NodesPointer = (ulong)(NodesBlock?.FilePosition ?? 0);
|
||||
NodesCount = (uint)(Nodes?.Length ?? 0); //assume NodesCountVehicle and Ped already updated..
|
||||
LinksPtr = (ulong)(LinksBlock?.FilePosition ?? 0);
|
||||
LinksCount = (uint)(Links?.Length ?? 0);
|
||||
JunctionsPtr = (ulong)(JunctionsBlock?.FilePosition ?? 0);
|
||||
JunctionHeightmapBytesPtr = (ulong)(JunctionHeightmapBytesBlock?.FilePosition ?? 0);
|
||||
JunctionRefsPtr = (ulong)(JunctionRefsBlock?.FilePosition ?? 0);
|
||||
JunctionRefsCount0 = (ushort)(JunctionRefs?.Length ?? 0);
|
||||
JunctionRefsCount1 = JunctionRefsCount1;
|
||||
JunctionsCount = (uint)(Junctions?.Length ?? 0);
|
||||
JunctionHeightmapBytesCount = (uint)(JunctionHeightmapBytes?.Length ?? 0);
|
||||
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.NodesPointer);
|
||||
writer.Write(this.NodesCount);
|
||||
writer.Write(this.NodesCountVehicle);
|
||||
writer.Write(this.NodesCountPed);
|
||||
writer.Write(this.Unk24);
|
||||
writer.Write(this.LinksPtr);
|
||||
writer.Write(this.LinksCount);
|
||||
writer.Write(this.Unk34);
|
||||
writer.Write(this.JunctionsPtr);
|
||||
writer.Write(this.JunctionHeightmapBytesPtr);
|
||||
writer.Write(this.Unk48);
|
||||
writer.Write(this.Unk4C);
|
||||
writer.Write(this.JunctionRefsPtr);
|
||||
writer.Write(this.JunctionRefsCount0);
|
||||
writer.Write(this.JunctionRefsCount1);
|
||||
writer.Write(this.Unk5C);
|
||||
writer.Write(this.JunctionsCount);
|
||||
writer.Write(this.JunctionHeightmapBytesCount);
|
||||
writer.Write(this.Unk68);
|
||||
writer.Write(this.Unk6C);
|
||||
}
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>(base.GetReferences());
|
||||
|
||||
if ((JunctionRefs != null) && (JunctionRefs.Length > 0))
|
||||
{
|
||||
JunctionRefsBlock = new ResourceSystemStructBlock<NodeJunctionRef>(JunctionRefs);
|
||||
list.Add(JunctionRefsBlock);
|
||||
}
|
||||
if ((JunctionHeightmapBytes != null) && (JunctionHeightmapBytes.Length > 0))
|
||||
{
|
||||
JunctionHeightmapBytesBlock = new ResourceSystemStructBlock<byte>(JunctionHeightmapBytes);
|
||||
list.Add(JunctionHeightmapBytesBlock);
|
||||
}
|
||||
if ((Junctions != null) && (Junctions.Length > 0))
|
||||
{
|
||||
JunctionsBlock = new ResourceSystemStructBlock<NodeJunction>(Junctions);
|
||||
list.Add(JunctionsBlock);
|
||||
}
|
||||
if ((Links != null) && (Links.Length > 0))
|
||||
{
|
||||
LinksBlock = new ResourceSystemStructBlock<NodeLink>(Links);
|
||||
list.Add(LinksBlock);
|
||||
}
|
||||
if ((Nodes != null) && (Nodes.Length > 0))
|
||||
{
|
||||
NodesBlock = new ResourceSystemStructBlock<Node>(Nodes);
|
||||
list.Add(NodesBlock);
|
||||
}
|
||||
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct Node
|
||||
{
|
||||
public uint Unused0 { get; set; } // 0x00000000
|
||||
public uint Unused1 { get; set; } // 0x00000000
|
||||
public uint Unused2 { get; set; } // 0x00000000
|
||||
public uint Unused3 { get; set; } // 0x00000000
|
||||
public ushort AreaID { get; set; }
|
||||
public ushort NodeID { get; set; }
|
||||
public TextHash StreetName { get; set; }
|
||||
public ushort Unused4 { get; set; }
|
||||
public ushort LinkID { get; set; }
|
||||
public short PositionX { get; set; }
|
||||
public short PositionY { get; set; }
|
||||
public FlagsByte Flags0 { get; set; }
|
||||
public FlagsByte Flags1 { get; set; }
|
||||
public short PositionZ { get; set; }
|
||||
public FlagsByte Flags2 { get; set; }
|
||||
public FlagsByte LinkCountFlags { get; set; }
|
||||
public FlagsByte Flags3 { get; set; }
|
||||
public FlagsByte Flags4 { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
//return Unused0.ToString() + ", " + Unused1.ToString() + ", " + Unused2.ToString() + ", " +
|
||||
// Unused3.ToString() + ", " + AreaID.ToString() + ", " + NodeID.ToString() + ", " +
|
||||
// UnknownInterp.ToString() + ", " + HeuristicCost.ToString() + ", " + LinkID.ToString() + ", " +
|
||||
// PositionX.ToString() + ", " + PositionY.ToString() + ", " + Unk20.ToString() + ", " + Unk21.ToString() + ", " +
|
||||
// Unk22.ToString() + ", " + Unk24.ToString() + ", " + Unk26.ToString();
|
||||
|
||||
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + StreetName.ToString();// + ", X:" +
|
||||
//PositionX.ToString() + ", Y:" + PositionY.ToString() + ", " + PositionZ.ToString();// + ", " +
|
||||
//Flags0.ToString() + ", " + Flags1.ToString() + ", Z:" +
|
||||
//Flags2.ToString() + ", " + LinkCountFlags.ToString() + ", " +
|
||||
//Flags3.ToString() + ", " + Flags4.ToString();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NodeLink
|
||||
{
|
||||
public ushort AreaID { get; set; }
|
||||
public ushort NodeID { get; set; }
|
||||
public FlagsByte Flags0 { get; set; }
|
||||
public FlagsByte Flags1 { get; set; }
|
||||
public FlagsByte Flags2 { get; set; }
|
||||
public FlagsByte LinkLength { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + Flags0.Value.ToString() + ", " + Flags1.Value.ToString() + ", " + Flags2.Value.ToString() + ", " + LinkLength.Value.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NodeJunction
|
||||
{
|
||||
public short MaxZ { get; set; }
|
||||
public short PositionX { get; set; }
|
||||
public short PositionY { get; set; }
|
||||
public short MinZ { get; set; }
|
||||
public ushort HeightmapPtr { get; set; }
|
||||
public byte HeightmapDimX { get; set; }
|
||||
public byte HeightmapDimY { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return PositionX.ToString() + ", " + PositionY.ToString() + ": " + MinZ.ToString() + ", " + MaxZ.ToString() + ": " + HeightmapDimX.ToString() + " x " + HeightmapDimY.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct NodeJunctionRef
|
||||
{
|
||||
public ushort AreaID { get; set; }
|
||||
public ushort NodeID { get; set; }
|
||||
public ushort JunctionID { get; set; }
|
||||
public ushort Unk0 { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return AreaID.ToString() + ", " + NodeID.ToString() + ", " + JunctionID.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,340 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class ResourceBuilder
|
||||
{
|
||||
protected const int RESOURCE_IDENT = 0x37435352;
|
||||
protected const int BASE_SIZE = 0x2000;
|
||||
private const int SKIP_SIZE = 512;//256;//64;
|
||||
private const int ALIGN_SIZE = 512;//64;
|
||||
|
||||
|
||||
public static void GetBlocks(IResourceBlock rootBlock, out IList<IResourceBlock> sys, out IList<IResourceBlock> gfx)
|
||||
{
|
||||
var systemBlocks = new HashSet<IResourceBlock>();
|
||||
var graphicBlocks = new HashSet<IResourceBlock>();
|
||||
var protectedBlocks = new List<IResourceBlock>();
|
||||
|
||||
var stack = new Stack<IResourceBlock>();
|
||||
stack.Push(rootBlock);
|
||||
|
||||
var processed = new HashSet<IResourceBlock>();
|
||||
processed.Add(rootBlock);
|
||||
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var block = stack.Pop();
|
||||
if (block == null)
|
||||
continue;
|
||||
|
||||
if (block is IResourceSystemBlock)
|
||||
{
|
||||
if (!systemBlocks.Contains(block))
|
||||
systemBlocks.Add(block);
|
||||
|
||||
// for system blocks, also process references...
|
||||
|
||||
var references = ((IResourceSystemBlock)block).GetReferences();
|
||||
//Array.Reverse(references);
|
||||
foreach (var reference in references)
|
||||
if (!processed.Contains(reference))
|
||||
{
|
||||
stack.Push(reference);
|
||||
processed.Add(reference);
|
||||
}
|
||||
var subs = new Stack<IResourceSystemBlock>();
|
||||
foreach (var part in ((IResourceSystemBlock)block).GetParts())
|
||||
subs.Push((IResourceSystemBlock)part.Item2);
|
||||
while (subs.Count > 0)
|
||||
{
|
||||
var sub = subs.Pop();
|
||||
|
||||
foreach (var x in sub.GetReferences())
|
||||
if (!processed.Contains(x))
|
||||
{
|
||||
stack.Push(x);
|
||||
processed.Add(x);
|
||||
}
|
||||
foreach (var x in sub.GetParts())
|
||||
subs.Push((IResourceSystemBlock)x.Item2);
|
||||
|
||||
protectedBlocks.Add(sub);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!graphicBlocks.Contains(block))
|
||||
graphicBlocks.Add(block);
|
||||
}
|
||||
}
|
||||
|
||||
//var result = new List<IResourceBlock>();
|
||||
//result.AddRange(systemBlocks);
|
||||
//result.AddRange(graphicBlocks);
|
||||
//return result;
|
||||
|
||||
// there are now sys-blocks in the list that actually
|
||||
// only substructures and therefore must not get
|
||||
// a new position!
|
||||
// -> remove them from the list
|
||||
foreach (var q in protectedBlocks)
|
||||
if (systemBlocks.Contains(q))
|
||||
systemBlocks.Remove(q);
|
||||
|
||||
|
||||
sys = new List<IResourceBlock>();
|
||||
foreach (var s in systemBlocks)
|
||||
sys.Add(s);
|
||||
gfx = new List<IResourceBlock>();
|
||||
foreach (var s in graphicBlocks)
|
||||
gfx.Add(s);
|
||||
}
|
||||
|
||||
public static void AssignPositions(IList<IResourceBlock> blocks, uint basePosition, ref int pageSize, out int pageCount)
|
||||
{
|
||||
// find largest structure
|
||||
long largestBlockSize = 0;
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (largestBlockSize < block.BlockLength)
|
||||
largestBlockSize = block.BlockLength;
|
||||
}
|
||||
|
||||
// find minimum page size
|
||||
long currentPageSize = pageSize;// 0x2000;
|
||||
while (currentPageSize < largestBlockSize)
|
||||
currentPageSize *= 2;
|
||||
|
||||
long currentPageCount;
|
||||
long currentPosition;
|
||||
while (true)
|
||||
{
|
||||
currentPageCount = 0;
|
||||
currentPosition = 0;
|
||||
|
||||
// reset all positions
|
||||
foreach (var block in blocks)
|
||||
block.FilePosition = -1;
|
||||
|
||||
foreach (var block in blocks)
|
||||
{
|
||||
if (block.FilePosition != -1)
|
||||
throw new Exception("A position of -1 is not possible!");
|
||||
//if (block.Length == 0)
|
||||
// throw new Exception("A length of 0 is not allowed!");
|
||||
|
||||
// check if new page is necessary...
|
||||
// if yes, add a new page and align to it
|
||||
long maxSpace = currentPageCount * currentPageSize - currentPosition;
|
||||
if (maxSpace < (block.BlockLength + SKIP_SIZE))
|
||||
{
|
||||
currentPageCount++;
|
||||
currentPosition = currentPageSize * (currentPageCount - 1);
|
||||
}
|
||||
|
||||
// set position
|
||||
block.FilePosition = basePosition + currentPosition;
|
||||
currentPosition += block.BlockLength + SKIP_SIZE;
|
||||
|
||||
// align...
|
||||
if ((currentPosition % ALIGN_SIZE) != 0)
|
||||
currentPosition += (ALIGN_SIZE - (currentPosition % ALIGN_SIZE));
|
||||
}
|
||||
|
||||
// break if everything fits...
|
||||
if (currentPageCount < 128)
|
||||
break;
|
||||
|
||||
currentPageSize *= 2;
|
||||
}
|
||||
|
||||
pageSize = (int)currentPageSize;
|
||||
pageCount = (int)currentPageCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static byte[] Build(ResourceFileBase fileBase, int version)
|
||||
{
|
||||
|
||||
fileBase.FilePagesInfo = new ResourcePagesInfo();
|
||||
|
||||
IList<IResourceBlock> systemBlocks;
|
||||
IList<IResourceBlock> graphicBlocks;
|
||||
GetBlocks(fileBase, out systemBlocks, out graphicBlocks);
|
||||
|
||||
int systemPageSize = BASE_SIZE;// *4;
|
||||
int systemPageCount;
|
||||
AssignPositions(systemBlocks, 0x50000000, ref systemPageSize, out systemPageCount);
|
||||
|
||||
int graphicsPageSize = BASE_SIZE;
|
||||
int graphicsPageCount;
|
||||
AssignPositions(graphicBlocks, 0x60000000, ref graphicsPageSize, out graphicsPageCount);
|
||||
|
||||
|
||||
|
||||
|
||||
fileBase.FilePagesInfo.SystemPagesCount = 0;
|
||||
if (systemPageCount > 0)
|
||||
fileBase.FilePagesInfo.SystemPagesCount = 1; // (byte)systemPageCount; //1
|
||||
fileBase.FilePagesInfo.GraphicsPagesCount = (byte)graphicsPageCount;
|
||||
|
||||
|
||||
|
||||
var systemStream = new MemoryStream();
|
||||
var graphicsStream = new MemoryStream();
|
||||
var resourceWriter = new ResourceDataWriter(systemStream, graphicsStream);
|
||||
|
||||
resourceWriter.Position = 0x50000000;
|
||||
foreach (var block in systemBlocks)
|
||||
{
|
||||
resourceWriter.Position = block.FilePosition;
|
||||
|
||||
var pos_before = resourceWriter.Position;
|
||||
block.Write(resourceWriter);
|
||||
var pos_after = resourceWriter.Position;
|
||||
|
||||
if ((pos_after - pos_before) != block.BlockLength)
|
||||
{
|
||||
throw new Exception("error in system length");
|
||||
}
|
||||
}
|
||||
|
||||
resourceWriter.Position = 0x60000000;
|
||||
foreach (var block in graphicBlocks)
|
||||
{
|
||||
resourceWriter.Position = block.FilePosition;
|
||||
|
||||
var pos_before = resourceWriter.Position;
|
||||
block.Write(resourceWriter);
|
||||
var pos_after = resourceWriter.Position;
|
||||
|
||||
if ((pos_after - pos_before) != block.BlockLength)
|
||||
{
|
||||
throw new Exception("error in graphics length");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var sysDataSize = 0x2000;
|
||||
while (sysDataSize < systemStream.Length)
|
||||
{
|
||||
sysDataSize *= 2;
|
||||
}
|
||||
var sysData = new byte[sysDataSize];
|
||||
systemStream.Flush();
|
||||
systemStream.Position = 0;
|
||||
systemStream.Read(sysData, 0, (int)systemStream.Length);
|
||||
|
||||
|
||||
var gfxPageSize = 0x2000;
|
||||
while (gfxPageSize != graphicsPageSize)
|
||||
{
|
||||
gfxPageSize *= 2;
|
||||
}
|
||||
var gfxDataSize = graphicsPageCount * gfxPageSize;
|
||||
var gfxData = new byte[gfxDataSize];
|
||||
graphicsStream.Flush();
|
||||
graphicsStream.Position = 0;
|
||||
graphicsStream.Read(gfxData, 0, (int)graphicsStream.Length);
|
||||
|
||||
|
||||
|
||||
uint uv = (uint)version;
|
||||
uint sv = (uv >> 4) & 0xF;
|
||||
uint gv = (uv >> 0) & 0xF;
|
||||
|
||||
//uint sf = RpfResourceFileEntry.GetFlagsFromSize(sysDataSize, sv);
|
||||
//uint gf = RpfResourceFileEntry.GetFlagsFromSize(gfxDataSize, gv); //TODO: might be broken...
|
||||
|
||||
uint sf = RpfResourceFileEntry.GetFlagsFromBlocks((uint)systemPageCount, (uint)systemPageSize, sv);
|
||||
uint gf = RpfResourceFileEntry.GetFlagsFromBlocks((uint)graphicsPageCount, (uint)graphicsPageSize, gv);
|
||||
|
||||
var tdatasize = sysDataSize + gfxDataSize;
|
||||
var tdata = new byte[tdatasize];
|
||||
Buffer.BlockCopy(sysData, 0, tdata, 0, sysDataSize);
|
||||
Buffer.BlockCopy(gfxData, 0, tdata, sysDataSize, gfxDataSize);
|
||||
|
||||
|
||||
var cdata = Compress(tdata);
|
||||
|
||||
|
||||
var dataSize = 16 + cdata.Length;// sysDataSize + gfxDataSize;
|
||||
var data = new byte[dataSize];
|
||||
|
||||
byte[] h1 = BitConverter.GetBytes((uint)0x37435352);
|
||||
byte[] h2 = BitConverter.GetBytes((int)version);
|
||||
byte[] h3 = BitConverter.GetBytes(sf);
|
||||
byte[] h4 = BitConverter.GetBytes(gf);
|
||||
Buffer.BlockCopy(h1, 0, data, 0, 4);
|
||||
Buffer.BlockCopy(h2, 0, data, 4, 4);
|
||||
Buffer.BlockCopy(h3, 0, data, 8, 4);
|
||||
Buffer.BlockCopy(h4, 0, data, 12, 4);
|
||||
Buffer.BlockCopy(cdata, 0, data, 16, cdata.Length);
|
||||
//Buffer.BlockCopy(sysData, 0, data, 16, sysDataSize);
|
||||
//Buffer.BlockCopy(gfxData, 0, data, 16 + sysDataSize, gfxDataSize);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static byte[] AddResourceHeader(RpfResourceFileEntry entry, byte[] data)
|
||||
{
|
||||
if (data == null) return null;
|
||||
byte[] newdata = new byte[data.Length + 16];
|
||||
byte[] h1 = BitConverter.GetBytes((uint)0x37435352);
|
||||
byte[] h2 = BitConverter.GetBytes(entry.Version);
|
||||
byte[] h3 = BitConverter.GetBytes(entry.SystemFlags);
|
||||
byte[] h4 = BitConverter.GetBytes(entry.GraphicsFlags);
|
||||
Buffer.BlockCopy(h1, 0, newdata, 0, 4);
|
||||
Buffer.BlockCopy(h2, 0, newdata, 4, 4);
|
||||
Buffer.BlockCopy(h3, 0, newdata, 8, 4);
|
||||
Buffer.BlockCopy(h4, 0, newdata, 12, 4);
|
||||
Buffer.BlockCopy(data, 0, newdata, 16, data.Length);
|
||||
return newdata;
|
||||
}
|
||||
|
||||
|
||||
public static byte[] Compress(byte[] data)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
{
|
||||
DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress, true);
|
||||
ds.Write(data, 0, data.Length);
|
||||
ds.Close();
|
||||
byte[] deflated = ms.GetBuffer();
|
||||
byte[] outbuf = new byte[ms.Length]; //need to copy to the right size buffer...
|
||||
Array.Copy(deflated, outbuf, outbuf.Length);
|
||||
return outbuf;
|
||||
}
|
||||
}
|
||||
public static byte[] Decompress(byte[] data)
|
||||
{
|
||||
using (MemoryStream ms = new MemoryStream(data))
|
||||
{
|
||||
DeflateStream ds = new DeflateStream(ms, CompressionMode.Decompress);
|
||||
MemoryStream outstr = new MemoryStream();
|
||||
ds.CopyTo(outstr);
|
||||
byte[] deflated = outstr.GetBuffer();
|
||||
byte[] outbuf = new byte[outstr.Length]; //need to copy to the right size buffer...
|
||||
Array.Copy(deflated, outbuf, outbuf.Length);
|
||||
return outbuf;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,750 @@
|
||||
/*
|
||||
Copyright(c) 2015 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//shamelessly stolen and mangled
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a resource data reader.
|
||||
/// </summary>
|
||||
public class ResourceDataReader : DataReader
|
||||
{
|
||||
private const long SYSTEM_BASE = 0x50000000;
|
||||
private const long GRAPHICS_BASE = 0x60000000;
|
||||
|
||||
private Stream systemStream;
|
||||
private Stream graphicsStream;
|
||||
|
||||
// this is a dictionary that contains all the resource blocks
|
||||
// which were read from this resource reader
|
||||
private Dictionary<long, List<IResourceBlock>> blockPool;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position within the underlying stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new resource data reader for the specified system- and graphics-stream.
|
||||
/// </summary>
|
||||
public ResourceDataReader(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
this.systemStream = systemStream;
|
||||
this.graphicsStream = graphicsStream;
|
||||
this.blockPool = new Dictionary<long, List<IResourceBlock>>();
|
||||
}
|
||||
|
||||
public ResourceDataReader(RpfResourceFileEntry resentry, byte[] data, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
var systemSize = resentry.SystemSize;
|
||||
var graphicsSize = resentry.GraphicsSize;
|
||||
|
||||
this.systemStream = new MemoryStream(data, 0, systemSize);
|
||||
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
|
||||
this.blockPool = new Dictionary<long, List<IResourceBlock>>();
|
||||
Position = 0x50000000;
|
||||
}
|
||||
|
||||
public ResourceDataReader(int systemSize, int graphicsSize, byte[] data, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
this.systemStream = new MemoryStream(data, 0, systemSize);
|
||||
this.graphicsStream = new MemoryStream(data, systemSize, graphicsSize);
|
||||
this.blockPool = new Dictionary<long, List<IResourceBlock>>();
|
||||
Position = 0x50000000;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from the underlying stream. This is the only method that directly accesses
|
||||
/// the data in the underlying stream.
|
||||
/// </summary>
|
||||
protected override byte[] ReadFromStream(int count, bool ignoreEndianess = false)
|
||||
{
|
||||
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
|
||||
{
|
||||
// read from system stream...
|
||||
|
||||
systemStream.Position = Position & ~0x50000000;
|
||||
|
||||
var buffer = new byte[count];
|
||||
systemStream.Read(buffer, 0, count);
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
Array.Reverse(buffer);
|
||||
}
|
||||
|
||||
Position = systemStream.Position | 0x50000000;
|
||||
return buffer;
|
||||
|
||||
}
|
||||
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
|
||||
{
|
||||
// read from graphic stream...
|
||||
|
||||
graphicsStream.Position = Position & ~0x60000000;
|
||||
|
||||
var buffer = new byte[count];
|
||||
graphicsStream.Read(buffer, 0, count);
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
Array.Reverse(buffer);
|
||||
}
|
||||
|
||||
Position = graphicsStream.Position | 0x60000000;
|
||||
return buffer;
|
||||
}
|
||||
throw new Exception("illegal position!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a block.
|
||||
/// </summary>
|
||||
public T ReadBlock<T>(params object[] parameters) where T : IResourceBlock, new()
|
||||
{
|
||||
// make sure to return the same object if the same
|
||||
// block is read again...
|
||||
if (blockPool.ContainsKey(Position))
|
||||
{
|
||||
var blocks = blockPool[Position];
|
||||
foreach (var block in blocks)
|
||||
if (block is T)
|
||||
{
|
||||
Position += block.BlockLength;
|
||||
|
||||
// since a resource block of the same type
|
||||
// has been found at the same address, return it
|
||||
return (T)block;
|
||||
}
|
||||
}
|
||||
|
||||
var result = new T();
|
||||
|
||||
|
||||
// replace with correct type...
|
||||
if (result is IResourceXXSystemBlock)
|
||||
result = (T)((IResourceXXSystemBlock)result).GetType(this, parameters);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
|
||||
|
||||
// add block to the block pool...
|
||||
if (blockPool.ContainsKey(Position))
|
||||
{
|
||||
blockPool[Position].Add(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
var blocks = new List<IResourceBlock>();
|
||||
blocks.Add(result);
|
||||
blockPool.Add(Position, blocks);
|
||||
}
|
||||
|
||||
var classPosition = Position;
|
||||
result.Read(this, parameters);
|
||||
//result.Position = classPosition; //TODO: need this if writing stuff!
|
||||
return (T)result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a block at a specified position.
|
||||
/// </summary>
|
||||
public T ReadBlockAt<T>(ulong position, params object[] parameters) where T : IResourceBlock, new()
|
||||
{
|
||||
if (position != 0)
|
||||
{
|
||||
var positionBackup = Position;
|
||||
|
||||
Position = (long)position;
|
||||
var result = ReadBlock<T>(parameters);
|
||||
Position = positionBackup;
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public byte[] ReadBytesAt(ulong position, uint count)
|
||||
{
|
||||
long pos = (long)position;
|
||||
if ((pos <= 0) || (count == 0)) return null;
|
||||
var posbackup = Position;
|
||||
Position = pos;
|
||||
var result = ReadBytes((int)count);
|
||||
Position = posbackup;
|
||||
return result;
|
||||
}
|
||||
public ushort[] ReadUshortsAt(ulong position, uint count)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new ushort[count];
|
||||
var length = count * 2;
|
||||
byte[] data = ReadBytesAt(position, length);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result2 = new ushort[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result2[i] = ReadUInt16();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
return result;
|
||||
}
|
||||
public uint[] ReadUintsAt(ulong position, uint count)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new uint[count];
|
||||
var length = count * 4;
|
||||
byte[] data = ReadBytesAt(position, length);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result = new uint[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = ReadUInt32();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
return result;
|
||||
}
|
||||
public ulong[] ReadUlongsAt(ulong position, uint count)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new ulong[count];
|
||||
var length = count * 8;
|
||||
byte[] data = ReadBytesAt(position, length);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result = new ulong[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = ReadUInt64();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
return result;
|
||||
}
|
||||
public float[] ReadFloatsAt(ulong position, uint count)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
var result = new float[count];
|
||||
var length = count * 4;
|
||||
byte[] data = ReadBytesAt(position, length);
|
||||
Buffer.BlockCopy(data, 0, result, 0, (int)length);
|
||||
|
||||
//var posbackup = Position;
|
||||
//Position = position;
|
||||
//var result = new float[count];
|
||||
//for (uint i = 0; i < count; i++)
|
||||
//{
|
||||
// result[i] = ReadSingle();
|
||||
//}
|
||||
//Position = posbackup;
|
||||
return result;
|
||||
}
|
||||
public T[] ReadStructsAt<T>(ulong position, uint count)//, uint structsize)
|
||||
{
|
||||
if ((position <= 0) || (count == 0)) return null;
|
||||
|
||||
uint structsize = (uint)Marshal.SizeOf(typeof(T));
|
||||
var length = count * structsize;
|
||||
byte[] data = ReadBytesAt(position, length);
|
||||
|
||||
//var result2 = new T[count];
|
||||
//Buffer.BlockCopy(data, 0, result2, 0, (int)length); //error: "object must be an array of primitives" :(
|
||||
|
||||
var result = new T[count];
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
result[i] = Marshal.PtrToStructure<T>(h + (int)(i * structsize));
|
||||
}
|
||||
handle.Free();
|
||||
|
||||
return result;
|
||||
}
|
||||
public T[] ReadStructs<T>(uint count)
|
||||
{
|
||||
uint structsize = (uint)Marshal.SizeOf(typeof(T));
|
||||
var result = new T[count];
|
||||
var length = count * structsize;
|
||||
byte[] data = ReadBytes((int)length);
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
for (uint i = 0; i < count; i++)
|
||||
{
|
||||
result[i] = Marshal.PtrToStructure<T>(h + (int)(i * structsize));
|
||||
}
|
||||
handle.Free();
|
||||
return result;
|
||||
}
|
||||
|
||||
public T ReadStruct<T>() where T : struct
|
||||
{
|
||||
uint structsize = (uint)Marshal.SizeOf(typeof(T));
|
||||
var length = structsize;
|
||||
byte[] data = ReadBytes((int)length);
|
||||
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
var h = handle.AddrOfPinnedObject();
|
||||
var result = Marshal.PtrToStructure<T>(h);
|
||||
handle.Free();
|
||||
return result;
|
||||
}
|
||||
|
||||
public T ReadStructAt<T>(long position) where T : struct
|
||||
{
|
||||
if ((position <= 0)) return default(T);
|
||||
var posbackup = Position;
|
||||
Position = (long)position;
|
||||
var result = ReadStruct<T>();
|
||||
Position = posbackup;
|
||||
return result;
|
||||
}
|
||||
|
||||
public string ReadStringAt(ulong position)
|
||||
{
|
||||
long newpos = (long)position;
|
||||
if ((newpos <= 0)) return null;
|
||||
var lastpos = Position;
|
||||
Position = newpos;
|
||||
var result = ReadString();
|
||||
Position = lastpos;
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a resource data writer.
|
||||
/// </summary>
|
||||
public class ResourceDataWriter : DataWriter
|
||||
{
|
||||
private const long SYSTEM_BASE = 0x50000000;
|
||||
private const long GRAPHICS_BASE = 0x60000000;
|
||||
|
||||
private Stream systemStream;
|
||||
private Stream graphicsStream;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the underlying stream.
|
||||
/// </summary>
|
||||
public override long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position within the underlying stream.
|
||||
/// </summary>
|
||||
public override long Position
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new resource data reader for the specified system- and graphics-stream.
|
||||
/// </summary>
|
||||
public ResourceDataWriter(Stream systemStream, Stream graphicsStream, Endianess endianess = Endianess.LittleEndian)
|
||||
: base((Stream)null, endianess)
|
||||
{
|
||||
this.systemStream = systemStream;
|
||||
this.graphicsStream = graphicsStream;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the underlying stream. This is the only method that directly accesses
|
||||
/// the data in the underlying stream.
|
||||
/// </summary>
|
||||
protected override void WriteToStream(byte[] value, bool ignoreEndianess = true)
|
||||
{
|
||||
if ((Position & SYSTEM_BASE) == SYSTEM_BASE)
|
||||
{
|
||||
// write to system stream...
|
||||
|
||||
systemStream.Position = Position & ~SYSTEM_BASE;
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
var buf = (byte[])value.Clone();
|
||||
Array.Reverse(buf);
|
||||
systemStream.Write(buf, 0, buf.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
systemStream.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
Position = systemStream.Position | 0x50000000;
|
||||
return;
|
||||
|
||||
}
|
||||
if ((Position & GRAPHICS_BASE) == GRAPHICS_BASE)
|
||||
{
|
||||
// write to graphic stream...
|
||||
|
||||
graphicsStream.Position = Position & ~GRAPHICS_BASE;
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
var buf = (byte[])value.Clone();
|
||||
Array.Reverse(buf);
|
||||
graphicsStream.Write(buf, 0, buf.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
graphicsStream.Write(value, 0, value.Length);
|
||||
}
|
||||
|
||||
Position = graphicsStream.Position | 0x60000000;
|
||||
return;
|
||||
}
|
||||
|
||||
throw new Exception("illegal position!");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a block.
|
||||
/// </summary>
|
||||
public void WriteBlock(IResourceBlock value)
|
||||
{
|
||||
value.Write(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void WriteStruct<T>(T val) where T : struct
|
||||
{
|
||||
int size = Marshal.SizeOf(typeof(T));
|
||||
byte[] arr = new byte[size];
|
||||
IntPtr ptr = Marshal.AllocHGlobal(size);
|
||||
Marshal.StructureToPtr(val, ptr, true);
|
||||
Marshal.Copy(ptr, arr, 0, size);
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
Write(arr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block in a resource file.
|
||||
/// </summary>
|
||||
public interface IResourceBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the data block.
|
||||
/// </summary>
|
||||
long FilePosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data block.
|
||||
/// </summary>
|
||||
long BlockLength { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data block.
|
||||
/// </summary>
|
||||
void Read(ResourceDataReader reader, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data block.
|
||||
/// </summary>
|
||||
void Write(ResourceDataWriter writer, params object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the system segement in a resource file.
|
||||
/// </summary>
|
||||
public interface IResourceSystemBlock : IResourceBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are part of this block.
|
||||
/// </summary>
|
||||
Tuple<long, IResourceBlock>[] GetParts();
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are referenced by this block.
|
||||
/// </summary>
|
||||
IResourceBlock[] GetReferences();
|
||||
}
|
||||
|
||||
public interface IResourceXXSystemBlock : IResourceSystemBlock
|
||||
{
|
||||
IResourceSystemBlock GetType(ResourceDataReader reader, params object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the graphics segmenet in a resource file.
|
||||
/// </summary>
|
||||
public interface IResourceGraphicsBlock : IResourceBlock
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the system segement in a resource file.
|
||||
/// </summary>
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public abstract class ResourceSystemBlock : IResourceSystemBlock
|
||||
{
|
||||
private long position;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the data block.
|
||||
/// </summary>
|
||||
public virtual long FilePosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return position;
|
||||
}
|
||||
set
|
||||
{
|
||||
position = value;
|
||||
foreach (var part in GetParts())
|
||||
{
|
||||
part.Item2.FilePosition = value + part.Item1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data block.
|
||||
/// </summary>
|
||||
public abstract long BlockLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data block.
|
||||
/// </summary>
|
||||
public abstract void Read(ResourceDataReader reader, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data block.
|
||||
/// </summary>
|
||||
public abstract void Write(ResourceDataWriter writer, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are part of this block.
|
||||
/// </summary>
|
||||
public virtual Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks that are referenced by this block.
|
||||
/// </summary>
|
||||
public virtual IResourceBlock[] GetReferences()
|
||||
{
|
||||
return new IResourceBlock[0];
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ResourecTypedSystemBlock : ResourceSystemBlock, IResourceXXSystemBlock
|
||||
{
|
||||
public abstract IResourceSystemBlock GetType(ResourceDataReader reader, params object[] parameters);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a data block of the graphics segmenet in a resource file.
|
||||
/// </summary>
|
||||
public abstract class ResourceGraphicsBlock : IResourceGraphicsBlock
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the data block.
|
||||
/// </summary>
|
||||
public virtual long FilePosition
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the data block.
|
||||
/// </summary>
|
||||
public abstract long BlockLength
|
||||
{
|
||||
get;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data block.
|
||||
/// </summary>
|
||||
public abstract void Read(ResourceDataReader reader, params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data block.
|
||||
/// </summary>
|
||||
public abstract void Write(ResourceDataWriter writer, params object[] parameters);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class ResourceSystemDataBlock : ResourceSystemBlock //used for writing resources.
|
||||
{
|
||||
public byte[] Data { get; set; }
|
||||
public int DataLength { get; set; }
|
||||
|
||||
public override long BlockLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return (Data != null) ? Data.Length : DataLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ResourceSystemDataBlock(byte[] data)
|
||||
{
|
||||
Data = data;
|
||||
DataLength = (Data != null) ? Data.Length : 0;
|
||||
}
|
||||
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
Data = reader.ReadBytes(DataLength);
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
writer.Write(Data);
|
||||
}
|
||||
}
|
||||
|
||||
public class ResourceSystemStructBlock<T> : ResourceSystemBlock where T : struct //used for writing resources.
|
||||
{
|
||||
public T[] Items { get; set; }
|
||||
public int ItemCount { get; set; }
|
||||
public int StructureSize { get; set; }
|
||||
|
||||
public override long BlockLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((Items != null) ? Items.Length : ItemCount) * StructureSize;
|
||||
}
|
||||
}
|
||||
|
||||
public ResourceSystemStructBlock(T[] items)
|
||||
{
|
||||
Items = items;
|
||||
ItemCount = (Items != null) ? Items.Length : 0;
|
||||
StructureSize = Marshal.SizeOf(typeof(T));
|
||||
}
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
int datalength = ItemCount * StructureSize;
|
||||
byte[] data = reader.ReadBytes(datalength);
|
||||
Items = MetaTypes.ConvertDataArray<T>(data, 0, ItemCount);
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
byte[] data = MetaTypes.ConvertArrayToBytes(Items);
|
||||
writer.Write(data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public interface ResourceDataStruct
|
||||
//{
|
||||
// void Read(ResourceDataReader reader);
|
||||
// void Write(ResourceDataWriter writer);
|
||||
//}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
Copyright(c) 2015 Neodymium
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
//shamelessly stolen and mangled
|
||||
|
||||
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class ResourceFileBase : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 16; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint FileVFT { get; set; }
|
||||
public uint FileUnknown { get; set; }
|
||||
public ulong FilePagesInfoPointer { get; set; }
|
||||
|
||||
// reference data
|
||||
public ResourcePagesInfo FilePagesInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.FileVFT = reader.ReadUInt32();
|
||||
this.FileUnknown = reader.ReadUInt32();
|
||||
this.FilePagesInfoPointer = reader.ReadUInt64();
|
||||
|
||||
// read reference data
|
||||
this.FilePagesInfo = reader.ReadBlockAt<ResourcePagesInfo>(
|
||||
this.FilePagesInfoPointer // offset
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// update structure data
|
||||
this.FilePagesInfoPointer = (ulong)(this.FilePagesInfo != null ? this.FilePagesInfo.FilePosition : 0);
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.FileVFT);
|
||||
writer.Write(this.FileUnknown);
|
||||
writer.Write(this.FilePagesInfoPointer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks which are referenced by this block.
|
||||
/// </summary>
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
if (FilePagesInfo != null) list.Add(FilePagesInfo);
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class ResourcePagesInfo : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 20; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint Unknown_0h { get; set; }
|
||||
public uint Unknown_4h { get; set; }
|
||||
public byte SystemPagesCount { get; set; }
|
||||
public byte GraphicsPagesCount { get; set; }
|
||||
public ushort Unknown_Ah { get; set; }
|
||||
public uint Unknown_Ch { get; set; }
|
||||
public uint Unknown_10h { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.Unknown_0h = reader.ReadUInt32();
|
||||
this.Unknown_4h = reader.ReadUInt32();
|
||||
this.SystemPagesCount = reader.ReadByte();
|
||||
this.GraphicsPagesCount = reader.ReadByte();
|
||||
this.Unknown_Ah = reader.ReadUInt16();
|
||||
this.Unknown_Ch = reader.ReadUInt32();
|
||||
this.Unknown_10h = reader.ReadUInt32();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_0h);
|
||||
writer.Write(this.Unknown_4h);
|
||||
writer.Write(this.SystemPagesCount);
|
||||
writer.Write(this.GraphicsPagesCount);
|
||||
writer.Write(this.Unknown_Ah);
|
||||
writer.Write(this.Unknown_Ch);
|
||||
writer.Write(this.Unknown_10h);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return SystemPagesCount.ToString() + ", " + GraphicsPagesCount.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,445 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureDictionary : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return 64;
|
||||
}
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint Unknown_10h { get; set; } // 0x00000000
|
||||
public uint Unknown_14h { get; set; } // 0x00000000
|
||||
public uint Unknown_18h { get; set; } // 0x00000001
|
||||
public uint Unknown_1Ch { get; set; } // 0x00000000
|
||||
public ResourceSimpleList64Ptr TextureNameHashesPtr { get; set; }
|
||||
public uint[] TextureNameHashes { get; set; }
|
||||
public ResourcePointerList64<Texture> Textures { get; set; }
|
||||
|
||||
public Dictionary<uint, Texture> Dict { get; set; }
|
||||
public long MemoryUsage
|
||||
{
|
||||
get
|
||||
{
|
||||
long val = 0;
|
||||
if ((Textures != null) && (Textures.data_items != null))
|
||||
{
|
||||
foreach (var tex in Textures.data_items)
|
||||
{
|
||||
if (tex != null)
|
||||
{
|
||||
val += tex.MemoryUsage;
|
||||
}
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
public TextureDictionary()
|
||||
{
|
||||
//this.TextureNameHashes = new ResourceSimpleList64<uint_r>();
|
||||
this.Textures = new ResourcePointerList64<Texture>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
// read structure data
|
||||
this.Unknown_10h = reader.ReadUInt32();
|
||||
this.Unknown_14h = reader.ReadUInt32();
|
||||
this.Unknown_18h = reader.ReadUInt32();
|
||||
this.Unknown_1Ch = reader.ReadUInt32();
|
||||
this.TextureNameHashesPtr = reader.ReadStruct<ResourceSimpleList64Ptr>();
|
||||
this.TextureNameHashes = reader.ReadUintsAt(this.TextureNameHashesPtr.EntriesPointer, this.TextureNameHashesPtr.EntriesCount);
|
||||
//this.TextureNameHashes = reader.ReadBlock<ResourceSimpleList64<uint_r>>();
|
||||
this.Textures = reader.ReadBlock<ResourcePointerList64<Texture>>();
|
||||
|
||||
var dict = new Dictionary<uint, Texture>();
|
||||
if ((Textures != null) && (Textures.data_items != null) && (TextureNameHashes != null))
|
||||
{
|
||||
for (int i = 0; (i < Textures.data_items.Length) && (i < TextureNameHashes.Length); i++)
|
||||
{
|
||||
var tex = Textures.data_items[i];
|
||||
var hash = TextureNameHashes[i];
|
||||
dict[hash] = tex;
|
||||
}
|
||||
}
|
||||
Dict = new Dictionary<uint, Texture>(dict);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_10h);
|
||||
writer.Write(this.Unknown_14h);
|
||||
writer.Write(this.Unknown_18h);
|
||||
writer.Write(this.Unknown_1Ch);
|
||||
//writer.WriteBlock(this.TextureNameHashes); //TODO: fix!
|
||||
//writer.WriteBlock(this.Textures);
|
||||
}
|
||||
|
||||
public override Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[] {
|
||||
//new Tuple<long, IResourceBlock>(0x20, TextureNameHashes), //TODO: fix!
|
||||
new Tuple<long, IResourceBlock>(0x30, Textures)
|
||||
};
|
||||
}
|
||||
|
||||
public Dictionary<uint, Texture> GetDictionary()
|
||||
{
|
||||
Dictionary<uint, Texture> td = new Dictionary<uint, Texture>();
|
||||
if ((Textures != null) && (Textures.data_items != null))
|
||||
{
|
||||
var texs = Textures.data_items;
|
||||
var hashes = TextureNameHashes;
|
||||
for (int i = 0; (i < texs.Length) && (i < hashes.Length); i++)
|
||||
{
|
||||
td.Add(hashes[i], texs[i]);
|
||||
}
|
||||
}
|
||||
return td;
|
||||
}
|
||||
|
||||
public Texture Lookup(uint hash)
|
||||
{
|
||||
Texture tex = null;
|
||||
if (Dict != null)
|
||||
{
|
||||
Dict.TryGetValue(hash, out tex);
|
||||
}
|
||||
return tex;
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureBase : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 64; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint VFT { get; set; }
|
||||
public uint Unknown_4h { get; set; } // 0x00000001
|
||||
public uint Unknown_8h { get; set; } // 0x00000000
|
||||
public uint Unknown_Ch { get; set; } // 0x00000000
|
||||
public uint Unknown_10h { get; set; } // 0x00000000
|
||||
public uint Unknown_14h { get; set; } // 0x00000000
|
||||
public uint Unknown_18h { get; set; } // 0x00000000
|
||||
public uint Unknown_1Ch { get; set; } // 0x00000000
|
||||
public uint Unknown_20h { get; set; } // 0x00000000
|
||||
public uint Unknown_24h { get; set; } // 0x00000000
|
||||
public ulong NamePointer { get; set; }
|
||||
public uint Unknown_30h { get; set; }
|
||||
public uint Unknown_34h { get; set; } // 0x00000000
|
||||
public uint Unknown_38h { get; set; } // 0x00000000
|
||||
public uint Unknown_3Ch { get; set; } // 0x00000000
|
||||
|
||||
// reference data
|
||||
public string Name { get; set; }
|
||||
public uint NameHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.VFT = reader.ReadUInt32();
|
||||
this.Unknown_4h = reader.ReadUInt32();
|
||||
this.Unknown_8h = reader.ReadUInt32();
|
||||
this.Unknown_Ch = reader.ReadUInt32();
|
||||
this.Unknown_10h = reader.ReadUInt32();
|
||||
this.Unknown_14h = reader.ReadUInt32();
|
||||
this.Unknown_18h = reader.ReadUInt32();
|
||||
this.Unknown_1Ch = reader.ReadUInt32();
|
||||
this.Unknown_20h = reader.ReadUInt32();
|
||||
this.Unknown_24h = reader.ReadUInt32();
|
||||
this.NamePointer = reader.ReadUInt64();
|
||||
this.Unknown_30h = reader.ReadUInt32();
|
||||
this.Unknown_34h = reader.ReadUInt32();
|
||||
this.Unknown_38h = reader.ReadUInt32();
|
||||
this.Unknown_3Ch = reader.ReadUInt32();
|
||||
|
||||
// read reference data
|
||||
this.Name = reader.ReadStringAt( //BlockAt<string_r>(
|
||||
this.NamePointer // offset
|
||||
);
|
||||
|
||||
if (!string.IsNullOrEmpty(Name))
|
||||
{
|
||||
NameHash = JenkHash.GenHash(Name.ToLowerInvariant());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// update structure data
|
||||
//this.NamePointer = (ulong)(this.Name != null ? this.Name.Position : 0); //TODO: fix
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.VFT);
|
||||
writer.Write(this.Unknown_4h);
|
||||
writer.Write(this.Unknown_8h);
|
||||
writer.Write(this.Unknown_Ch);
|
||||
writer.Write(this.Unknown_10h);
|
||||
writer.Write(this.Unknown_14h);
|
||||
writer.Write(this.Unknown_18h);
|
||||
writer.Write(this.Unknown_1Ch);
|
||||
writer.Write(this.Unknown_20h);
|
||||
writer.Write(this.Unknown_24h);
|
||||
writer.Write(this.NamePointer);
|
||||
writer.Write(this.Unknown_30h);
|
||||
writer.Write(this.Unknown_34h);
|
||||
writer.Write(this.Unknown_38h);
|
||||
writer.Write(this.Unknown_3Ch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks which are referenced by this block.
|
||||
/// </summary>
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
//if (Name != null) list.Add(Name); //TODO: fix
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "TextureBase: " + Name;
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class Texture : TextureBase
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 144; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public uint Unknown_40h { get; set; }
|
||||
public uint Unknown_44h { get; set; } // 0x00000000
|
||||
public uint Unknown_48h { get; set; }
|
||||
public uint Unknown_4Ch { get; set; } // 0x00000000
|
||||
public ushort Width { get; set; }
|
||||
public ushort Height { get; set; }
|
||||
public ushort Unknown_54h { get; set; } // 0x0001
|
||||
public ushort Stride { get; set; }
|
||||
public TextureFormat Format { get; set; }
|
||||
public byte Unknown_5Ch { get; set; } // 0x00
|
||||
public byte Levels { get; set; }
|
||||
public ushort Unknown_5Eh { get; set; } // 0x0000
|
||||
public uint Unknown_60h { get; set; } // 0x00000000
|
||||
public uint Unknown_64h { get; set; } // 0x00000000
|
||||
public uint Unknown_68h { get; set; } // 0x00000000
|
||||
public uint Unknown_6Ch { get; set; } // 0x00000000
|
||||
public ulong DataPointer { get; set; }
|
||||
public uint Unknown_78h { get; set; } // 0x00000000
|
||||
public uint Unknown_7Ch { get; set; } // 0x00000000
|
||||
public uint Unknown_80h { get; set; } // 0x00000000
|
||||
public uint Unknown_84h { get; set; } // 0x00000000
|
||||
public uint Unknown_88h { get; set; } // 0x00000000
|
||||
public uint Unknown_8Ch { get; set; } // 0x00000000
|
||||
|
||||
// reference data
|
||||
public TextureData Data { get; set; }
|
||||
|
||||
public long MemoryUsage
|
||||
{
|
||||
get
|
||||
{
|
||||
long val = 0;
|
||||
if (Data != null)
|
||||
{
|
||||
val += Data.FullData.LongLength;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
// read structure data
|
||||
this.Unknown_40h = reader.ReadUInt32();
|
||||
this.Unknown_44h = reader.ReadUInt32();
|
||||
this.Unknown_48h = reader.ReadUInt32();
|
||||
this.Unknown_4Ch = reader.ReadUInt32();
|
||||
this.Width = reader.ReadUInt16();
|
||||
this.Height = reader.ReadUInt16();
|
||||
this.Unknown_54h = reader.ReadUInt16();
|
||||
this.Stride = reader.ReadUInt16();
|
||||
this.Format = (TextureFormat)reader.ReadUInt32();
|
||||
this.Unknown_5Ch = reader.ReadByte();
|
||||
this.Levels = reader.ReadByte();
|
||||
this.Unknown_5Eh = reader.ReadUInt16();
|
||||
this.Unknown_60h = reader.ReadUInt32();
|
||||
this.Unknown_64h = reader.ReadUInt32();
|
||||
this.Unknown_68h = reader.ReadUInt32();
|
||||
this.Unknown_6Ch = reader.ReadUInt32();
|
||||
this.DataPointer = reader.ReadUInt64();
|
||||
this.Unknown_78h = reader.ReadUInt32();
|
||||
this.Unknown_7Ch = reader.ReadUInt32();
|
||||
this.Unknown_80h = reader.ReadUInt32();
|
||||
this.Unknown_84h = reader.ReadUInt32();
|
||||
this.Unknown_88h = reader.ReadUInt32();
|
||||
this.Unknown_8Ch = reader.ReadUInt32();
|
||||
|
||||
// read reference data
|
||||
this.Data = reader.ReadBlockAt<TextureData>(
|
||||
this.DataPointer, // offset
|
||||
this.Format,
|
||||
this.Width,
|
||||
this.Height,
|
||||
this.Levels,
|
||||
this.Stride
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
this.DataPointer = (ulong)this.Data.FilePosition;
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_40h);
|
||||
writer.Write(this.Unknown_44h);
|
||||
writer.Write(this.Unknown_48h);
|
||||
writer.Write(this.Unknown_4Ch);
|
||||
writer.Write(this.Width);
|
||||
writer.Write(this.Height);
|
||||
writer.Write(this.Unknown_54h);
|
||||
writer.Write(this.Stride);
|
||||
writer.Write((uint)this.Format);
|
||||
writer.Write(this.Unknown_5Ch);
|
||||
writer.Write(this.Levels);
|
||||
writer.Write(this.Unknown_5Eh);
|
||||
writer.Write(this.Unknown_60h);
|
||||
writer.Write(this.Unknown_64h);
|
||||
writer.Write(this.Unknown_68h);
|
||||
writer.Write(this.Unknown_6Ch);
|
||||
writer.Write(this.DataPointer);
|
||||
writer.Write(this.Unknown_78h);
|
||||
writer.Write(this.Unknown_7Ch);
|
||||
writer.Write(this.Unknown_80h);
|
||||
writer.Write(this.Unknown_84h);
|
||||
writer.Write(this.Unknown_88h);
|
||||
writer.Write(this.Unknown_8Ch);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks which are referenced by this block.
|
||||
/// </summary>
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>(base.GetReferences());
|
||||
list.Add(Data);
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return "Texture: " + Width.ToString() + "x" + Height.ToString() + ": " + Name;
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class TextureData : ResourceGraphicsBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get
|
||||
{
|
||||
return FullData.Length;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] FullData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
uint format = Convert.ToUInt32(parameters[0]);
|
||||
int Width = Convert.ToInt32(parameters[1]);
|
||||
int Height = Convert.ToInt32(parameters[2]);
|
||||
int Levels = Convert.ToInt32(parameters[3]);
|
||||
int Stride = Convert.ToInt32(parameters[4]);
|
||||
|
||||
int fullLength = 0;
|
||||
int length = Stride * Height;
|
||||
for (int i = 0; i < Levels; i++)
|
||||
{
|
||||
fullLength += length;
|
||||
length /= 4;
|
||||
}
|
||||
|
||||
FullData = reader.ReadBytes(fullLength);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
writer.Write(FullData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public enum TextureFormat : uint
|
||||
{
|
||||
D3DFMT_A8R8G8B8 = 21,
|
||||
D3DFMT_A1R5G5B5 = 25,
|
||||
D3DFMT_A8 = 28,
|
||||
D3DFMT_A8B8G8R8 = 32,
|
||||
D3DFMT_L8 = 50,
|
||||
|
||||
// fourCC
|
||||
D3DFMT_DXT1 = 0x31545844,
|
||||
D3DFMT_DXT3 = 0x33545844,
|
||||
D3DFMT_DXT5 = 0x35545844,
|
||||
D3DFMT_ATI1 = 0x31495441,
|
||||
D3DFMT_ATI2 = 0x32495441,
|
||||
D3DFMT_BC7 = 0x20374342,
|
||||
|
||||
//UNKNOWN
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
public class VehicleRecordList : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength => 0x20;
|
||||
|
||||
public ResourceSimpleList64<VehicleRecordEntry> Entries;
|
||||
|
||||
public VehicleRecordList()
|
||||
{
|
||||
this.Entries = new ResourceSimpleList64<VehicleRecordEntry>();
|
||||
}
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
this.Entries = reader.ReadBlock<ResourceSimpleList64<VehicleRecordEntry>>();
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
writer.WriteBlock(this.Entries);
|
||||
}
|
||||
|
||||
public override Tuple<long, IResourceBlock>[] GetParts()
|
||||
{
|
||||
return new Tuple<long, IResourceBlock>[] {
|
||||
new Tuple<long, IResourceBlock>(16, Entries)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class VehicleRecordEntry : ResourceSystemBlock
|
||||
{
|
||||
// this looks exactly like an rrr entry:
|
||||
// -> http://www.gtamodding.com/wiki/Carrec
|
||||
|
||||
public override long BlockLength => 0x20;
|
||||
|
||||
// structure data
|
||||
public uint Time;
|
||||
public short VelocityX;
|
||||
public short VelocityY;
|
||||
public short VelocityZ;
|
||||
public sbyte RightX;
|
||||
public sbyte RightY;
|
||||
public sbyte RightZ;
|
||||
public sbyte TopX;
|
||||
public sbyte TopY;
|
||||
public sbyte TopZ;
|
||||
public byte SteeringAngle;
|
||||
public byte GasPedalPower;
|
||||
public byte BrakePedalPower;
|
||||
public byte HandbrakeUsed;
|
||||
public float PositionX;
|
||||
public float PositionY;
|
||||
public float PositionZ;
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.Time = reader.ReadUInt32();
|
||||
this.VelocityX = reader.ReadInt16();
|
||||
this.VelocityY = reader.ReadInt16();
|
||||
this.VelocityZ = reader.ReadInt16();
|
||||
this.RightX = (sbyte)reader.ReadByte();
|
||||
this.RightY = (sbyte)reader.ReadByte();
|
||||
this.RightZ = (sbyte)reader.ReadByte();
|
||||
this.TopX = (sbyte)reader.ReadByte();
|
||||
this.TopY = (sbyte)reader.ReadByte();
|
||||
this.TopZ = (sbyte)reader.ReadByte();
|
||||
this.SteeringAngle = reader.ReadByte();
|
||||
this.GasPedalPower = reader.ReadByte();
|
||||
this.BrakePedalPower = reader.ReadByte();
|
||||
this.HandbrakeUsed = reader.ReadByte();
|
||||
this.PositionX = reader.ReadSingle();
|
||||
this.PositionY = reader.ReadSingle();
|
||||
this.PositionZ = reader.ReadSingle();
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// write structure data
|
||||
writer.Write(this.Time);
|
||||
writer.Write(this.VelocityX);
|
||||
writer.Write(this.VelocityY);
|
||||
writer.Write(this.VelocityZ);
|
||||
writer.Write(this.RightX);
|
||||
writer.Write(this.RightY);
|
||||
writer.Write(this.RightZ);
|
||||
writer.Write(this.TopX);
|
||||
writer.Write(this.TopY);
|
||||
writer.Write(this.TopZ);
|
||||
writer.Write(this.SteeringAngle);
|
||||
writer.Write(this.GasPedalPower);
|
||||
writer.Write(this.BrakePedalPower);
|
||||
writer.Write(this.HandbrakeUsed);
|
||||
writer.Write(this.PositionX);
|
||||
writer.Write(this.PositionY);
|
||||
writer.Write(this.PositionZ);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
|
||||
public enum VertexType : uint
|
||||
{
|
||||
Default = 89, //PNCT
|
||||
DefaultEx = 16473, //PNCTX
|
||||
PNCCT = 121,
|
||||
PNCCTTTT = 1017,
|
||||
PCCNCCTTX = 16639,
|
||||
PCCNCCT = 127,
|
||||
PNCTTTX = 16857,
|
||||
PNCTTX = 16601,
|
||||
PNCTTTX_2 = 19545,
|
||||
PNCTTTX_3 = 17113,
|
||||
PNCCTTX = 16633,
|
||||
PNCCTTX_2 = 17017,
|
||||
PNCCTTTX = 17145,
|
||||
PCCNCCTX = 16511,
|
||||
PCCNCTX = 16479,
|
||||
PCCNCT = 95,
|
||||
PNCCTT = 249,
|
||||
PNCCTX = 16505,
|
||||
PCT = 81,
|
||||
PT = 65,
|
||||
PTT = 193,
|
||||
PNC = 25,
|
||||
PC = 17,
|
||||
PCC = 7,
|
||||
PCCH2H4 = 2147500121, //0x80004059 (16473 + 0x80000000) DefaultEx Cloth?
|
||||
PNCH2 = 2147483737, //0x80000059 (89 + 0x80000000) Default Cloth?
|
||||
PNCTTTTX = 19673, //normal_spec_detail_dpm_vertdecal_tnt
|
||||
PNCTTTT = 985,
|
||||
PCCNCCTT = 255,
|
||||
PCTT = 209,
|
||||
PCCCCT = 119,
|
||||
PCCNC = 31,
|
||||
PCCNCTT = 223,
|
||||
PCCNCTTX = 16607,
|
||||
PCCNCTTT = 479,
|
||||
PNCTT = 217,
|
||||
PNCTTT = 473,
|
||||
}
|
||||
|
||||
|
||||
//vertex data to be used by the editor. TODO: maybe move somewhere else.
|
||||
public struct EditorVertex
|
||||
{
|
||||
public Vector3 Position;
|
||||
public uint Colour;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
public class WaypointRecordList : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength => 0x30;
|
||||
|
||||
public uint Unknown_10h; // 0x00000000
|
||||
public uint Unknown_14h; // 0x00000000
|
||||
public ulong EntriesPointer;
|
||||
public uint EntriesCount;
|
||||
public uint Unknown_24h; // 0x00000000
|
||||
public uint Unknown_28h; // 0x00000000
|
||||
public uint Unknown_2Ch; // 0x00000000
|
||||
|
||||
public ResourceSimpleArray<WaypointRecordEntry> Entries;
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
base.Read(reader, parameters);
|
||||
|
||||
this.Unknown_10h = reader.ReadUInt32();
|
||||
this.Unknown_14h = reader.ReadUInt32();
|
||||
this.EntriesPointer = reader.ReadUInt64();
|
||||
this.EntriesCount = reader.ReadUInt32();
|
||||
this.Unknown_24h = reader.ReadUInt32();
|
||||
this.Unknown_28h = reader.ReadUInt32();
|
||||
this.Unknown_2Ch = reader.ReadUInt32();
|
||||
|
||||
this.Entries = reader.ReadBlockAt<ResourceSimpleArray<WaypointRecordEntry>>(
|
||||
this.EntriesPointer, // offset
|
||||
this.EntriesCount
|
||||
);
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
//// update structure data
|
||||
//this.EntriesPointer = (ulong)(this.Entries?.Position ?? 0);
|
||||
//this.EntriesCount = (uint)(this.Entries?.Count ?? 0);
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_10h);
|
||||
writer.Write(this.Unknown_14h);
|
||||
writer.Write(this.EntriesPointer);
|
||||
writer.Write(this.EntriesCount);
|
||||
writer.Write(this.Unknown_24h);
|
||||
writer.Write(this.Unknown_28h);
|
||||
writer.Write(this.Unknown_2Ch);
|
||||
}
|
||||
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>(base.GetReferences());
|
||||
if (Entries != null) list.Add(Entries);
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class WaypointRecordEntry : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength => 20;
|
||||
|
||||
public float PositionX;
|
||||
public float PositionY;
|
||||
public float PositionZ;
|
||||
public ushort Unk0;
|
||||
public ushort Unk1;
|
||||
public ushort Unk2;
|
||||
public ushort Unk3;
|
||||
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.PositionX = reader.ReadSingle();
|
||||
this.PositionY = reader.ReadSingle();
|
||||
this.PositionZ = reader.ReadSingle();
|
||||
this.Unk0 = reader.ReadUInt16();
|
||||
this.Unk1 = reader.ReadUInt16();
|
||||
this.Unk2 = reader.ReadUInt16();
|
||||
this.Unk3 = reader.ReadUInt16();
|
||||
}
|
||||
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// write structure data
|
||||
writer.Write(this.PositionX);
|
||||
writer.Write(this.PositionY);
|
||||
writer.Write(this.PositionZ);
|
||||
writer.Write(this.Unk0);
|
||||
writer.Write(this.Unk1);
|
||||
writer.Write(this.Unk2);
|
||||
writer.Write(this.Unk3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,492 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class RpfManager
|
||||
{
|
||||
//for caching and management of RPF file data.
|
||||
|
||||
public string Folder { get; private set; }
|
||||
public string[] ExcludePaths { get; set; }
|
||||
public bool EnableMods { get; set; }
|
||||
public Action<string> UpdateStatus { get; private set; }
|
||||
public Action<string> ErrorLog { get; private set; }
|
||||
|
||||
public List<RpfFile> BaseRpfs { get; private set; }
|
||||
public List<RpfFile> ModRpfs { get; private set; }
|
||||
public List<RpfFile> DlcRpfs { get; private set; }
|
||||
public List<RpfFile> AllRpfs { get; private set; }
|
||||
public List<RpfFile> DlcNoModRpfs { get; private set; }
|
||||
public List<RpfFile> AllNoModRpfs { get; private set; }
|
||||
public Dictionary<string, RpfFile> RpfDict { get; private set; }
|
||||
public Dictionary<string, RpfEntry> EntryDict { get; private set; }
|
||||
public Dictionary<string, RpfFile> ModRpfDict { get; private set; }
|
||||
public Dictionary<string, RpfEntry> ModEntryDict { get; private set; }
|
||||
|
||||
public volatile bool IsInited = false;
|
||||
|
||||
public void Init(string folder, Action<string> updateStatus, Action<string> errorLog, bool rootOnly = false, bool buildIndex = true)
|
||||
{
|
||||
UpdateStatus = updateStatus;
|
||||
ErrorLog = errorLog;
|
||||
|
||||
string replpath = folder + "\\";
|
||||
var sopt = rootOnly ? SearchOption.TopDirectoryOnly : SearchOption.AllDirectories;
|
||||
string[] allfiles = Directory.GetFiles(folder, "*.rpf", sopt);
|
||||
|
||||
BaseRpfs = new List<RpfFile>();
|
||||
ModRpfs = new List<RpfFile>();
|
||||
DlcRpfs = new List<RpfFile>();
|
||||
AllRpfs = new List<RpfFile>();
|
||||
DlcNoModRpfs = new List<RpfFile>();
|
||||
AllNoModRpfs = new List<RpfFile>();
|
||||
RpfDict = new Dictionary<string, RpfFile>();
|
||||
EntryDict = new Dictionary<string, RpfEntry>();
|
||||
ModRpfDict = new Dictionary<string, RpfFile>();
|
||||
ModEntryDict = new Dictionary<string, RpfEntry>();
|
||||
|
||||
foreach (string rpfpath in allfiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
RpfFile rf = new RpfFile(rpfpath, rpfpath.Replace(replpath, ""));
|
||||
|
||||
if (ExcludePaths != null)
|
||||
{
|
||||
bool excl = false;
|
||||
for (int i = 0; i < ExcludePaths.Length; i++)
|
||||
{
|
||||
if (rf.Path.StartsWith(ExcludePaths[i]))
|
||||
{
|
||||
excl = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (excl) continue; //skip files in exclude paths.
|
||||
}
|
||||
|
||||
rf.ScanStructure(updateStatus, errorLog);
|
||||
|
||||
AddRpfFile(rf, false, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
errorLog(rpfpath + ": " + ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (buildIndex)
|
||||
{
|
||||
updateStatus("Building jenkindex...");
|
||||
BuildBaseJenkIndex();
|
||||
}
|
||||
|
||||
updateStatus("Scan complete");
|
||||
|
||||
IsInited = true;
|
||||
}
|
||||
|
||||
public void Init(List<RpfFile> allRpfs)
|
||||
{
|
||||
//fast init used by RPF explorer's File cache
|
||||
AllRpfs = allRpfs;
|
||||
|
||||
BaseRpfs = new List<RpfFile>();
|
||||
ModRpfs = new List<RpfFile>();
|
||||
DlcRpfs = new List<RpfFile>();
|
||||
DlcNoModRpfs = new List<RpfFile>();
|
||||
AllNoModRpfs = new List<RpfFile>();
|
||||
RpfDict = new Dictionary<string, RpfFile>();
|
||||
EntryDict = new Dictionary<string, RpfEntry>();
|
||||
ModRpfDict = new Dictionary<string, RpfFile>();
|
||||
ModEntryDict = new Dictionary<string, RpfEntry>();
|
||||
foreach (var rpf in allRpfs)
|
||||
{
|
||||
RpfDict[rpf.Path] = rpf;
|
||||
if (rpf.AllEntries == null) continue;
|
||||
foreach (var entry in rpf.AllEntries)
|
||||
{
|
||||
EntryDict[entry.Path] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
BuildBaseJenkIndex();
|
||||
|
||||
IsInited = true;
|
||||
}
|
||||
|
||||
|
||||
private void AddRpfFile(RpfFile file, bool isdlc, bool ismod)
|
||||
{
|
||||
isdlc = isdlc || (file.NameLower == "dlc.rpf") || (file.NameLower == "update.rpf");
|
||||
ismod = ismod || (file.Path.StartsWith("mods\\"));
|
||||
|
||||
if (file.AllEntries != null)
|
||||
{
|
||||
AllRpfs.Add(file);
|
||||
if (!ismod)
|
||||
{
|
||||
AllNoModRpfs.Add(file);
|
||||
}
|
||||
if (isdlc)
|
||||
{
|
||||
DlcRpfs.Add(file);
|
||||
if (!ismod)
|
||||
{
|
||||
DlcNoModRpfs.Add(file);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ismod)
|
||||
{
|
||||
ModRpfs.Add(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
BaseRpfs.Add(file);
|
||||
}
|
||||
}
|
||||
if (ismod)
|
||||
{
|
||||
ModRpfDict[file.Path.Substring(5)] = file;
|
||||
}
|
||||
|
||||
RpfDict[file.Path] = file;
|
||||
|
||||
foreach (RpfEntry entry in file.AllEntries)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(entry.Name))
|
||||
{
|
||||
if (ismod)
|
||||
{
|
||||
ModEntryDict[entry.Path] = entry;
|
||||
ModEntryDict[entry.Path.Substring(5)] = entry;
|
||||
}
|
||||
else
|
||||
{
|
||||
EntryDict[entry.Path] = entry;
|
||||
}
|
||||
|
||||
if (entry is RpfFileEntry)
|
||||
{
|
||||
RpfFileEntry fentry = entry as RpfFileEntry;
|
||||
entry.NameHash = JenkHash.GenHash(entry.NameLower);
|
||||
int ind = entry.NameLower.LastIndexOf('.');
|
||||
entry.ShortNameHash = (ind > 0) ? JenkHash.GenHash(entry.NameLower.Substring(0, ind)) : entry.NameHash;
|
||||
if (entry.ShortNameHash != 0)
|
||||
{
|
||||
//EntryHashDict[entry.ShortNameHash] = entry;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
file.LastError = ex.ToString();
|
||||
file.LastException = ex;
|
||||
ErrorLog(entry.Path + ": " + ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (file.Children != null)
|
||||
{
|
||||
foreach (RpfFile cfile in file.Children)
|
||||
{
|
||||
AddRpfFile(cfile, isdlc, ismod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public RpfFile FindRpfFile(string path)
|
||||
{
|
||||
RpfFile file = null; //check the dictionary
|
||||
|
||||
if (EnableMods && ModRpfDict.TryGetValue(path, out file))
|
||||
{
|
||||
return file;
|
||||
}
|
||||
|
||||
if (RpfDict.TryGetValue(path, out file))
|
||||
{
|
||||
return file;
|
||||
}
|
||||
|
||||
string lpath = path.ToLowerInvariant(); //try look at names etc
|
||||
foreach (RpfFile tfile in AllRpfs)
|
||||
{
|
||||
if (tfile.NameLower == lpath)
|
||||
{
|
||||
return tfile;
|
||||
}
|
||||
if (tfile.Path == lpath)
|
||||
{
|
||||
return tfile;
|
||||
}
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
public RpfEntry GetEntry(string path)
|
||||
{
|
||||
RpfEntry entry;
|
||||
string pathl = path.ToLowerInvariant();
|
||||
if (EnableMods && ModEntryDict.TryGetValue(pathl, out entry))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
EntryDict.TryGetValue(pathl, out entry);
|
||||
if (entry == null)
|
||||
{
|
||||
pathl = pathl.Replace("/", "\\");
|
||||
pathl = pathl.Replace("common:", "common.rpf");
|
||||
if (EnableMods && ModEntryDict.TryGetValue(pathl, out entry))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
EntryDict.TryGetValue(pathl, out entry);
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
public byte[] GetFileData(string path)
|
||||
{
|
||||
byte[] data = null;
|
||||
RpfFileEntry entry = GetEntry(path) as RpfFileEntry;
|
||||
if (entry != null)
|
||||
{
|
||||
data = entry.File.ExtractFile(entry);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
public string GetFileUTF8Text(string path)
|
||||
{
|
||||
byte[] bytes = GetFileData(path);
|
||||
if (bytes == null)
|
||||
{ return string.Empty; } //file not found..
|
||||
if ((bytes.Length > 3) && (bytes[0] == 0xEF) && (bytes[1] == 0xBB) && (bytes[2] == 0xBF))
|
||||
{
|
||||
byte[] newb = new byte[bytes.Length - 3];
|
||||
for (int i = 3; i < bytes.Length; i++)
|
||||
{
|
||||
newb[i - 3] = bytes[i];
|
||||
}
|
||||
bytes = newb; //trim starting 3 "magic" bytes?
|
||||
}
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
public XmlDocument GetFileXml(string path)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
string text = GetFileUTF8Text(path);
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
doc.LoadXml(text);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public T GetFile<T>(string path) where T : class, PackedFile, new()
|
||||
{
|
||||
T file = null;
|
||||
byte[] data = null;
|
||||
RpfFileEntry entry = GetEntry(path) as RpfFileEntry;
|
||||
if (entry != null)
|
||||
{
|
||||
data = entry.File.ExtractFile(entry);
|
||||
}
|
||||
if (data != null)
|
||||
{
|
||||
file = new T();
|
||||
file.Load(data, entry);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
public T GetFile<T>(RpfEntry e) where T : class, PackedFile, new()
|
||||
{
|
||||
T file = null;
|
||||
byte[] data = null;
|
||||
RpfFileEntry entry = e as RpfFileEntry;
|
||||
if (entry != null)
|
||||
{
|
||||
data = entry.File.ExtractFile(entry);
|
||||
}
|
||||
if (data != null)
|
||||
{
|
||||
file = new T();
|
||||
file.Load(data, entry);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
public bool LoadFile<T>(T file, RpfEntry e) where T : class, PackedFile
|
||||
{
|
||||
byte[] data = null;
|
||||
RpfFileEntry entry = e as RpfFileEntry;
|
||||
if (entry != null)
|
||||
{
|
||||
data = entry.File.ExtractFile(entry);
|
||||
}
|
||||
if (data != null)
|
||||
{
|
||||
file.Load(data, entry);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void BuildBaseJenkIndex()
|
||||
{
|
||||
JenkIndex.Clear();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (RpfFile file in AllRpfs)
|
||||
{
|
||||
try
|
||||
{
|
||||
JenkIndex.Ensure(file.Name);
|
||||
foreach (RpfEntry entry in file.AllEntries)
|
||||
{
|
||||
var nlow = entry.NameLower;
|
||||
if (string.IsNullOrEmpty(nlow)) continue;
|
||||
//JenkIndex.Ensure(entry.Name);
|
||||
//JenkIndex.Ensure(nlow);
|
||||
int ind = nlow.LastIndexOf('.');
|
||||
if (ind > 0)
|
||||
{
|
||||
JenkIndex.Ensure(entry.Name.Substring(0, ind));
|
||||
JenkIndex.Ensure(nlow.Substring(0, ind));
|
||||
|
||||
//if (ind < entry.Name.Length - 2)
|
||||
//{
|
||||
// JenkIndex.Ensure(entry.Name.Substring(0, ind) + ".#" + entry.Name.Substring(ind + 2));
|
||||
// JenkIndex.Ensure(entry.NameLower.Substring(0, ind) + ".#" + entry.NameLower.Substring(ind + 2));
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
JenkIndex.Ensure(entry.Name);
|
||||
JenkIndex.Ensure(nlow);
|
||||
}
|
||||
if (nlow.EndsWith(".ydr") || nlow.EndsWith(".yft"))
|
||||
{
|
||||
var sname = nlow.Substring(0, nlow.Length - 4);
|
||||
JenkIndex.Ensure(sname + "_lod");
|
||||
JenkIndex.Ensure(sname + "_loda");
|
||||
JenkIndex.Ensure(sname + "_lodb");
|
||||
}
|
||||
if (nlow.EndsWith(".ydd"))
|
||||
{
|
||||
if (nlow.EndsWith("_children.ydd"))
|
||||
{
|
||||
var strn = nlow.Substring(0, nlow.Length - 13);
|
||||
JenkIndex.Ensure(strn);
|
||||
JenkIndex.Ensure(strn + "_lod");
|
||||
JenkIndex.Ensure(strn + "_loda");
|
||||
JenkIndex.Ensure(strn + "_lodb");
|
||||
}
|
||||
var idx = nlow.LastIndexOf('_');
|
||||
if (idx > 0)
|
||||
{
|
||||
var str1 = nlow.Substring(0, idx);
|
||||
var idx2 = str1.LastIndexOf('_');
|
||||
if (idx2 > 0)
|
||||
{
|
||||
var str2 = str1.Substring(0, idx2);
|
||||
JenkIndex.Ensure(str2 + "_lod");
|
||||
var maxi = 100;
|
||||
for (int i = 1; i <= maxi; i++)
|
||||
{
|
||||
var str3 = str2 + "_" + i.ToString().PadLeft(2, '0');
|
||||
//JenkIndex.Ensure(str3);
|
||||
JenkIndex.Ensure(str3 + "_lod");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nlow.EndsWith(".awc")) //create audio container path hashes...
|
||||
{
|
||||
string[] parts = entry.Path.Split('\\');
|
||||
int pl = parts.Length;
|
||||
if (pl > 2)
|
||||
{
|
||||
string fn = parts[pl - 1];
|
||||
string fd = parts[pl - 2];
|
||||
string hpath = fn.Substring(0, fn.Length - 4);
|
||||
if (fd.EndsWith(".rpf"))
|
||||
{
|
||||
fd = fd.Substring(0, fd.Length - 4);
|
||||
}
|
||||
hpath = fd + "/" + hpath;
|
||||
if (parts[pl - 3] != "sfx")
|
||||
{ }//no hit
|
||||
|
||||
JenkIndex.Ensure(hpath);
|
||||
}
|
||||
}
|
||||
if (nlow.EndsWith(".nametable"))
|
||||
{
|
||||
RpfBinaryFileEntry binfe = entry as RpfBinaryFileEntry;
|
||||
if (binfe != null)
|
||||
{
|
||||
byte[] data = file.ExtractFile(binfe);
|
||||
if (data != null)
|
||||
{
|
||||
sb.Clear();
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
byte c = data[i];
|
||||
if (c == 0)
|
||||
{
|
||||
string str = sb.ToString();
|
||||
if (!string.IsNullOrEmpty(str))
|
||||
{
|
||||
string strl = str.ToLowerInvariant();
|
||||
//JenkIndex.Ensure(str);
|
||||
JenkIndex.Ensure(strl);
|
||||
|
||||
////DirMod_Sounds_ entries apparently can be used to infer SP audio strings
|
||||
////no luck here yet though
|
||||
//if (strl.StartsWith("dirmod_sounds_") && (strl.Length > 14))
|
||||
//{
|
||||
// strl = strl.Substring(14);
|
||||
// JenkIndex.Ensure(strl);
|
||||
//}
|
||||
}
|
||||
sb.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append((char)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
//failing silently!! not so good really
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user