mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2026-05-15 00:54:47 +08:00
R26_dev8 - First public commit
This commit is contained in:
@@ -0,0 +1,702 @@
|
||||
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; }
|
||||
|
||||
|
||||
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...
|
||||
}
|
||||
|
||||
Magic = BitConverter.ToUInt32(data, 0);
|
||||
|
||||
Endianess endianess = Endianess.LittleEndian;
|
||||
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 UnkMinusOne { get; set; }
|
||||
public ushort SamplesPerSecond { get; set; }
|
||||
public ushort Unk1 { get; set; }
|
||||
public ushort Unk2 { get; set; }
|
||||
public ushort Unk3 { get; set; }
|
||||
public ushort Unk4 { get; set; }
|
||||
public byte Unk5 { get; set; }
|
||||
public byte Unk6 { get; set; }
|
||||
|
||||
public AwcFormatChunk(DataReader r)
|
||||
{
|
||||
Samples = r.ReadUInt32();
|
||||
UnkMinusOne = r.ReadInt32();
|
||||
SamplesPerSecond = r.ReadUInt16();
|
||||
Unk1 = r.ReadUInt16();
|
||||
Unk2 = r.ReadUInt16();
|
||||
Unk3 = r.ReadUInt16();
|
||||
Unk4 = r.ReadUInt16();
|
||||
Unk5 = r.ReadByte();
|
||||
Unk6 = r.ReadByte();
|
||||
|
||||
//Apparently sometimes this struct is longer? TODO: fix??
|
||||
//r.ReadUInt16();
|
||||
//r.ReadUInt16();
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Unk1.ToString() + ", " + Unk6.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 = 4;//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 fmt = "ADPCM";
|
||||
switch (Format.Unk6)
|
||||
{
|
||||
case 4:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var hz = Format?.SamplesPerSecond ?? 0;
|
||||
|
||||
return fmt + ((hz > 0) ? (", " + hz.ToString() + " Hz") : "");
|
||||
}
|
||||
}
|
||||
public string LengthStr
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Format == null) return "0:00";
|
||||
float sec = (float)Format.Samples / Format.SamplesPerSecond;
|
||||
TimeSpan ts = TimeSpan.FromSeconds(sec);
|
||||
return ts.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 Stream GetWavStream()
|
||||
{
|
||||
MemoryStream ms = new MemoryStream();
|
||||
BinaryWriter w = new BinaryWriter(ms);
|
||||
|
||||
|
||||
//see http://icculus.org/SDL_sound/downloads/external_documentation/wavecomp.htm
|
||||
//see https://github.com/naudio/NAudio/blob/master/NAudio/Wave/WaveFormats/AdpcmWaveFormat.cs
|
||||
//see https://msdn.microsoft.com/en-us/library/windows/desktop/ff538799(v=vs.85).aspx
|
||||
|
||||
|
||||
int sampleCount = SampleCount;
|
||||
int samplesPerSec = SamplesPerSecond;
|
||||
//short sampleSize = (short)((BitsPerSample / 8) * Channels);//2
|
||||
//int avgBytesPerSec = sampleSize * samplesPerSec;
|
||||
short blockAlign = 512;
|
||||
|
||||
short samplesPerBlock = (short)((((blockAlign - (7 * Channels)) * 8) / (BitsPerSample * Channels)) + 2);
|
||||
int avgBytesPerSec = ((samplesPerSec / samplesPerBlock) * blockAlign);
|
||||
|
||||
w.Write("RIFF".ToCharArray());
|
||||
w.Write(0); //file size written later...
|
||||
w.Write("WAVE".ToCharArray());
|
||||
w.Write("fmt ".ToCharArray());
|
||||
w.Write(50); //(PCM:16) //header size
|
||||
w.Write((short)2); //pcm format tag 1=PCM, 2=ADPCM
|
||||
w.Write(Channels);
|
||||
w.Write(samplesPerSec);
|
||||
w.Write(avgBytesPerSec);
|
||||
w.Write(blockAlign);// sampleSize);
|
||||
w.Write(BitsPerSample);
|
||||
w.Write((short)32);//extra byte count for WAVEFORMATEX
|
||||
|
||||
w.Write(samplesPerBlock);
|
||||
w.Write((short)7);//num coefficients
|
||||
w.Write((short)256); //coeff 0
|
||||
w.Write((short)0);
|
||||
w.Write((short)512); //coeff 1
|
||||
w.Write((short)-256);
|
||||
w.Write((short)0); //coeff 2
|
||||
w.Write((short)0);
|
||||
w.Write((short)192); //coeff 3
|
||||
w.Write((short)64);
|
||||
w.Write((short)240); //coeff 4
|
||||
w.Write((short)0);
|
||||
w.Write((short)460); //coeff 5
|
||||
w.Write((short)-208);
|
||||
w.Write((short)392); //coeff 6
|
||||
w.Write((short)-232);
|
||||
|
||||
w.Write("data".ToCharArray());
|
||||
w.Write(0); //data size written later...
|
||||
|
||||
if (sampleCount != 0)
|
||||
{
|
||||
|
||||
//var sc = sampleCount * sampleSize;
|
||||
var datalen = Data.Length;
|
||||
w.Write(Data);
|
||||
}
|
||||
else
|
||||
{
|
||||
w.Write(Data);
|
||||
}
|
||||
|
||||
ms.Position = 4;
|
||||
w.Write((int)ms.Length - 8);
|
||||
|
||||
ms.Position = 74;// 40;
|
||||
w.Write((int)ms.Length - 78);// 44);
|
||||
|
||||
w.Flush();
|
||||
|
||||
ms.Position = 0;
|
||||
return ms;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
[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,491 @@
|
||||
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 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);
|
||||
}
|
||||
string versionstr = sb.ToString();
|
||||
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 override string ToString()
|
||||
{
|
||||
if (FileEntry != null)
|
||||
{
|
||||
return FileEntry.ToString();
|
||||
}
|
||||
return base.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class CacheFileDate
|
||||
{
|
||||
public MetaHash FileName { get; set; }
|
||||
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 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();
|
||||
Unk18 = br.ReadUInt32();
|
||||
}
|
||||
|
||||
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 Unk02 { get; set; }
|
||||
public byte Unk03 { get; set; }
|
||||
public byte Unk04 { get; set; }
|
||||
public byte Unk05 { 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());
|
||||
Unk02 = br.ReadByte();
|
||||
Unk03 = br.ReadByte();
|
||||
Unk04 = br.ReadByte();
|
||||
Unk05 = br.ReadByte();
|
||||
|
||||
if (Unk05 == 0xFE)
|
||||
{
|
||||
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).ToLower();
|
||||
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,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
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,800 @@
|
||||
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 RelFile : PackedFile
|
||||
{
|
||||
public RpfFileEntry FileEntry { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public uint Type { get; set; }
|
||||
public uint DataLength { get; set; }
|
||||
public byte[] DataBlock { get; set; }
|
||||
public uint DataUnkVal { get; set; }
|
||||
public uint NameTableLength { get; set; }
|
||||
public uint NameTableCount { get; set; }
|
||||
public uint[] NameTableOffsets { get; set; }
|
||||
public string[] NameTable { get; set; }
|
||||
public uint IndexCount { get; set; }
|
||||
public uint IndexStringFlags { get; set; }
|
||||
public RelIndexHash[] IndexHashes { get; set; }
|
||||
public RelIndexString[] IndexStrings { get; set; }
|
||||
public uint Unk05Count { get; set; }
|
||||
public uint[] Unk05Arr { get; set; }
|
||||
public MetaHash[] Unk05Hashes { get; set; }
|
||||
public uint Unk06Count { get; set; }
|
||||
public uint[] Unk06Arr { get; set; }
|
||||
public MetaHash[] Unk06Hashes { get; set; }
|
||||
|
||||
public RelData[] RelDatas { get; set; }
|
||||
public RelData[] RelDatasSorted { get; set; }
|
||||
//testing zone for decoding .rel audio files.
|
||||
|
||||
public RelFile()
|
||||
{
|
||||
}
|
||||
public RelFile(RpfFileEntry entry)
|
||||
{
|
||||
FileEntry = entry;
|
||||
}
|
||||
|
||||
public void Load(byte[] data, RpfFileEntry entry)
|
||||
{
|
||||
FileEntry = entry;
|
||||
Name = entry.Name;
|
||||
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
BinaryReader br = new BinaryReader(ms);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
Type = br.ReadUInt32(); //type/version?
|
||||
|
||||
DataLength = br.ReadUInt32(); //length of data block
|
||||
DataBlock = br.ReadBytes((int)DataLength); //data block... synth infos? script?
|
||||
|
||||
NameTableLength = br.ReadUInt32(); //length of this nametable block
|
||||
NameTableCount = br.ReadUInt32();
|
||||
if (NameTableCount > 0)
|
||||
{
|
||||
uint[] d02 = new uint[NameTableCount]; //string offsets
|
||||
for (uint i = 0; i < NameTableCount; i++)
|
||||
{
|
||||
d02[i] = br.ReadUInt32();
|
||||
}
|
||||
NameTableOffsets = d02;
|
||||
string[] names = new string[NameTableCount];
|
||||
for (uint i = 0; i < NameTableCount; i++)
|
||||
{
|
||||
sb.Clear();
|
||||
while (true)
|
||||
{
|
||||
char c = (char)br.ReadByte();
|
||||
if (c != 0) sb.Append(c);
|
||||
else break;
|
||||
}
|
||||
names[i] = sb.ToString();
|
||||
}
|
||||
NameTable = names;
|
||||
}
|
||||
|
||||
IndexCount = br.ReadUInt32(); //count of index items
|
||||
if (IndexCount > 0)
|
||||
{
|
||||
//checking NameTableLength here doesn't make sense!
|
||||
if ((Type == 4) && (NameTableLength == 4))//audioconfig.dat4.rel
|
||||
{
|
||||
IndexStringFlags = br.ReadUInt32(); //what is this? 2524
|
||||
RelIndexString[] indexstrs = new RelIndexString[IndexCount];
|
||||
for (uint i = 0; i < IndexCount; i++)
|
||||
{
|
||||
byte sl = br.ReadByte();
|
||||
sb.Clear();
|
||||
for (int j = 0; j < sl; j++)
|
||||
{
|
||||
char c = (char)br.ReadByte();
|
||||
if (c != 0) sb.Append(c);
|
||||
}
|
||||
RelIndexString cunk01 = new RelIndexString();
|
||||
cunk01.Name = sb.ToString();
|
||||
cunk01.Offset = br.ReadUInt32();
|
||||
cunk01.Length = br.ReadUInt32();
|
||||
indexstrs[i] = cunk01;
|
||||
}
|
||||
IndexStrings = indexstrs;
|
||||
}
|
||||
else //for all other .rel files...
|
||||
{
|
||||
RelIndexHash[] indexhashes = new RelIndexHash[IndexCount];
|
||||
for (uint i = 0; i < IndexCount; i++)
|
||||
{
|
||||
RelIndexHash unk01 = new RelIndexHash();
|
||||
unk01.Name = new MetaHash(br.ReadUInt32());
|
||||
unk01.Offset = br.ReadUInt32();
|
||||
unk01.Length = br.ReadUInt32();
|
||||
indexhashes[i] = unk01;
|
||||
}
|
||||
IndexHashes = indexhashes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Unk05Count = br.ReadUInt32();
|
||||
if (Unk05Count != 0)
|
||||
{
|
||||
uint[] d05 = new uint[Unk05Count];
|
||||
MetaHash[] d05h = new MetaHash[Unk05Count];
|
||||
for (uint i = 0; i < Unk05Count; i++)
|
||||
{
|
||||
d05[i] = br.ReadUInt32();
|
||||
|
||||
var pos = ms.Position;
|
||||
ms.Position = d05[i];
|
||||
d05h[i] = new MetaHash(br.ReadUInt32());
|
||||
ms.Position = pos;
|
||||
}
|
||||
Unk05Arr = d05;
|
||||
Unk05Hashes = d05h;
|
||||
}
|
||||
|
||||
Unk06Count = br.ReadUInt32();
|
||||
if (Unk06Count != 0)
|
||||
{
|
||||
uint[] d06 = new uint[Unk06Count];
|
||||
MetaHash[] d06h = new MetaHash[Unk06Count];
|
||||
for (uint i = 0; i < Unk06Count; i++)
|
||||
{
|
||||
d06[i] = br.ReadUInt32();
|
||||
|
||||
var pos = ms.Position;
|
||||
ms.Position = d06[i];
|
||||
d06h[i] = new MetaHash(br.ReadUInt32());
|
||||
ms.Position = pos;
|
||||
}
|
||||
Unk06Arr = d06;
|
||||
Unk06Hashes = d06h;
|
||||
}
|
||||
|
||||
if (ms.Position != ms.Length)
|
||||
{ }
|
||||
//EOF!
|
||||
|
||||
br.Dispose();
|
||||
ms.Dispose();
|
||||
|
||||
|
||||
ParseDataBlock();
|
||||
}
|
||||
|
||||
|
||||
private void ParseDataBlock()
|
||||
{
|
||||
|
||||
|
||||
|
||||
MemoryStream ms = new MemoryStream(DataBlock);
|
||||
BinaryReader br = new BinaryReader(ms);
|
||||
|
||||
DataUnkVal = br.ReadUInt32(); //3 bytes used... for? ..version?
|
||||
switch (DataUnkVal)
|
||||
{
|
||||
case 5252715: //dlcbusiness_amp.dat10.rel
|
||||
case 5301323: //dlcbeach_game.dat149.rel
|
||||
case 5378673: //dlcmpheist_game.dat150.rel
|
||||
case 5750395: //dlcbeach_game.dat150.rel
|
||||
case 6353778: //dlcbeach_game.dat151.rel
|
||||
case 6894089: //dlcpilotschool_game.dat151.rel
|
||||
case 6978435: //dlcxmas2_amp.dat10.rel
|
||||
case 7126027: //audioconfig.dat4.rel
|
||||
case 7314721: //dlcmpheist_amp.dat10.rel
|
||||
case 7516460: //dlcpd03_game.dat151.rel
|
||||
case 7917027: //dlcluxe_amp.dat10.rel
|
||||
case 7921508: //dlcluxe_game.dat151.rel
|
||||
case 8149475: //dlcluxe2_amp.dat10.rel
|
||||
case 8751734: //dlcsfx1_game.dat151.rel
|
||||
case 9028036: //dlchalloween_amp.dat10.rel
|
||||
case 9037528: //dlclowrider_amp.dat10.rel
|
||||
case 9458585: //dlcapartment_amp.dat10.rel
|
||||
case 9486222: //dlcapartment_mix.dat15.rel
|
||||
case 9806108: //mpvalentines2_amp.dat10.rel
|
||||
case 9813679: //dlcjanuary2016_amp.dat10.rel
|
||||
case 10269543://dlclow2_amp.dat10.rel
|
||||
case 10891463://dlcexec1_amp.dat10.rel
|
||||
case 11171338://dlcstunt_amp.dat10.rel
|
||||
case 11918985://dlcbiker_amp.dat10.rel
|
||||
case 12470522://dlcimportexport_amp.dat10.rel
|
||||
case 12974726://audioconfig.dat4.rel
|
||||
case 13117164://dlcspecialraces_amp.dat10.rel
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
List<RelData> reldatas = new List<RelData>();
|
||||
if (IndexHashes != null)
|
||||
{
|
||||
foreach (var indexhash in IndexHashes)
|
||||
{
|
||||
ms.Position = indexhash.Offset;
|
||||
RelData d = new RelData();
|
||||
d.NameHash = indexhash.Name;
|
||||
d.Offset = indexhash.Offset;
|
||||
d.Length = indexhash.Length;
|
||||
d.Data = br.ReadBytes((int)indexhash.Length);
|
||||
reldatas.Add(d);
|
||||
}
|
||||
}
|
||||
else if (IndexStrings != null)
|
||||
{
|
||||
foreach (var indexstr in IndexStrings)
|
||||
{
|
||||
ms.Position = indexstr.Offset;
|
||||
RelData d = new RelData();
|
||||
d.Name = indexstr.Name;
|
||||
d.Offset = indexstr.Offset;
|
||||
d.Length = indexstr.Length;
|
||||
d.Data = br.ReadBytes((int)indexstr.Length);
|
||||
reldatas.Add(d);
|
||||
}
|
||||
}
|
||||
RelDatas = reldatas.ToArray();
|
||||
|
||||
reldatas.Sort((d1, d2) => d1.Offset.CompareTo(d2.Offset));
|
||||
RelDatasSorted = reldatas.ToArray();
|
||||
|
||||
|
||||
br.Dispose();
|
||||
ms.Dispose();
|
||||
|
||||
|
||||
foreach (var d in RelDatas)
|
||||
{
|
||||
using (BinaryReader dbr = new BinaryReader(new MemoryStream(d.Data)))
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case 4: //00000100 //speech.dat4.rel, audioconfig.dat4.rel
|
||||
ParseData4(d, dbr);
|
||||
break;
|
||||
case 10: //00001010 //amp.dat10.rel
|
||||
ParseData10(d, dbr);
|
||||
break;
|
||||
case 15: //00001111 //mix.dat15.rel
|
||||
ParseData15(d, dbr);
|
||||
break;
|
||||
case 16: //00010000 //curves.dat16.rel
|
||||
ParseData16(d, dbr);
|
||||
break;
|
||||
case 22: //00010110 //categories.dat22.rel
|
||||
ParseData22(d, dbr);
|
||||
break;
|
||||
case 54: //00110110 //sounds.dat54.rel
|
||||
ParseData54(d, dbr);
|
||||
break;
|
||||
case 149: //10010101 //game.dat149.rel
|
||||
ParseData149(d, dbr);
|
||||
break;
|
||||
case 150: //10010110 //game.dat150.rel
|
||||
ParseData150(d, dbr);
|
||||
break;
|
||||
case 151: //10010111 //game.dat151.rel
|
||||
ParseData151(d, dbr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private void ParseData4(RelData d, BinaryReader br)
|
||||
{
|
||||
//speech.dat4.rel, audioconfig.dat4.rel
|
||||
|
||||
if (d.Length == 1)
|
||||
{
|
||||
byte b = br.ReadByte();
|
||||
switch (b)
|
||||
{
|
||||
case 0:
|
||||
case 25:
|
||||
case 28:
|
||||
case 34:
|
||||
case 89:
|
||||
case 94:
|
||||
case 178:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (d.Length == 2)
|
||||
{
|
||||
byte b = br.ReadByte();
|
||||
switch (b)
|
||||
{
|
||||
case 4:
|
||||
case 1:
|
||||
case 15:
|
||||
case 12:
|
||||
case 3:
|
||||
case 2:
|
||||
case 7:
|
||||
case 5:
|
||||
case 158:
|
||||
case 25:
|
||||
case 16:
|
||||
case 64:
|
||||
case 6:
|
||||
case 8:
|
||||
case 14:
|
||||
case 22:
|
||||
case 18:
|
||||
case 20:
|
||||
case 32:
|
||||
case 17:
|
||||
case 30:
|
||||
case 9:
|
||||
case 0:
|
||||
case 47:
|
||||
case 224:
|
||||
case 200:
|
||||
case 136:
|
||||
case 45:
|
||||
case 54:
|
||||
case 28:
|
||||
case 19:
|
||||
case 37:
|
||||
case 61:
|
||||
case 38:
|
||||
case 128:
|
||||
case 24:
|
||||
case 26:
|
||||
case 40:
|
||||
case 13:
|
||||
case 36:
|
||||
case 78:
|
||||
case 34:
|
||||
case 10:
|
||||
case 21:
|
||||
case 192:
|
||||
case 60:
|
||||
case 29:
|
||||
case 33:
|
||||
case 72:
|
||||
case 57:
|
||||
case 133:
|
||||
case 11:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (d.Length == 4)
|
||||
{
|
||||
uint h = br.ReadUInt32();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 4:
|
||||
case 1:
|
||||
case 0:
|
||||
case 6:
|
||||
case 3:
|
||||
case 2:
|
||||
case 5:
|
||||
case 7:
|
||||
case 15:
|
||||
case 10:
|
||||
case 8:
|
||||
case 9:
|
||||
break;
|
||||
|
||||
case 23:
|
||||
case 12:
|
||||
case 11:
|
||||
case 16:
|
||||
case 13:
|
||||
case 36:
|
||||
case 30:
|
||||
case 31:
|
||||
case 27:
|
||||
case 20:
|
||||
case 19:
|
||||
case 14:
|
||||
case 40:
|
||||
case 46:
|
||||
case 22:
|
||||
case 18:
|
||||
case 21:
|
||||
case 45:
|
||||
case 17:
|
||||
case 48:
|
||||
case 87:
|
||||
case 38:
|
||||
case 28:
|
||||
case 29:
|
||||
case 43:
|
||||
case 69:
|
||||
case 50:
|
||||
case 25:
|
||||
case 32:
|
||||
case 35:
|
||||
case 34:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData10(RelData d, BinaryReader br)
|
||||
{
|
||||
//amp.dat10.rel
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData15(RelData d, BinaryReader br)
|
||||
{
|
||||
//mix.dat15.rel
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData16(RelData d, BinaryReader br)
|
||||
{
|
||||
//curves.dat16.rel
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 12:
|
||||
case 13:
|
||||
case 15:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData22(RelData d, BinaryReader br)
|
||||
{
|
||||
//categories.dat22.rel
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData54(RelData d, BinaryReader br)
|
||||
{
|
||||
//sounds.dat54.rel
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
case 8:
|
||||
case 9:
|
||||
case 10:
|
||||
case 11:
|
||||
case 12:
|
||||
case 13:
|
||||
case 14:
|
||||
case 15:
|
||||
case 16:
|
||||
case 17:
|
||||
case 18:
|
||||
case 19:
|
||||
case 20:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 26:
|
||||
case 27:
|
||||
case 28:
|
||||
case 29:
|
||||
case 30:
|
||||
case 31:
|
||||
case 32:
|
||||
case 33:
|
||||
case 34:
|
||||
case 35:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData149(RelData d, BinaryReader br)
|
||||
{
|
||||
//game.dat149.rel
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 3:
|
||||
case 4:
|
||||
case 17:
|
||||
case 50:
|
||||
case 57:
|
||||
case 62:
|
||||
case 63:
|
||||
case 66:
|
||||
case 76:
|
||||
case 88:
|
||||
case 90:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData150(RelData d, BinaryReader br)
|
||||
{
|
||||
//game.dat150.rel
|
||||
|
||||
byte b00 = br.ReadByte();
|
||||
switch (b00)
|
||||
{
|
||||
case 3:
|
||||
case 4:
|
||||
case 6:
|
||||
case 8:
|
||||
case 17:
|
||||
case 32:
|
||||
case 37:
|
||||
case 38:
|
||||
case 39:
|
||||
case 47:
|
||||
case 50:
|
||||
case 52:
|
||||
case 57:
|
||||
case 62:
|
||||
case 63:
|
||||
case 64:
|
||||
case 65:
|
||||
case 66:
|
||||
case 76:
|
||||
case 88:
|
||||
case 90:
|
||||
case 117:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void ParseData151(RelData d, BinaryReader br)
|
||||
{
|
||||
//game.dat151.rel
|
||||
|
||||
byte b00 = br.ReadByte(); //???
|
||||
switch (b00)
|
||||
{
|
||||
case 1://new
|
||||
case 2://new
|
||||
case 3:
|
||||
case 4:
|
||||
case 5://new
|
||||
case 6:
|
||||
case 7://new
|
||||
case 8://
|
||||
case 9://new
|
||||
case 11://new
|
||||
case 12://new
|
||||
case 13://new
|
||||
case 14://new
|
||||
case 15://new
|
||||
case 16://new
|
||||
case 17:
|
||||
case 18://new
|
||||
case 22://new
|
||||
case 23://new
|
||||
case 24://new
|
||||
case 25://new
|
||||
case 26://new
|
||||
case 27://new
|
||||
case 28://new
|
||||
case 29://new
|
||||
case 30://new
|
||||
case 31://new
|
||||
case 32://
|
||||
case 33://new
|
||||
case 35://new
|
||||
case 36://new
|
||||
case 37://
|
||||
case 38://
|
||||
case 39://
|
||||
case 40://new
|
||||
case 41://new
|
||||
case 42://new
|
||||
case 44://new
|
||||
case 45://new
|
||||
case 46://new
|
||||
case 47://
|
||||
case 48://new
|
||||
case 49://new
|
||||
case 50:
|
||||
case 51://new
|
||||
case 52://
|
||||
case 53://new
|
||||
case 54://new
|
||||
case 55://new
|
||||
case 56://new
|
||||
case 57:
|
||||
case 59://new
|
||||
case 62:
|
||||
case 63:
|
||||
case 64:
|
||||
case 65://
|
||||
case 66:
|
||||
case 67://new
|
||||
case 68://new
|
||||
case 69://new
|
||||
case 70://new
|
||||
case 71://new
|
||||
case 72://new
|
||||
case 73://new
|
||||
case 74://new
|
||||
case 75://new
|
||||
case 76:
|
||||
case 77://new
|
||||
case 78://new
|
||||
case 79://new
|
||||
case 80://new
|
||||
case 81://new
|
||||
case 82://new
|
||||
case 83://new
|
||||
case 84://new
|
||||
case 85://new
|
||||
case 86://new
|
||||
case 87://new
|
||||
case 88:
|
||||
case 90:
|
||||
case 91://new
|
||||
case 92://new
|
||||
case 93://new
|
||||
case 94://new
|
||||
case 95://new
|
||||
case 96://new
|
||||
case 98://new
|
||||
case 99://new
|
||||
case 100://new
|
||||
case 101://new
|
||||
case 102://new
|
||||
case 103://new
|
||||
case 104://new
|
||||
case 105://new
|
||||
case 106://new
|
||||
case 107://new
|
||||
case 108://new
|
||||
case 109://new
|
||||
case 110://new
|
||||
case 111://new
|
||||
case 112://new
|
||||
case 113://new
|
||||
case 114://new
|
||||
case 115://new
|
||||
case 116://new
|
||||
case 117:
|
||||
case 118://new
|
||||
case 119://new
|
||||
case 120://new
|
||||
case 121://new
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public struct RelIndexHash
|
||||
{
|
||||
public MetaHash Name { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
public uint Length { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name.ToString() + ", " + Offset.ToString() + ", " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public struct RelIndexString
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
public uint Length { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Name + ", " + Offset.ToString() + ", " + Length.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class RelData
|
||||
{
|
||||
public MetaHash NameHash { get; set; }
|
||||
public string Name { get; set; }
|
||||
public uint Offset { get; set; }
|
||||
public uint Length { get; set; }
|
||||
public byte[] Data { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string ol= ", " + Offset.ToString() + ", " + Length.ToString();
|
||||
if (!string.IsNullOrEmpty(Name)) return Name + ol;
|
||||
return NameHash.ToString() + ol;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class YcdFile : GameFile, PackedFile
|
||||
{
|
||||
public ClipDictionary ClipDictionary { get; set; }
|
||||
|
||||
public Dictionary<MetaHash, ClipMapEntry> ClipMap { 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;
|
||||
//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>();
|
||||
if ((ClipDictionary != null) && (ClipDictionary.Clips != null) && (ClipDictionary.Clips.data_items != null))
|
||||
{
|
||||
foreach (var cme in ClipDictionary.Clips.data_items)
|
||||
{
|
||||
if (cme != null)
|
||||
{
|
||||
ClipMap[cme.Hash] = cme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
DrawableDictionary 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 VertexTypePC[] 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<VertexTypePC> rverts = new List<VertexTypePC>();
|
||||
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)
|
||||
{ }
|
||||
|
||||
|
||||
VertexTypePC p0 = new VertexTypePC();
|
||||
VertexTypePC p1 = new VertexTypePC();
|
||||
VertexTypePC p2 = new VertexTypePC();
|
||||
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 VertexTypePC[] GetPathVertices()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
public VertexTypePC[] 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,265 @@
|
||||
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 CMapTypes CMapTypes { get; set; }
|
||||
//public CBaseArchetypeDef[] CBaseArchetypeDefs { get; set; }
|
||||
//public CTimeArchetypeDef[] CTimeArchetypeDefs { get; set; }
|
||||
//public CMloArchetypeDef[] CMloArchetypeDefs { get; set; }
|
||||
|
||||
public CExtensionDefAudioEmitter[] AudioEmitters { get; set; }
|
||||
|
||||
//public CEntityDef[] CEntityDefs { get; set; }
|
||||
|
||||
public CCompositeEntityType[] CompositeEntityTypes { get; set; }
|
||||
|
||||
public uint NameHash { get; set; }
|
||||
public string[] Strings { get; set; }
|
||||
|
||||
|
||||
|
||||
public Archetype[] AllArchetypes { get; set; }
|
||||
|
||||
public MetaWrapper[] Extensions { 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];
|
||||
int blocki = ptr.BlockID - 1;
|
||||
int offset = ptr.ItemOffset * 16;//block data size...
|
||||
if (blocki >= Meta.DataBlocks.Count)
|
||||
{ continue; }
|
||||
var block = Meta.DataBlocks[blocki];
|
||||
if ((offset < 0) || (block.Data == null) || (offset >= block.Data.Length))
|
||||
{ continue; }
|
||||
|
||||
var ba = new Archetype();
|
||||
switch (block.StructureNameHash)
|
||||
{
|
||||
case MetaName.CBaseArchetypeDef:
|
||||
var basearch = PsoTypes.ConvertDataRaw<CBaseArchetypeDef>(block.Data, offset);
|
||||
ba.Init(this, basearch);
|
||||
ba.Extensions = MetaTypes.GetExtensions(Meta, basearch.extensions);
|
||||
break;
|
||||
case MetaName.CTimeArchetypeDef:
|
||||
var timearch = PsoTypes.ConvertDataRaw<CTimeArchetypeDef>(block.Data, offset);
|
||||
ba.Init(this, timearch);
|
||||
ba.Extensions = MetaTypes.GetExtensions(Meta, timearch.CBaseArchetypeDef.extensions);
|
||||
break;
|
||||
case MetaName.CMloArchetypeDef:
|
||||
var mloarch = PsoTypes.ConvertDataRaw<CMloArchetypeDef>(block.Data, offset);
|
||||
ba.Init(this, mloarch);
|
||||
ba.Extensions = MetaTypes.GetExtensions(Meta, mloarch.CBaseArchetypeDef.extensions);
|
||||
|
||||
MloArchetypeData mlod = new MloArchetypeData();
|
||||
mlod.entities = MetaTypes.ConvertDataArray<CEntityDef>(Meta, MetaName.CEntityDef, mloarch.entities);
|
||||
mlod.rooms = MetaTypes.ConvertDataArray<CMloRoomDef>(Meta, MetaName.CMloRoomDef, mloarch.rooms);
|
||||
mlod.portals = MetaTypes.ConvertDataArray<CMloPortalDef>(Meta, MetaName.CMloPortalDef, mloarch.portals);
|
||||
mlod.entitySets = MetaTypes.ConvertDataArray<CMloEntitySet>(Meta, MetaName.CMloEntitySet, mloarch.entitySets);
|
||||
mlod.timeCycleModifiers = MetaTypes.ConvertDataArray<CMloTimeCycleModifier>(Meta, MetaName.CMloTimeCycleModifier, mloarch.timeCycleModifiers);
|
||||
ba.MloData = mlod;
|
||||
|
||||
//if (mlod.entities != null)
|
||||
//{
|
||||
// for (int e = 0; e < mlod.entities.Length; e++)
|
||||
// {
|
||||
// EnsureEntityExtensions(Meta, ref mlod.entities[e]);
|
||||
// }
|
||||
//}
|
||||
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
allarchs.Add(ba);
|
||||
}
|
||||
}
|
||||
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.SectionUNKNOWN2:
|
||||
//case MetaName.SectionUNKNOWN3:
|
||||
//case MetaName.SectionUNKNOWN8:
|
||||
case MetaName.POINTER:
|
||||
case MetaName.UINT:
|
||||
case MetaName.VECTOR4:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//MetaTypes.ParseMetaData(Meta);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//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);
|
||||
|
||||
//Meta = rd.ReadBlock<Meta>();
|
||||
|
||||
//MetaTypes.EnsureMetaTypes(Meta);
|
||||
|
||||
//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,74 @@
|
||||
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.FileSize : 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,
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,641 @@
|
||||
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 Meta : ResourceFileBase
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 112; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public int Unknown_10h { get; set; } = 0x50524430;
|
||||
public short Unknown_14h { get; set; } = 0x0079;
|
||||
public byte HasUselessData { get; set; }
|
||||
public byte Unknown_17h { get; set; } = 0x00;
|
||||
public int Unknown_18h { get; set; } = 0x00000000;
|
||||
public int RootBlockIndex { get; set; }
|
||||
public long StructureInfosPointer { get; set; }
|
||||
public long EnumInfosPointer { get; set; }
|
||||
public long DataBlocksPointer { get; set; }
|
||||
public long NamePointer { get; set; }
|
||||
public long UselessPointer { get; set; }
|
||||
public short StructureInfosCount { get; set; }
|
||||
public short EnumInfosCount { get; set; }
|
||||
public short DataBlocksCount { get; set; }
|
||||
public short Unknown_4Eh { get; set; } = 0x0000;
|
||||
public uint Unknown_50h { get; set; } = 0x00000000;
|
||||
public uint Unknown_54h { get; set; } = 0x00000000;
|
||||
public uint Unknown_58h { get; set; } = 0x00000000;
|
||||
public uint Unknown_5Ch { get; set; } = 0x00000000;
|
||||
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;
|
||||
|
||||
// reference data
|
||||
public ResourceSimpleArray<MetaStructureInfo> StructureInfos { get; set; }
|
||||
public ResourceSimpleArray<MetaEnumInfo> EnumInfos { get; set; }
|
||||
public ResourceSimpleArray<MetaDataBlock> DataBlocks { get; set; }
|
||||
public string Name { get; private set; }
|
||||
//public string[] Strings { get; set; }
|
||||
|
||||
|
||||
/// <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.ReadInt32();
|
||||
this.Unknown_14h = reader.ReadInt16();
|
||||
this.HasUselessData = reader.ReadByte();
|
||||
this.Unknown_17h = reader.ReadByte();
|
||||
this.Unknown_18h = reader.ReadInt32();
|
||||
this.RootBlockIndex = reader.ReadInt32();
|
||||
this.StructureInfosPointer = reader.ReadInt64();
|
||||
this.EnumInfosPointer = reader.ReadInt64();
|
||||
this.DataBlocksPointer = reader.ReadInt64();
|
||||
this.NamePointer = reader.ReadInt64();
|
||||
this.UselessPointer = reader.ReadInt64();
|
||||
this.StructureInfosCount = reader.ReadInt16();
|
||||
this.EnumInfosCount = reader.ReadInt16();
|
||||
this.DataBlocksCount = reader.ReadInt16();
|
||||
this.Unknown_4Eh = reader.ReadInt16();
|
||||
this.Unknown_50h = reader.ReadUInt32();
|
||||
this.Unknown_54h = reader.ReadUInt32();
|
||||
this.Unknown_58h = reader.ReadUInt32();
|
||||
this.Unknown_5Ch = reader.ReadUInt32();
|
||||
this.Unknown_60h = reader.ReadUInt32();
|
||||
this.Unknown_64h = reader.ReadUInt32();
|
||||
this.Unknown_68h = reader.ReadUInt32();
|
||||
this.Unknown_6Ch = reader.ReadUInt32();
|
||||
|
||||
// read reference data
|
||||
this.StructureInfos = reader.ReadBlockAt<ResourceSimpleArray<MetaStructureInfo>>(
|
||||
(ulong)this.StructureInfosPointer, // offset
|
||||
this.StructureInfosCount
|
||||
);
|
||||
|
||||
this.EnumInfos = reader.ReadBlockAt<ResourceSimpleArray<MetaEnumInfo>>(
|
||||
(ulong)this.EnumInfosPointer, // offset
|
||||
this.EnumInfosCount
|
||||
);
|
||||
|
||||
this.DataBlocks = reader.ReadBlockAt<ResourceSimpleArray<MetaDataBlock>>(
|
||||
(ulong)this.DataBlocksPointer, // offset
|
||||
this.DataBlocksCount
|
||||
);
|
||||
|
||||
this.Name = reader.ReadStringAt(//BlockAt<string_r>(
|
||||
(ulong)this.NamePointer // offset
|
||||
);
|
||||
|
||||
//Strings = MetaTypes.GetStrings(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
base.Write(writer, parameters);
|
||||
|
||||
// update structure data
|
||||
this.StructureInfosPointer = this.StructureInfos?.FilePosition ?? 0;
|
||||
this.EnumInfosPointer = this.EnumInfos?.FilePosition ?? 0;
|
||||
this.DataBlocksPointer = this.DataBlocks?.FilePosition ?? 0;
|
||||
//this.NamePointer = this.Name?.Position ?? 0; //TODO: fix
|
||||
this.UselessPointer = 0;
|
||||
this.StructureInfosCount = (short)(this.StructureInfos?.Count ?? 0);
|
||||
this.EnumInfosCount = (short)(this.EnumInfos?.Count ?? 0);
|
||||
this.DataBlocksCount = (short)(this.DataBlocks?.Count ?? 0);
|
||||
|
||||
// write structure data
|
||||
writer.Write(this.Unknown_10h);
|
||||
writer.Write(this.Unknown_14h);
|
||||
writer.Write(this.HasUselessData);
|
||||
writer.Write(this.Unknown_17h);
|
||||
writer.Write(this.Unknown_18h);
|
||||
writer.Write(this.RootBlockIndex);
|
||||
writer.Write(this.StructureInfosPointer);
|
||||
writer.Write(this.EnumInfosPointer);
|
||||
writer.Write(this.DataBlocksPointer);
|
||||
writer.Write(this.NamePointer);
|
||||
writer.Write(this.UselessPointer);
|
||||
writer.Write(this.StructureInfosCount);
|
||||
writer.Write(this.EnumInfosCount);
|
||||
writer.Write(this.DataBlocksCount);
|
||||
writer.Write(this.Unknown_4Eh);
|
||||
writer.Write(this.Unknown_50h);
|
||||
writer.Write(this.Unknown_54h);
|
||||
writer.Write(this.Unknown_58h);
|
||||
writer.Write(this.Unknown_5Ch);
|
||||
writer.Write(this.Unknown_60h);
|
||||
writer.Write(this.Unknown_64h);
|
||||
writer.Write(this.Unknown_68h);
|
||||
writer.Write(this.Unknown_6Ch);
|
||||
}
|
||||
|
||||
/// <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());
|
||||
if ((StructureInfos != null) && (StructureInfos.Count > 0)) list.Add(StructureInfos);
|
||||
if ((EnumInfos != null) && (EnumInfos.Count > 0)) list.Add(EnumInfos);
|
||||
if ((DataBlocks != null) && (DataBlocks.Count > 0)) list.Add(DataBlocks);
|
||||
//if (Name != null) list.Add(Name); //TODO: fix
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public MetaDataBlock FindBlock(MetaName name)
|
||||
{
|
||||
if (DataBlocks == null) return null;
|
||||
foreach (var block in DataBlocks)
|
||||
{
|
||||
if (block.StructureNameHash == name)
|
||||
{
|
||||
return block;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public MetaDataBlock GetRootBlock()
|
||||
{
|
||||
MetaDataBlock block = null;
|
||||
var rootind = RootBlockIndex - 1;
|
||||
if ((rootind >= 0) && (rootind < DataBlocks.Count) && (DataBlocks.Data != null))
|
||||
{
|
||||
block = DataBlocks[rootind];
|
||||
}
|
||||
return block;
|
||||
}
|
||||
public MetaDataBlock GetBlock(int id)
|
||||
{
|
||||
MetaDataBlock block = null;
|
||||
var ind = id - 1;
|
||||
if ((ind >= 0) && (ind < DataBlocks.Count) && (DataBlocks.Data != null))
|
||||
{
|
||||
block = DataBlocks[ind];
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class MetaStructureInfo : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 32; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public MetaName StructureNameHash { get; set; }
|
||||
public uint StructureKey { get; set; }
|
||||
public uint Unknown_8h { get; set; }
|
||||
public uint Unknown_Ch { get; set; } = 0x00000000;
|
||||
public long EntriesPointer { get; private set; }
|
||||
public int StructureSize { get; set; }
|
||||
public short Unknown_1Ch { get; set; } = 0x0000;
|
||||
public short EntriesCount { get; private set; }
|
||||
|
||||
// reference data
|
||||
public MetaStructureEntryInfo_s[] Entries { get; private set; }
|
||||
|
||||
private ResourceSystemStructBlock<MetaStructureEntryInfo_s> EntriesBlock = null;
|
||||
|
||||
|
||||
public MetaStructureInfo()
|
||||
{ }
|
||||
public MetaStructureInfo(MetaName nameHash, uint key, uint unknown, int length, params MetaStructureEntryInfo_s[] entries)
|
||||
{
|
||||
StructureNameHash = nameHash;
|
||||
StructureKey = key;
|
||||
Unknown_8h = unknown;
|
||||
StructureSize = length;
|
||||
Entries = entries;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.StructureNameHash = (MetaName)reader.ReadInt32();
|
||||
this.StructureKey = reader.ReadUInt32();
|
||||
this.Unknown_8h = reader.ReadUInt32();
|
||||
this.Unknown_Ch = reader.ReadUInt32();
|
||||
this.EntriesPointer = reader.ReadInt64();
|
||||
this.StructureSize = reader.ReadInt32();
|
||||
this.Unknown_1Ch = reader.ReadInt16();
|
||||
this.EntriesCount = reader.ReadInt16();
|
||||
|
||||
// read reference data
|
||||
this.Entries = reader.ReadStructsAt<MetaStructureEntryInfo_s>((ulong)this.EntriesPointer, (uint)this.EntriesCount);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// update structure data
|
||||
//this.EntriesPointer = this.Entries?.Position ?? 0; //TODO: fix
|
||||
//this.EntriesCount = (short)(this.Entries?.Count ?? 0);
|
||||
this.EntriesPointer = this.EntriesBlock?.FilePosition ?? 0; //TODO: fix
|
||||
this.EntriesCount = (short)(this.EntriesBlock?.ItemCount ?? 0);
|
||||
|
||||
// write structure data
|
||||
writer.Write((int)this.StructureNameHash);
|
||||
writer.Write(this.StructureKey);
|
||||
writer.Write(this.Unknown_8h);
|
||||
writer.Write(this.Unknown_Ch);
|
||||
writer.Write(this.EntriesPointer);
|
||||
writer.Write(this.StructureSize);
|
||||
writer.Write(this.Unknown_1Ch);
|
||||
writer.Write(this.EntriesCount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks which are referenced by this block.
|
||||
/// </summary>
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
if (EntriesBlock == null)
|
||||
{
|
||||
EntriesBlock = new ResourceSystemStructBlock<MetaStructureEntryInfo_s>(Entries);
|
||||
}
|
||||
if (EntriesBlock != null) list.Add(EntriesBlock);
|
||||
|
||||
//if (Entries != null) list.Add(Entries); //TODO: fix
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return StructureNameHash.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public enum MetaStructureEntryDataType : byte
|
||||
{
|
||||
Boolean = 0x01,
|
||||
SignedByte = 0x10,
|
||||
UnsignedByte = 0x11, // OCCURS IN ARRAY
|
||||
SignedShort = 0x12,
|
||||
UnsignedShort = 0x13, // OCCURS IN ARRAY
|
||||
SignedInt = 0x14,
|
||||
UnsignedInt = 0x15, // OCCURS IN ARRAY
|
||||
Float = 0x21, // OCCURS IN ARRAY
|
||||
Float_XYZ = 0x33, // OCCURS IN ARRAY
|
||||
Float_XYZW = 0x34,
|
||||
ByteEnum = 0x60, // has enum name hash in info
|
||||
IntEnum = 0x62, // has enum name hash in info
|
||||
ShortFlags = 0x64, // has enum name hash in info
|
||||
IntFlags1 = 0x63, // has enum name hash in info
|
||||
IntFlags2 = 0x65, // has enum name hash in info (optional?)
|
||||
Hash = 0x4A, // OCCURS IN ARRAY
|
||||
Array = 0x52,
|
||||
ArrayOfChars = 0x40, // has length in info
|
||||
ArrayOfBytes = 0x50, // has length in info
|
||||
DataBlockPointer = 0x59,
|
||||
CharPointer = 0x44,
|
||||
StructurePointer = 0x07, // OCCURS IN ARRAY
|
||||
Structure = 0x05 // has structure name hash in info, OCCURS IN ARRAY
|
||||
}
|
||||
public static class MetaStructureEntryDataTypes
|
||||
{
|
||||
public static string GetCSharpTypeName(MetaStructureEntryDataType t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case MetaStructureEntryDataType.Boolean: return "bool";
|
||||
case MetaStructureEntryDataType.SignedByte: return "sbyte";
|
||||
case MetaStructureEntryDataType.UnsignedByte: return "byte";
|
||||
case MetaStructureEntryDataType.SignedShort: return "short";
|
||||
case MetaStructureEntryDataType.UnsignedShort: return "ushort";
|
||||
case MetaStructureEntryDataType.SignedInt: return "int";
|
||||
case MetaStructureEntryDataType.UnsignedInt: return "uint";
|
||||
case MetaStructureEntryDataType.Float: return "float";
|
||||
case MetaStructureEntryDataType.Float_XYZ: return "Vector3";
|
||||
case MetaStructureEntryDataType.Float_XYZW: return "Vector4";
|
||||
|
||||
case MetaStructureEntryDataType.Hash: return "uint"; //uint hashes...
|
||||
case MetaStructureEntryDataType.ByteEnum: return "byte"; //convert to enum later..
|
||||
case MetaStructureEntryDataType.IntEnum: return "int";
|
||||
case MetaStructureEntryDataType.ShortFlags: return "short";
|
||||
case MetaStructureEntryDataType.IntFlags1: return "int";
|
||||
case MetaStructureEntryDataType.IntFlags2: return "int";
|
||||
|
||||
case MetaStructureEntryDataType.Array:
|
||||
case MetaStructureEntryDataType.ArrayOfChars:
|
||||
case MetaStructureEntryDataType.ArrayOfBytes:
|
||||
case MetaStructureEntryDataType.DataBlockPointer:
|
||||
case MetaStructureEntryDataType.CharPointer:
|
||||
case MetaStructureEntryDataType.StructurePointer:
|
||||
case MetaStructureEntryDataType.Structure:
|
||||
default:
|
||||
return t.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct MetaStructureEntryInfo_s
|
||||
{
|
||||
// structure data
|
||||
public MetaName EntryNameHash { get; set; }
|
||||
public int DataOffset { get; set; }
|
||||
public MetaStructureEntryDataType DataType { get; set; }
|
||||
public byte Unknown_9h { get; set; }
|
||||
public short ReferenceTypeIndex { get; set; }
|
||||
public MetaName ReferenceKey { get; set; }
|
||||
|
||||
public MetaStructureEntryInfo_s(MetaName nameHash, int dataOffset, MetaStructureEntryDataType dataType, byte unk9h, short referenceTypeIndex, MetaName referenceKey)
|
||||
{
|
||||
EntryNameHash = nameHash;
|
||||
DataOffset = dataOffset;
|
||||
DataType = dataType;
|
||||
Unknown_9h = unk9h;
|
||||
ReferenceTypeIndex = referenceTypeIndex;
|
||||
ReferenceKey = referenceKey;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return DataOffset.ToString() + ": " + DataType.ToString() + ": " + ReferenceKey.ToString() + ": " + EntryNameHash.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class MetaEnumInfo : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 24; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public MetaName EnumNameHash { get; set; }
|
||||
public uint EnumKey { get; set; }
|
||||
public long EntriesPointer { get; private set; }
|
||||
public int EntriesCount { get; private set; }
|
||||
public int Unknown_14h { get; set; } = 0x00000000;
|
||||
|
||||
// reference data
|
||||
//public ResourceSimpleArray<MetaEnumEntryInfo> Entries;
|
||||
public MetaEnumEntryInfo_s[] Entries { get; private set; }
|
||||
|
||||
private ResourceSystemStructBlock<MetaEnumEntryInfo_s> EntriesBlock = null;
|
||||
|
||||
|
||||
public MetaEnumInfo()
|
||||
{ }
|
||||
public MetaEnumInfo(MetaName nameHash, uint key, params MetaEnumEntryInfo_s[] entries)
|
||||
{
|
||||
EnumNameHash = nameHash;
|
||||
EnumKey = key;
|
||||
Entries = entries;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.EnumNameHash = (MetaName)reader.ReadInt32();
|
||||
this.EnumKey = reader.ReadUInt32();
|
||||
this.EntriesPointer = reader.ReadInt64();
|
||||
this.EntriesCount = reader.ReadInt32();
|
||||
this.Unknown_14h = reader.ReadInt32();
|
||||
|
||||
// read reference data
|
||||
//this.Entries = reader.ReadBlockAt<ResourceSimpleArray<MetaEnumEntryInfo>>(
|
||||
// (ulong)this.EntriesPointer, // offset
|
||||
// this.EntriesCount
|
||||
//);
|
||||
this.Entries = reader.ReadStructsAt<MetaEnumEntryInfo_s>((ulong)this.EntriesPointer, (uint)this.EntriesCount);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// update structure data
|
||||
//this.EntriesPointer = this.Entries?.Position ?? 0; //TODO: fix
|
||||
//this.EntriesCount = this.Entries?.Count ?? 0;
|
||||
this.EntriesPointer = this.EntriesBlock?.FilePosition ?? 0; //TODO: fix
|
||||
this.EntriesCount = (short)(this.EntriesBlock?.ItemCount ?? 0);
|
||||
|
||||
// write structure data
|
||||
writer.Write((int)this.EnumNameHash);
|
||||
writer.Write(this.EnumKey);
|
||||
writer.Write(this.EntriesPointer);
|
||||
writer.Write(this.EntriesCount);
|
||||
writer.Write(this.Unknown_14h);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks which are referenced by this block.
|
||||
/// </summary>
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
//if (Entries != null) list.Add(Entries);
|
||||
if (EntriesBlock == null)
|
||||
{
|
||||
EntriesBlock = new ResourceSystemStructBlock<MetaEnumEntryInfo_s>(Entries);
|
||||
}
|
||||
if (EntriesBlock != null) list.Add(EntriesBlock);
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return EnumNameHash.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct MetaEnumEntryInfo_s
|
||||
{
|
||||
// structure data
|
||||
public MetaName EntryNameHash { get; set; }
|
||||
public int EntryValue { get; set; }
|
||||
|
||||
public MetaEnumEntryInfo_s(MetaName nameHash, int value)
|
||||
{
|
||||
EntryNameHash = nameHash;
|
||||
EntryValue = value;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return EntryNameHash.ToString() + ": " + EntryValue.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public class MetaDataBlock : ResourceSystemBlock
|
||||
{
|
||||
public override long BlockLength
|
||||
{
|
||||
get { return 16; }
|
||||
}
|
||||
|
||||
// structure data
|
||||
public MetaName StructureNameHash { get; set; }
|
||||
public int DataLength { get; set; }
|
||||
public long DataPointer { get; private set; }
|
||||
|
||||
// reference data
|
||||
public byte[] Data { get; set; }
|
||||
private ResourceSystemDataBlock DataBlock = null;
|
||||
|
||||
/// <summary>
|
||||
/// Reads the data-block from a stream.
|
||||
/// </summary>
|
||||
public override void Read(ResourceDataReader reader, params object[] parameters)
|
||||
{
|
||||
// read structure data
|
||||
this.StructureNameHash = (MetaName)reader.ReadInt32();
|
||||
this.DataLength = reader.ReadInt32();
|
||||
this.DataPointer = reader.ReadInt64();
|
||||
|
||||
|
||||
this.Data = reader.ReadBytesAt((ulong)this.DataPointer, (uint)DataLength);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the data-block to a stream.
|
||||
/// </summary>
|
||||
public override void Write(ResourceDataWriter writer, params object[] parameters)
|
||||
{
|
||||
// update structure data
|
||||
this.DataLength = this.Data?.Length ?? 0;
|
||||
//this.DataPointer = (this.Data!=null)? DataPos : 0; //TODO:fix...
|
||||
this.DataPointer = this.DataBlock?.FilePosition ?? 0;
|
||||
|
||||
// write structure data
|
||||
writer.Write((int)this.StructureNameHash);
|
||||
writer.Write(this.DataLength);
|
||||
writer.Write(this.DataPointer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of data blocks which are referenced by this block.
|
||||
/// </summary>
|
||||
public override IResourceBlock[] GetReferences()
|
||||
{
|
||||
var list = new List<IResourceBlock>();
|
||||
if (DataBlock == null)
|
||||
{
|
||||
DataBlock = new ResourceSystemDataBlock(Data);
|
||||
}
|
||||
if (DataBlock != null) list.Add(DataBlock);
|
||||
//if (Data != null) list.Add(Data);
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return StructureNameHash.ToString() + ": " + DataPointer.ToString() + " (" + DataLength.ToString() + ")";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct MetaHash
|
||||
{
|
||||
public uint Hash { get; set; }
|
||||
|
||||
public string Hex
|
||||
{
|
||||
get
|
||||
{
|
||||
return Hash.ToString("X").PadLeft(8, '0');
|
||||
}
|
||||
}
|
||||
|
||||
public MetaHash(uint h) { Hash = h; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var str = JenkIndex.TryGetString(Hash);
|
||||
if (!string.IsNullOrEmpty(str)) return str;
|
||||
return GlobalText.GetString(Hash);
|
||||
}
|
||||
|
||||
|
||||
public static implicit operator uint(MetaHash h)
|
||||
{
|
||||
return h.Hash; //implicit conversion
|
||||
}
|
||||
|
||||
public static implicit operator MetaHash(uint v)
|
||||
{
|
||||
return new MetaHash(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))] public struct TextHash
|
||||
{
|
||||
public uint Hash { get; set; }
|
||||
|
||||
public string Hex
|
||||
{
|
||||
get
|
||||
{
|
||||
return Hash.ToString("X");
|
||||
}
|
||||
}
|
||||
|
||||
public TextHash(uint h) { Hash = h; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return GlobalText.GetString(Hash);
|
||||
}
|
||||
|
||||
|
||||
public static implicit operator uint(TextHash h)
|
||||
{
|
||||
return h.Hash; //implicit conversion
|
||||
}
|
||||
|
||||
public static implicit operator TextHash(uint v)
|
||||
{
|
||||
return new TextHash(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
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 = 0x2000; //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
|
||||
{
|
||||
MetaBuilderBlock block = EnsureBlock(type);
|
||||
byte[] data = MetaTypes.ConvertToBytes(item);
|
||||
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.Block = block.Index + 1;
|
||||
r.Offset = (idx * data.Length) / 16;
|
||||
r.Length = data.Length;
|
||||
return r;
|
||||
}
|
||||
public MetaBuilderPointer AddItemArray<T>(MetaName type, T[] items) where T : struct
|
||||
{
|
||||
MetaBuilderBlock block = EnsureBlock(type);
|
||||
byte[] data = MetaTypes.ConvertArrayToBytes(items);
|
||||
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 / 16;
|
||||
int idx = block.AddItem(newdata);
|
||||
MetaBuilderPointer r = new MetaBuilderPointer();
|
||||
r.Block = block.Index + 1;
|
||||
r.Offset = offs; //(idx * data.Length) / 16;
|
||||
r.Length = items.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 / 16;
|
||||
int idx = block.AddItem(newdata);
|
||||
MetaBuilderPointer r = new MetaBuilderPointer();
|
||||
r.Block = block.Index + 1;
|
||||
r.Offset = offs;// (idx * data.Length) / 16;//not sure if this is correct! should also use sub-offset!
|
||||
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((ushort)ptr.Block, (ushort)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_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_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 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.Block = meptr.BlockID;
|
||||
mbp.Offset = meptr.ItemOffset;
|
||||
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 Block { get; set; } //0-based index
|
||||
public int Offset { get; set; } //(byteoffset/16)
|
||||
public int Length { get; set; } //for temp use...
|
||||
public uint Pointer
|
||||
{
|
||||
get
|
||||
{
|
||||
uint bidx = (((uint)Block) & 0xFFF);
|
||||
uint offs = (((uint)Offset) & 0xFFFF) << 16;
|
||||
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,910 @@
|
||||
/*
|
||||
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
|
||||
{
|
||||
//BYTE_00h = 0x00,
|
||||
//LONG_01h = 0x01,
|
||||
//BYTE_02h = 0x02,
|
||||
//SHORT_03h = 0x03,
|
||||
//SHORT_04h = 0x04,
|
||||
//INT_05h = 0x05,
|
||||
//INT_06h = 0x06,
|
||||
//Float = 0x07,
|
||||
//LONG_08h = 0x08,
|
||||
//TYPE_09h = 0x09,
|
||||
//TYPE_0Ah = 0x0a,
|
||||
//INT_0Bh = 0x0b, //Hash? Name
|
||||
//Structure = 0x0c,
|
||||
//Array = 0x0d,
|
||||
//BYTE_ENUM_VALUE = 0x0e,
|
||||
//SHORT_0Fh = 0x0f, //short flags
|
||||
//TYPE_10h = 0x10,
|
||||
//TYPE_14h = 0x14,
|
||||
//Vector4 = 0x15,
|
||||
//SHORT_1Eh = 0x1e,
|
||||
//LONG_20h = 0x20
|
||||
|
||||
Boolean = 0x00,
|
||||
Byte1 = 0x01, //signed byte..
|
||||
Byte2 = 0x02,
|
||||
SHORT_03h = 0x03, //signed short?
|
||||
SHORT_04h = 0x04,
|
||||
INT_05h = 0x05, //signed int?
|
||||
Integer = 0x06, //...unsigned?
|
||||
Float = 0x07,
|
||||
Float2 = 0x08,
|
||||
Float3 = 0x09,
|
||||
Float4 = 0x0a,
|
||||
String = 0x0b,
|
||||
Structure = 0x0c,
|
||||
Array = 0x0d,
|
||||
Enum = 0x0e,
|
||||
Flags = 0x0f,
|
||||
Map = 0x10,
|
||||
Float3a = 0x14,
|
||||
Float3b = 0x15,
|
||||
SHORT_1Eh = 0x1e,
|
||||
LONG_20h = 0x20
|
||||
|
||||
}
|
||||
public static class PsoDataTypes
|
||||
{
|
||||
public static string GetCSharpTypeName(PsoDataType t)
|
||||
{
|
||||
//MetaStructureEntryDataType mdt = (MetaStructureEntryDataType)t;
|
||||
switch (t)
|
||||
{
|
||||
case PsoDataType.Boolean: return "bool";
|
||||
case PsoDataType.Byte1: return "sbyte"; //was LONG_01h.. why?
|
||||
case PsoDataType.Byte2: return "byte";
|
||||
case PsoDataType.SHORT_03h: return "short";
|
||||
case PsoDataType.SHORT_04h: return "short";
|
||||
case PsoDataType.INT_05h: return "int";
|
||||
case PsoDataType.Integer: 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.SHORT_1Eh: return "short";
|
||||
case PsoDataType.LONG_20h: return "long";
|
||||
case PsoDataType.Float3:
|
||||
case PsoDataType.Float4:
|
||||
case PsoDataType.Map:
|
||||
case PsoDataType.Float3a:
|
||||
case PsoDataType.Float3b:
|
||||
case PsoDataType.Structure:
|
||||
case PsoDataType.Array:
|
||||
default:
|
||||
return t.ToString();
|
||||
|
||||
//case MetaStructureEntryDataType.Boolean: return "bool";
|
||||
//case MetaStructureEntryDataType.SignedByte: return "sbyte";
|
||||
//case MetaStructureEntryDataType.UnsignedByte: return "byte";
|
||||
//case MetaStructureEntryDataType.SignedShort: return "short";
|
||||
//case MetaStructureEntryDataType.UnsignedShort: return "ushort";
|
||||
//case MetaStructureEntryDataType.SignedInt: return "int";
|
||||
//case MetaStructureEntryDataType.UnsignedInt: return "uint";
|
||||
//case MetaStructureEntryDataType.Float: return "float";
|
||||
//case MetaStructureEntryDataType.Float_XYZ: return "Vector3";
|
||||
//case MetaStructureEntryDataType.Float_XYZW: return "Vector4";
|
||||
|
||||
//case MetaStructureEntryDataType.Hash: return "uint"; //uint hashes...
|
||||
//case MetaStructureEntryDataType.ByteEnum: return "byte"; //convert to enum later..
|
||||
//case MetaStructureEntryDataType.IntEnum: return "int";
|
||||
//case MetaStructureEntryDataType.ShortFlags: return "short";
|
||||
//case MetaStructureEntryDataType.IntFlags1: return "int";
|
||||
//case MetaStructureEntryDataType.IntFlags2: return "int";
|
||||
|
||||
//case MetaStructureEntryDataType.Array:
|
||||
//case MetaStructureEntryDataType.ArrayOfChars:
|
||||
//case MetaStructureEntryDataType.ArrayOfBytes:
|
||||
//case MetaStructureEntryDataType.DataBlockPointer:
|
||||
//case MetaStructureEntryDataType.CharPointer:
|
||||
//case MetaStructureEntryDataType.StructurePointer:
|
||||
//case MetaStructureEntryDataType.Structure:
|
||||
//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 (ushort)((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,191 @@
|
||||
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 MetaHash Hash { get; set; }
|
||||
public YtypFile Ytyp { get; set; }
|
||||
public CBaseArchetypeDef BaseArchetype { get; set; }
|
||||
public CTimeArchetypeDef TimeArchetype { get; set; }
|
||||
public CMloArchetypeDef MloArchetype { 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 bool IsTimeArchetype { get; set; }
|
||||
public bool IsMloArchetype { get; set; }
|
||||
public float LodDist { get; set; }
|
||||
public MloArchetypeData MloData { get; set; }
|
||||
public MetaWrapper[] Extensions { get; set; }
|
||||
public TimedArchetypeTimes Times { get; set; }
|
||||
|
||||
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsTimeArchetype) return TimeArchetype.CBaseArchetypeDef.name.ToString();
|
||||
if (IsMloArchetype) return MloArchetype.CBaseArchetypeDef.name.ToString();
|
||||
return BaseArchetype.name.ToString();
|
||||
}
|
||||
}
|
||||
public string AssetName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsTimeArchetype) return TimeArchetype.CBaseArchetypeDef.assetName.ToString();
|
||||
if (IsMloArchetype) return MloArchetype.CBaseArchetypeDef.assetName.ToString();
|
||||
return BaseArchetype.assetName.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public void Init(YtypFile ytyp, CBaseArchetypeDef arch)
|
||||
{
|
||||
Hash = arch.assetName;
|
||||
if (Hash.Hash == 0) Hash = arch.name;
|
||||
Ytyp = ytyp;
|
||||
BaseArchetype = arch;
|
||||
DrawableDict = arch.drawableDictionary;
|
||||
TextureDict = arch.textureDictionary;
|
||||
ClipDict = arch.clipDictionary;
|
||||
BBMin = arch.bbMin;
|
||||
BBMax = arch.bbMax;
|
||||
BSCenter = arch.bsCentre;
|
||||
BSRadius = arch.bsRadius;
|
||||
IsTimeArchetype = false;
|
||||
IsMloArchetype = false;
|
||||
LodDist = arch.lodDist;
|
||||
}
|
||||
public void Init(YtypFile ytyp, CTimeArchetypeDef arch)
|
||||
{
|
||||
Hash = arch.CBaseArchetypeDef.assetName;
|
||||
if (Hash.Hash == 0) Hash = arch.CBaseArchetypeDef.name;
|
||||
Ytyp = ytyp;
|
||||
TimeArchetype = arch;
|
||||
DrawableDict = arch.CBaseArchetypeDef.drawableDictionary;
|
||||
TextureDict = arch.CBaseArchetypeDef.textureDictionary;
|
||||
ClipDict = arch.CBaseArchetypeDef.clipDictionary;
|
||||
BBMin = arch.CBaseArchetypeDef.bbMin;
|
||||
BBMax = arch.CBaseArchetypeDef.bbMax;
|
||||
BSCenter = arch.CBaseArchetypeDef.bsCentre;
|
||||
BSRadius = arch.CBaseArchetypeDef.bsRadius;
|
||||
IsTimeArchetype = true;
|
||||
IsMloArchetype = false;
|
||||
LodDist = arch.CBaseArchetypeDef.lodDist;
|
||||
Times = new TimedArchetypeTimes(arch.timeFlags);
|
||||
}
|
||||
public void Init(YtypFile ytyp, CMloArchetypeDef arch)
|
||||
{
|
||||
Hash = arch.CBaseArchetypeDef.assetName;
|
||||
if (Hash.Hash == 0) Hash = arch.CBaseArchetypeDef.name;
|
||||
Ytyp = ytyp;
|
||||
MloArchetype = arch;
|
||||
DrawableDict = arch.CBaseArchetypeDef.drawableDictionary;
|
||||
TextureDict = arch.CBaseArchetypeDef.textureDictionary;
|
||||
ClipDict = arch.CBaseArchetypeDef.clipDictionary;
|
||||
BBMin = arch.CBaseArchetypeDef.bbMin;
|
||||
BBMax = arch.CBaseArchetypeDef.bbMax;
|
||||
BSCenter = arch.CBaseArchetypeDef.bsCentre;
|
||||
BSRadius = arch.CBaseArchetypeDef.bsRadius;
|
||||
IsTimeArchetype = false;
|
||||
IsMloArchetype = true;
|
||||
LodDist = arch.CBaseArchetypeDef.lodDist;
|
||||
}
|
||||
|
||||
public bool IsActive(float hour)
|
||||
{
|
||||
if (Times == null) return true;
|
||||
//if (Times.ExtraFlag) hour -= 0.5f;
|
||||
//if (hour < 0.0f) hour += 24.0f;
|
||||
int h = ((int)hour) % 24;
|
||||
if ((h < 0) || (h > 23)) return true;
|
||||
return Times.ActiveHours[h];
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (IsTimeArchetype) return TimeArchetype.ToString();
|
||||
if (IsMloArchetype) return MloArchetype.ToString();
|
||||
return BaseArchetype.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class MloArchetypeData
|
||||
{
|
||||
public CEntityDef[] entities { get; set; }
|
||||
public CMloRoomDef[] rooms { get; set; }
|
||||
public CMloPortalDef[] portals { get; set; }
|
||||
public CMloEntitySet[] entitySets { get; set; }
|
||||
public CMloTimeCycleModifier[] timeCycleModifiers { get; set; }
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class MloEntityData
|
||||
{
|
||||
public YmapEntityDef[] AllEntities { get; set; }
|
||||
|
||||
public void CreateYmapEntities(YmapEntityDef owner, MloArchetypeData mlod)
|
||||
{
|
||||
if (owner == null) return;
|
||||
if (mlod.entities == null) return;
|
||||
AllEntities = new YmapEntityDef[mlod.entities.Length];
|
||||
for (int i = 0; i < mlod.entities.Length; i++)
|
||||
{
|
||||
YmapEntityDef e = new YmapEntityDef(null, i, ref mlod.entities[i]);
|
||||
|
||||
e.MloParent = owner;
|
||||
e.Position = owner.Position + owner.Orientation.Multiply(e.Position);
|
||||
e.Orientation = Quaternion.Multiply(owner.Orientation, e.Orientation);
|
||||
|
||||
e.UpdateWidgetPosition();
|
||||
e.UpdateWidgetOrientation();
|
||||
|
||||
if ((owner.Orientation != Quaternion.Identity)&&(owner.Orientation.Z!=1.0f))
|
||||
{ }
|
||||
|
||||
AllEntities[i] = e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[TypeConverter(typeof(ExpandableObjectConverter))]
|
||||
public class TimedArchetypeTimes
|
||||
{
|
||||
public uint TimeFlags { get; set; }
|
||||
public bool[] ActiveHours { get; set; }
|
||||
public string[] ActiveHoursText { get; set; }
|
||||
public bool ExtraFlag { get; set; }
|
||||
|
||||
public TimedArchetypeTimes(uint timeFlags)
|
||||
{
|
||||
TimeFlags = 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
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
|
||||
|
||||
|
||||
|
||||
using SharpDX;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public enum Endianess
|
||||
{
|
||||
LittleEndian,
|
||||
BigEndian
|
||||
}
|
||||
|
||||
public enum DataType
|
||||
{
|
||||
Byte = 0,
|
||||
Int16 = 1,
|
||||
Int32 = 2,
|
||||
Int64 = 3,
|
||||
Uint16 = 4,
|
||||
Uint32 = 5,
|
||||
Uint64 = 6,
|
||||
Float = 7,
|
||||
Double = 8,
|
||||
String = 9,
|
||||
}
|
||||
|
||||
public class DataReader
|
||||
{
|
||||
private Stream baseStream;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the endianess of the underlying stream.
|
||||
/// </summary>
|
||||
public Endianess Endianess
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the underlying stream.
|
||||
/// </summary>
|
||||
public virtual long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position within the underlying stream.
|
||||
/// </summary>
|
||||
public virtual long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
baseStream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new data reader for the specified stream.
|
||||
/// </summary>
|
||||
public DataReader(Stream stream, Endianess endianess = Endianess.LittleEndian)
|
||||
{
|
||||
this.baseStream = stream;
|
||||
this.Endianess = endianess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from the underlying stream. This is the only method that directly accesses
|
||||
/// the data in the underlying stream.
|
||||
/// </summary>
|
||||
protected virtual byte[] ReadFromStream(int count, bool ignoreEndianess = false)
|
||||
{
|
||||
var buffer = new byte[count];
|
||||
baseStream.Read(buffer, 0, count);
|
||||
|
||||
// handle endianess
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
Array.Reverse(buffer);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a byte.
|
||||
/// </summary>
|
||||
public byte ReadByte()
|
||||
{
|
||||
return ReadFromStream(1)[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a sequence of bytes.
|
||||
/// </summary>
|
||||
public byte[] ReadBytes(int count)
|
||||
{
|
||||
return ReadFromStream(count, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a signed 16-bit value.
|
||||
/// </summary>
|
||||
public short ReadInt16()
|
||||
{
|
||||
return BitConverter.ToInt16(ReadFromStream(2), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a signed 32-bit value.
|
||||
/// </summary>
|
||||
public int ReadInt32()
|
||||
{
|
||||
return BitConverter.ToInt32(ReadFromStream(4), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a signed 64-bit value.
|
||||
/// </summary>
|
||||
public long ReadInt64()
|
||||
{
|
||||
return BitConverter.ToInt64(ReadFromStream(8), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an unsigned 16-bit value.
|
||||
/// </summary>
|
||||
public ushort ReadUInt16()
|
||||
{
|
||||
return BitConverter.ToUInt16(ReadFromStream(2), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an unsigned 32-bit value.
|
||||
/// </summary>
|
||||
public uint ReadUInt32()
|
||||
{
|
||||
return BitConverter.ToUInt32(ReadFromStream(4), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads an unsigned 64-bit value.
|
||||
/// </summary>
|
||||
public ulong ReadUInt64()
|
||||
{
|
||||
return BitConverter.ToUInt64(ReadFromStream(8), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a single precision floating point value.
|
||||
/// </summary>
|
||||
public float ReadSingle()
|
||||
{
|
||||
return BitConverter.ToSingle(ReadFromStream(4), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a double precision floating point value.
|
||||
/// </summary>
|
||||
public double ReadDouble()
|
||||
{
|
||||
return BitConverter.ToDouble(ReadFromStream(8), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a string.
|
||||
/// </summary>
|
||||
public string ReadString()
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
var temp = ReadFromStream(1)[0];
|
||||
while (temp != 0)
|
||||
{
|
||||
bytes.Add(temp);
|
||||
temp = ReadFromStream(1)[0];
|
||||
}
|
||||
|
||||
return Encoding.UTF8.GetString(bytes.ToArray());
|
||||
}
|
||||
|
||||
|
||||
public Vector3 ReadVector3()
|
||||
{
|
||||
Vector3 v = new Vector3();
|
||||
v.X = ReadSingle();
|
||||
v.Y = ReadSingle();
|
||||
v.Z = ReadSingle();
|
||||
return v;
|
||||
}
|
||||
public Vector4 ReadVector4()
|
||||
{
|
||||
Vector4 v = new Vector4();
|
||||
v.X = ReadSingle();
|
||||
v.Y = ReadSingle();
|
||||
v.Z = ReadSingle();
|
||||
v.W = ReadSingle();
|
||||
return v;
|
||||
}
|
||||
|
||||
public Matrix ReadMatrix()
|
||||
{
|
||||
Matrix m = new Matrix();
|
||||
m.M11 = ReadSingle();
|
||||
m.M21 = ReadSingle();
|
||||
m.M31 = ReadSingle();
|
||||
m.M41 = ReadSingle();
|
||||
m.M12 = ReadSingle();
|
||||
m.M22 = ReadSingle();
|
||||
m.M32 = ReadSingle();
|
||||
m.M42 = ReadSingle();
|
||||
m.M13 = ReadSingle();
|
||||
m.M23 = ReadSingle();
|
||||
m.M33 = ReadSingle();
|
||||
m.M43 = ReadSingle();
|
||||
m.M14 = ReadSingle();
|
||||
m.M24 = ReadSingle();
|
||||
m.M34 = ReadSingle();
|
||||
m.M44 = ReadSingle();
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//TODO: put this somewhere else...
|
||||
public static uint SizeOf(DataType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case DataType.Byte: return 1;
|
||||
case DataType.Int16: return 2;
|
||||
case DataType.Int32: return 4;
|
||||
case DataType.Int64: return 8;
|
||||
case DataType.Uint16: return 2;
|
||||
case DataType.Uint32: return 4;
|
||||
case DataType.Uint64: return 8;
|
||||
case DataType.Float: return 4;
|
||||
case DataType.Double: return 8;
|
||||
case DataType.String: return 0; //how long is a string..?
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class DataWriter
|
||||
{
|
||||
private Stream baseStream;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the endianess of the underlying stream.
|
||||
/// </summary>
|
||||
public Endianess Endianess
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the underlying stream.
|
||||
/// </summary>
|
||||
public virtual long Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position within the underlying stream.
|
||||
/// </summary>
|
||||
public virtual long Position
|
||||
{
|
||||
get
|
||||
{
|
||||
return baseStream.Position;
|
||||
}
|
||||
set
|
||||
{
|
||||
baseStream.Position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new data writer for the specified stream.
|
||||
/// </summary>
|
||||
public DataWriter(Stream stream, Endianess endianess = Endianess.LittleEndian)
|
||||
{
|
||||
this.baseStream = stream;
|
||||
this.Endianess = endianess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the underlying stream. This is the only method that directly accesses
|
||||
/// the data in the underlying stream.
|
||||
/// </summary>
|
||||
protected virtual void WriteToStream(byte[] value, bool ignoreEndianess = false)
|
||||
{
|
||||
if (!ignoreEndianess && (Endianess == Endianess.BigEndian))
|
||||
{
|
||||
var buffer = (byte[])value.Clone();
|
||||
Array.Reverse(buffer);
|
||||
baseStream.Write(buffer, 0, buffer.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
baseStream.Write(value, 0, value.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a byte.
|
||||
/// </summary>
|
||||
public void Write(byte value)
|
||||
{
|
||||
WriteToStream(new byte[] { value });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a sequence of bytes.
|
||||
/// </summary>
|
||||
public void Write(byte[] value)
|
||||
{
|
||||
WriteToStream(value, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a signed 16-bit value.
|
||||
/// </summary>
|
||||
public void Write(short value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a signed 32-bit value.
|
||||
/// </summary>
|
||||
public void Write(int value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a signed 64-bit value.
|
||||
/// </summary>
|
||||
public void Write(long value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an unsigned 16-bit value.
|
||||
/// </summary>
|
||||
public void Write(ushort value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an unsigned 32-bit value.
|
||||
/// </summary>
|
||||
public void Write(uint value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes an unsigned 64-bit value.
|
||||
/// </summary>
|
||||
public void Write(ulong value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a single precision floating point value.
|
||||
/// </summary>
|
||||
public void Write(float value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a double precision floating point value.
|
||||
/// </summary>
|
||||
public void Write(double value)
|
||||
{
|
||||
WriteToStream(BitConverter.GetBytes(value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a string.
|
||||
/// </summary>
|
||||
public void Write(string value)
|
||||
{
|
||||
foreach (var c in value)
|
||||
Write((byte)c);
|
||||
Write((byte)0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void Write(Vector3 value)
|
||||
{
|
||||
Write(value.X);
|
||||
Write(value.Y);
|
||||
Write(value.Z);
|
||||
}
|
||||
public void Write(Vector4 value)
|
||||
{
|
||||
Write(value.X);
|
||||
Write(value.Y);
|
||||
Write(value.Z);
|
||||
Write(value.W);
|
||||
}
|
||||
|
||||
public void Write(Matrix value)
|
||||
{
|
||||
Write(value.M11);
|
||||
Write(value.M21);
|
||||
Write(value.M31);
|
||||
Write(value.M41);
|
||||
Write(value.M12);
|
||||
Write(value.M22);
|
||||
Write(value.M32);
|
||||
Write(value.M42);
|
||||
Write(value.M13);
|
||||
Write(value.M23);
|
||||
Write(value.M33);
|
||||
Write(value.M43);
|
||||
Write(value.M14);
|
||||
Write(value.M24);
|
||||
Write(value.M34);
|
||||
Write(value.M44);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,803 @@
|
||||
/*
|
||||
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 ToVector3()
|
||||
{
|
||||
const float usmax = (float)ushort.MaxValue;
|
||||
return new Vector3(X / usmax, Y / usmax, 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,149 @@
|
||||
/*
|
||||
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 SharpDX.DXGI;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,423 @@
|
||||
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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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.ToLower(); //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.ToLower();
|
||||
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();
|
||||
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);
|
||||
//JenkIndex.Ensure(entry.Path);
|
||||
//JenkIndex.Ensure(entry.Path.ToLower());
|
||||
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));
|
||||
//}
|
||||
}
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
//failing silently!! not so good really
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
using SharpDX.DXGI;
|
||||
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.ToLower());
|
||||
}
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
|
||||
public static class TextureFormats
|
||||
{
|
||||
public static SharpDX.DXGI.Format GetDXGIFormat(TextureFormat fmt)
|
||||
{
|
||||
SharpDX.DXGI.Format format = SharpDX.DXGI.Format.Unknown;
|
||||
switch (fmt)
|
||||
{
|
||||
// compressed
|
||||
case TextureFormat.D3DFMT_DXT1: format = SharpDX.DXGI.Format.BC1_UNorm; break;
|
||||
case TextureFormat.D3DFMT_DXT3: format = SharpDX.DXGI.Format.BC2_UNorm; break;
|
||||
case TextureFormat.D3DFMT_DXT5: format = SharpDX.DXGI.Format.BC3_UNorm; break;
|
||||
case TextureFormat.D3DFMT_ATI1: format = SharpDX.DXGI.Format.BC4_UNorm; break;
|
||||
case TextureFormat.D3DFMT_ATI2: format = SharpDX.DXGI.Format.BC5_UNorm; break;
|
||||
case TextureFormat.D3DFMT_BC7: format = SharpDX.DXGI.Format.BC7_UNorm; break;
|
||||
|
||||
// uncompressed
|
||||
case TextureFormat.D3DFMT_A1R5G5B5: format = SharpDX.DXGI.Format.B5G5R5A1_UNorm; break;
|
||||
case TextureFormat.D3DFMT_A8: format = SharpDX.DXGI.Format.A8_UNorm; break;
|
||||
case TextureFormat.D3DFMT_A8B8G8R8: format = SharpDX.DXGI.Format.R8G8B8A8_UNorm; break;
|
||||
case TextureFormat.D3DFMT_L8: format = SharpDX.DXGI.Format.R8_UNorm; break;
|
||||
case TextureFormat.D3DFMT_A8R8G8B8: format = SharpDX.DXGI.Format.B8G8R8A8_UNorm; break;
|
||||
}
|
||||
return format;
|
||||
}
|
||||
|
||||
public static int ByteSize(TextureFormat fmt)
|
||||
{
|
||||
switch (fmt)
|
||||
{
|
||||
// compressed
|
||||
case TextureFormat.D3DFMT_DXT1: return 4;// BC1_UNorm
|
||||
case TextureFormat.D3DFMT_DXT3: return 8;// BC2_UNorm
|
||||
case TextureFormat.D3DFMT_DXT5: return 8;// BC3_UNorm
|
||||
case TextureFormat.D3DFMT_ATI1: return 4;// BC4_UNorm
|
||||
case TextureFormat.D3DFMT_ATI2: return 8;// BC5_UNorm
|
||||
case TextureFormat.D3DFMT_BC7: return 8;// BC7_UNorm
|
||||
|
||||
// uncompressed
|
||||
case TextureFormat.D3DFMT_A1R5G5B5: return 16;// B5G5R5A1_UNorm
|
||||
case TextureFormat.D3DFMT_A8: return 8;// A8_UNorm
|
||||
case TextureFormat.D3DFMT_A8B8G8R8: return 32;// R8G8B8A8_UNorm
|
||||
case TextureFormat.D3DFMT_L8: return 8;// R8_UNorm
|
||||
case TextureFormat.D3DFMT_A8R8G8B8: return 32;// B8G8R8A8_UNorm
|
||||
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void ComputePitch(Format fmt, int width, int height, out int rowPitch, out int slicePitch, uint flags)
|
||||
{
|
||||
int nbw, nbh;
|
||||
switch (fmt)
|
||||
{
|
||||
case Format.BC1_Typeless:
|
||||
case Format.BC1_UNorm:
|
||||
case Format.BC1_UNorm_SRgb:
|
||||
case Format.BC4_Typeless:
|
||||
case Format.BC4_UNorm:
|
||||
case Format.BC4_SNorm:
|
||||
nbw = Math.Max(1, (width + 3) / 4);
|
||||
nbh = Math.Max(1, (height + 3) / 4);
|
||||
rowPitch = nbw * 8;
|
||||
slicePitch = rowPitch * nbh;
|
||||
break;
|
||||
case Format.BC2_Typeless:
|
||||
case Format.BC2_UNorm:
|
||||
case Format.BC2_UNorm_SRgb:
|
||||
case Format.BC3_Typeless:
|
||||
case Format.BC3_UNorm:
|
||||
case Format.BC3_UNorm_SRgb:
|
||||
case Format.BC5_Typeless:
|
||||
case Format.BC5_UNorm:
|
||||
case Format.BC5_SNorm:
|
||||
case Format.BC6H_Typeless:
|
||||
case Format.BC6H_Uf16:
|
||||
case Format.BC6H_Sf16:
|
||||
case Format.BC7_Typeless:
|
||||
case Format.BC7_UNorm:
|
||||
case Format.BC7_UNorm_SRgb:
|
||||
nbw = Math.Max(1, (width + 3) / 4);
|
||||
nbh = Math.Max(1, (height + 3) / 4);
|
||||
rowPitch = nbw * 16;
|
||||
slicePitch = rowPitch * nbh;
|
||||
break;
|
||||
|
||||
case Format.R8G8_B8G8_UNorm:
|
||||
case Format.G8R8_G8B8_UNorm:
|
||||
case Format.YUY2:
|
||||
rowPitch = ((width + 1) >> 1) * 4;
|
||||
slicePitch = rowPitch * height;
|
||||
break;
|
||||
|
||||
case Format.Y210:
|
||||
case Format.Y216:
|
||||
rowPitch = ((width + 1) >> 1) * 8;
|
||||
slicePitch = rowPitch * height;
|
||||
break;
|
||||
|
||||
case Format.NV12:
|
||||
case Format.Opaque420:
|
||||
rowPitch = ((width + 1) >> 1) * 2;
|
||||
slicePitch = rowPitch * (height + ((height + 1) >> 1));
|
||||
break;
|
||||
|
||||
case Format.P010:
|
||||
case Format.P016:
|
||||
//case Format.XBOX_DXGI_FORMAT_D16_UNORM_S8_UINT:
|
||||
//case Format.XBOX_DXGI_FORMAT_R16_UNORM_X8_TYPELESS:
|
||||
//case Format.XBOX_DXGI_FORMAT_X16_TYPELESS_G8_UINT:
|
||||
rowPitch = ((width + 1) >> 1) * 4;
|
||||
slicePitch = rowPitch * (height + ((height + 1) >> 1));
|
||||
break;
|
||||
|
||||
case Format.NV11:
|
||||
rowPitch = ((width + 3) >> 2) * 4;
|
||||
slicePitch = rowPitch * height * 2;
|
||||
break;
|
||||
|
||||
//case Format.WIN10_DXGI_FORMAT_P208:
|
||||
// rowPitch = ((width + 1) >> 1) * 2;
|
||||
// slicePitch = rowPitch * height * 2;
|
||||
// break;
|
||||
|
||||
//case Format.WIN10_DXGI_FORMAT_V208:
|
||||
// rowPitch = width;
|
||||
// slicePitch = rowPitch * (height + (((height + 1) >> 1) * 2));
|
||||
// break;
|
||||
|
||||
//case Format.WIN10_DXGI_FORMAT_V408:
|
||||
// rowPitch = width;
|
||||
// slicePitch = rowPitch * (height + ((height >> 1) * 4));
|
||||
// break;
|
||||
|
||||
default:
|
||||
int bpp = FormatHelper.SizeOfInBytes(fmt) * 8;
|
||||
// Default byte alignment
|
||||
rowPitch = (width * bpp + 7) / 8;
|
||||
slicePitch = rowPitch * height;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
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
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
public class GTACrypto
|
||||
{
|
||||
|
||||
|
||||
public static byte[] DecryptAES(byte[] data)
|
||||
{
|
||||
return DecryptAESData(data, GTA5Keys.PC_AES_KEY);
|
||||
}
|
||||
|
||||
public static byte[] DecryptAESData(byte[] data, byte[] key, int rounds = 1)
|
||||
{
|
||||
var rijndael = Rijndael.Create();
|
||||
rijndael.KeySize = 256;
|
||||
rijndael.Key = key;
|
||||
rijndael.BlockSize = 128;
|
||||
rijndael.Mode = CipherMode.ECB;
|
||||
rijndael.Padding = PaddingMode.None;
|
||||
|
||||
var buffer = (byte[])data.Clone();
|
||||
var length = data.Length - data.Length % 16;
|
||||
|
||||
// decrypt...
|
||||
if (length > 0)
|
||||
{
|
||||
var decryptor = rijndael.CreateDecryptor();
|
||||
for (var roundIndex = 0; roundIndex < rounds; roundIndex++)
|
||||
decryptor.TransformBlock(buffer, 0, length, buffer, 0);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
public static byte[] EncryptAESData(byte[] data, byte[] key, int rounds = 1)
|
||||
{
|
||||
var rijndael = Rijndael.Create();
|
||||
rijndael.KeySize = 256;
|
||||
rijndael.Key = key;
|
||||
rijndael.BlockSize = 128;
|
||||
rijndael.Mode = CipherMode.ECB;
|
||||
rijndael.Padding = PaddingMode.None;
|
||||
|
||||
var buffer = (byte[])data.Clone();
|
||||
var length = data.Length - data.Length % 16;
|
||||
|
||||
// encrypt...
|
||||
if (length > 0)
|
||||
{
|
||||
var encryptor = rijndael.CreateEncryptor();
|
||||
for (var roundIndex = 0; roundIndex < rounds; roundIndex++)
|
||||
encryptor.TransformBlock(buffer, 0, length, buffer, 0);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static byte[] GetNGKey(string name, uint length)
|
||||
{
|
||||
uint hash = GTA5Hash.CalculateHash(name);
|
||||
uint keyidx = (hash + length + (101 - 40)) % 0x65;
|
||||
return GTA5Keys.PC_NG_KEYS[keyidx];
|
||||
}
|
||||
|
||||
|
||||
public static byte[] DecryptNG(byte[] data, string name, uint length)
|
||||
{
|
||||
byte[] key = GetNGKey(name, length);
|
||||
return DecryptNG(data, key);
|
||||
}
|
||||
|
||||
public static byte[] DecryptNG(byte[] data, byte[] key)
|
||||
{
|
||||
var decryptedData = new byte[data.Length];
|
||||
|
||||
var keyuints = new uint[key.Length / 4];
|
||||
Buffer.BlockCopy(key, 0, keyuints, 0, key.Length);
|
||||
|
||||
for (int blockIndex = 0; blockIndex < data.Length / 16; blockIndex++)
|
||||
{
|
||||
var encryptedBlock = new byte[16];
|
||||
Array.Copy(data, 16 * blockIndex, encryptedBlock, 0, 16);
|
||||
var decryptedBlock = DecryptNGBlock(encryptedBlock, keyuints);
|
||||
Array.Copy(decryptedBlock, 0, decryptedData, 16 * blockIndex, 16);
|
||||
}
|
||||
|
||||
if (data.Length % 16 != 0)
|
||||
{
|
||||
var left = data.Length % 16;
|
||||
Buffer.BlockCopy(data, data.Length - left, decryptedData, data.Length - left, left);
|
||||
}
|
||||
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
public static byte[] DecryptNGBlock(byte[] data, uint[] key)
|
||||
{
|
||||
var buffer = data;
|
||||
|
||||
// prepare key...
|
||||
var subKeys = new uint[17][];
|
||||
for (int i = 0; i < 17; i++)
|
||||
{
|
||||
subKeys[i] = new uint[4];
|
||||
subKeys[i][0] = key[4 * i + 0];
|
||||
subKeys[i][1] = key[4 * i + 1];
|
||||
subKeys[i][2] = key[4 * i + 2];
|
||||
subKeys[i][3] = key[4 * i + 3];
|
||||
}
|
||||
|
||||
buffer = DecryptNGRoundA(buffer, subKeys[0], GTA5Keys.PC_NG_DECRYPT_TABLES[0]);
|
||||
buffer = DecryptNGRoundA(buffer, subKeys[1], GTA5Keys.PC_NG_DECRYPT_TABLES[1]);
|
||||
for (int k = 2; k <= 15; k++)
|
||||
buffer = DecryptNGRoundB(buffer, subKeys[k], GTA5Keys.PC_NG_DECRYPT_TABLES[k]);
|
||||
buffer = DecryptNGRoundA(buffer, subKeys[16], GTA5Keys.PC_NG_DECRYPT_TABLES[16]);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
// round 1,2,16
|
||||
public static byte[] DecryptNGRoundA(byte[] data, uint[] key, uint[][] table)
|
||||
{
|
||||
var x1 =
|
||||
table[0][data[0]] ^
|
||||
table[1][data[1]] ^
|
||||
table[2][data[2]] ^
|
||||
table[3][data[3]] ^
|
||||
key[0];
|
||||
var x2 =
|
||||
table[4][data[4]] ^
|
||||
table[5][data[5]] ^
|
||||
table[6][data[6]] ^
|
||||
table[7][data[7]] ^
|
||||
key[1];
|
||||
var x3 =
|
||||
table[8][data[8]] ^
|
||||
table[9][data[9]] ^
|
||||
table[10][data[10]] ^
|
||||
table[11][data[11]] ^
|
||||
key[2];
|
||||
var x4 =
|
||||
table[12][data[12]] ^
|
||||
table[13][data[13]] ^
|
||||
table[14][data[14]] ^
|
||||
table[15][data[15]] ^
|
||||
key[3];
|
||||
|
||||
var result = new byte[16];
|
||||
Array.Copy(BitConverter.GetBytes(x1), 0, result, 0, 4);
|
||||
Array.Copy(BitConverter.GetBytes(x2), 0, result, 4, 4);
|
||||
Array.Copy(BitConverter.GetBytes(x3), 0, result, 8, 4);
|
||||
Array.Copy(BitConverter.GetBytes(x4), 0, result, 12, 4);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// round 3-15
|
||||
public static byte[] DecryptNGRoundB(byte[] data, uint[] key, uint[][] table)
|
||||
{
|
||||
var x1 =
|
||||
table[0][data[0]] ^
|
||||
table[7][data[7]] ^
|
||||
table[10][data[10]] ^
|
||||
table[13][data[13]] ^
|
||||
key[0];
|
||||
var x2 =
|
||||
table[1][data[1]] ^
|
||||
table[4][data[4]] ^
|
||||
table[11][data[11]] ^
|
||||
table[14][data[14]] ^
|
||||
key[1];
|
||||
var x3 =
|
||||
table[2][data[2]] ^
|
||||
table[5][data[5]] ^
|
||||
table[8][data[8]] ^
|
||||
table[15][data[15]] ^
|
||||
key[2];
|
||||
var x4 =
|
||||
table[3][data[3]] ^
|
||||
table[6][data[6]] ^
|
||||
table[9][data[9]] ^
|
||||
table[12][data[12]] ^
|
||||
key[3];
|
||||
|
||||
//var result = new byte[16];
|
||||
//Array.Copy(BitConverter.GetBytes(x1), 0, result, 0, 4);
|
||||
//Array.Copy(BitConverter.GetBytes(x2), 0, result, 4, 4);
|
||||
//Array.Copy(BitConverter.GetBytes(x3), 0, result, 8, 4);
|
||||
//Array.Copy(BitConverter.GetBytes(x4), 0, result, 12, 4);
|
||||
//return result;
|
||||
|
||||
var result = new byte[16];
|
||||
result[0] = (byte)((x1 >> 0) & 0xFF);
|
||||
result[1] = (byte)((x1 >> 8) & 0xFF);
|
||||
result[2] = (byte)((x1 >> 16) & 0xFF);
|
||||
result[3] = (byte)((x1 >> 24) & 0xFF);
|
||||
result[4] = (byte)((x2 >> 0) & 0xFF);
|
||||
result[5] = (byte)((x2 >> 8) & 0xFF);
|
||||
result[6] = (byte)((x2 >> 16) & 0xFF);
|
||||
result[7] = (byte)((x2 >> 24) & 0xFF);
|
||||
result[8] = (byte)((x3 >> 0) & 0xFF);
|
||||
result[9] = (byte)((x3 >> 8) & 0xFF);
|
||||
result[10] = (byte)((x3 >> 16) & 0xFF);
|
||||
result[11] = (byte)((x3 >> 24) & 0xFF);
|
||||
result[12] = (byte)((x4 >> 0) & 0xFF);
|
||||
result[13] = (byte)((x4 >> 8) & 0xFF);
|
||||
result[14] = (byte)((x4 >> 16) & 0xFF);
|
||||
result[15] = (byte)((x4 >> 24) & 0xFF);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CodeWalker.GameFiles
|
||||
{
|
||||
|
||||
public class JenkHash
|
||||
{
|
||||
public JenkHashInputEncoding Encoding { get; set; }
|
||||
public string Text { get; set; }
|
||||
public int HashInt { get; set; }
|
||||
public uint HashUint { get; set; }
|
||||
public string HashHex { get; set; }
|
||||
|
||||
public JenkHash(string text, JenkHashInputEncoding encoding)
|
||||
{
|
||||
Encoding = encoding;
|
||||
Text = text;
|
||||
HashUint = GenHash(text, encoding);
|
||||
HashInt = (int)HashUint;
|
||||
HashHex = "0x" + HashUint.ToString("X");
|
||||
}
|
||||
|
||||
|
||||
public static uint GenHash(string text, JenkHashInputEncoding encoding)
|
||||
{
|
||||
uint h = 0;
|
||||
byte[] chars;
|
||||
|
||||
switch (encoding)
|
||||
{
|
||||
default:
|
||||
case JenkHashInputEncoding.UTF8:
|
||||
chars = UTF8Encoding.UTF8.GetBytes(text);
|
||||
break;
|
||||
case JenkHashInputEncoding.ASCII:
|
||||
chars = ASCIIEncoding.ASCII.GetBytes(text);
|
||||
break;
|
||||
}
|
||||
|
||||
for (uint i = 0; i < chars.Length; i++)
|
||||
{
|
||||
h += chars[i];
|
||||
h += (h << 10);
|
||||
h ^= (h >> 6);
|
||||
}
|
||||
h += (h << 3);
|
||||
h ^= (h >> 11);
|
||||
h += (h << 15);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
public static uint GenHash(string text)
|
||||
{
|
||||
uint h = 0;
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
h += (byte)text[i];
|
||||
h += (h << 10);
|
||||
h ^= (h >> 6);
|
||||
}
|
||||
h += (h << 3);
|
||||
h ^= (h >> 11);
|
||||
h += (h << 15);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
public static uint GenHash(byte[] data)
|
||||
{
|
||||
uint h = 0;
|
||||
for (uint i = 0; i < data.Length; i++)
|
||||
{
|
||||
h += data[i];
|
||||
h += (h << 10);
|
||||
h ^= (h >> 6);
|
||||
}
|
||||
h += (h << 3);
|
||||
h ^= (h >> 11);
|
||||
h += (h << 15);
|
||||
return h;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public enum JenkHashInputEncoding
|
||||
{
|
||||
UTF8 = 0,
|
||||
ASCII = 1,
|
||||
}
|
||||
|
||||
|
||||
public class JenkIndMatch
|
||||
{
|
||||
public string Hash { get; set; }
|
||||
public string Value { get; set; }
|
||||
public double Score { get; set; }
|
||||
|
||||
public JenkIndMatch(string hash, string val)
|
||||
{
|
||||
Hash = hash;
|
||||
Value = val;
|
||||
CalculateScore();
|
||||
}
|
||||
|
||||
public void CalculateScore()
|
||||
{
|
||||
|
||||
int wordlength = 0;
|
||||
int wordrank = 0;
|
||||
|
||||
string okwordsymbs = " _-.";
|
||||
string goodwordsymbs = "_";
|
||||
|
||||
for (int i = 0; i < Value.Length; i++)
|
||||
{
|
||||
char c = Value[i];
|
||||
|
||||
bool wordchar = (char.IsLetter(c) || char.IsDigit(c) || goodwordsymbs.Contains(c));
|
||||
|
||||
if (wordchar)
|
||||
{
|
||||
wordlength++;
|
||||
}
|
||||
else if (okwordsymbs.Contains(c))
|
||||
{
|
||||
//wordlength++; //don't add this to the score, but allow it to continue the chain
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wordlength > 2)
|
||||
{
|
||||
wordrank += wordlength; //linear word increment, ignoring 1-2char matches
|
||||
}
|
||||
wordlength = 0;
|
||||
}
|
||||
|
||||
//wordrank += wordlength; //each sequential letter in a word contributes more to the rank, ie. 1+2+3+4+...
|
||||
}
|
||||
if (wordlength > 2)
|
||||
{
|
||||
wordrank += wordlength; //linear word increment, ignoring 1-2char matches
|
||||
}
|
||||
|
||||
|
||||
if (Value.Length > 0)
|
||||
{
|
||||
//the max value for a given length when 1+2+3+4+5+..n = n(n+1)/2
|
||||
//double n = (double)Value.Length;
|
||||
//double maxscore = n * (n + 1.0) * 0.5;
|
||||
|
||||
double n = (double)Value.Length;
|
||||
Score = (((double)wordrank) / n);
|
||||
//Score = (((double)wordrank));
|
||||
}
|
||||
else
|
||||
{
|
||||
Score = 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} -> {1} ({2:0.##})", Hash, Value, Score);
|
||||
}
|
||||
}
|
||||
|
||||
public class JenkIndProblem
|
||||
{
|
||||
public string Filename { get; set; }
|
||||
public string Excuse { get; set; }
|
||||
public int Line { get; set; }
|
||||
|
||||
public JenkIndProblem(string filepath, string excuse, int line)
|
||||
{
|
||||
Filename = Path.GetFileName(filepath);
|
||||
Excuse = excuse;
|
||||
Line = line;
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("{0} : {1} at line {2}", Filename, Excuse, Line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static class JenkIndex
|
||||
{
|
||||
public static Dictionary<uint, string> Index = new Dictionary<uint, string>();
|
||||
private static object syncRoot = new object();
|
||||
|
||||
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 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user