mirror of
https://mirror.ghproxy.com/https://github.com/dexyfex/CodeWalker
synced 2026-05-16 10:22:59 +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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user