Merge remote-tracking branch 'upstream/master'

This commit is contained in:
nvbeusekom 2024-09-28 17:53:16 +02:00
commit e28d9ae955
11 changed files with 197 additions and 31 deletions

View File

@ -85,6 +85,8 @@ public int DexIndex
public int FormStatsIndex { get; set; }
public byte FormCount { get; set; } = 1;
public int BST => FB.Base.HP + FB.Base.ATK + FB.Base.DEF + FB.Base.SPE + FB.Base.SPA + FB.Base.SPD;
public void Write(BinaryWriter bw)
{
bw.Write(FB.Base.HP);
@ -136,10 +138,15 @@ public void Write(BinaryWriter bw)
}
}
bw.Write(tmFlags);
bw.Write(FB.KitakamiDex);
bw.Write(FB.BlueberryDex);
bw.Write((byte)FB.KitakamiDex); // always <= 255
bw.Write((byte)FB.BlueberryDex); // always <= 255
bw.Write(GetEXP(BST, FB.EvoStage, FB.BaseEXPAddend));
bw.Write((ushort)0); // align 0x50
}
private static ushort GetEXP(int bst, byte evoStage, short add)
=> (ushort)(Math.Ceiling(bst * (1 + (3 * evoStage)) / 20d) + add);
public static readonly ushort[] TMIndexes =
[
005, 036, 204, 313, 097, 189, 184, 182, 424, 422,

View File

@ -95,6 +95,7 @@ private static void Dump(Memory<byte> data, string type, TextWriter tw, int dept
case "trinity_EnvironmentParameter": DumpTrinityEnvironmentParameter(data, tw, depth); break;
case "trinity_OverrideSensorData": DumpTrinityOverrideSensorData(data, tw, depth); break;
case "pe_FlatbuffersDataComponent": DumpPEFlatbuffersDataComponent(data, tw, depth); break;
case "trinity_CharacterCreationMasterComponent": DumpTrinityCharacterCreationMasterComponent(data, tw, depth); break;
default:
if (ThrowOnUnknownType)
throw new ArgumentOutOfRangeException(nameof(type), type, null);
@ -110,6 +111,35 @@ private static void Dump(Memory<byte> data, string type, TextWriter tw, int dept
AddToBucket(data.Span, type);
}
private static void DumpTrinityCharacterCreationMasterComponent(Memory<byte> data, TextWriter tw, int depth)
{
var props = FlatBufferConverter.DeserializeFrom<TrinityCharacterCreationMasterComponent>(data);
Write(tw, depth, $"{nameof(props.Field00)}: {props.Field00}");
Write(tw, depth, $"{nameof(props.Field01)}: {props.Field01}");
Write(tw, depth, $"{nameof(props.Field02)}: {props.Field02}");
Write(tw, depth, $"{nameof(props.Field03)}: {props.Field03}");
Write(tw, depth, $"{nameof(props.Field04)}: {props.Field04}");
Write(tw, depth, $"{nameof(props.Field05)}: {props.Field05}");
Write(tw, depth, $"{nameof(props.Field06)}: {props.Field06}");
Write(tw, depth, $"{nameof(props.Field07)}: {props.Field07}");
Write(tw, depth, $"{nameof(props.Field08)}: {props.Field08}");
Write(tw, depth, $"{nameof(props.Field09)}: {props.Field09}");
Write(tw, depth, $"{nameof(props.Field10)}: {props.Field10}");
Write(tw, depth, $"{nameof(props.Field11)}: {props.Field11}");
Write(tw, depth, $"{nameof(props.Field12)}: {props.Field12}");
Write(tw, depth, $"{nameof(props.Field13)}: {props.Field13}");
Write(tw, depth, $"{nameof(props.Field14)}: {props.Field14}");
foreach (var obj in props.Field15)
{
Write(tw, depth + 1, $"{nameof(obj.ModelType)}: {obj.ModelType}");
Write(tw, depth + 1, $"{nameof(obj.ModelPath)}: {obj.ModelPath}");
Write(tw, depth + 1, $"{nameof(obj.Variant)}: {obj.Variant}");
}
Write(tw, depth, $"{nameof(props.Field16)}: {props.Field16}");
}
private static void DumpTrinityOverrideSensorData(Memory<byte> data, TextWriter tw, int depth)
{
var props = FlatBufferConverter.DeserializeFrom<TrinityOverrideSensorData>(data);

View File

@ -114,4 +114,30 @@ table TrinitySceneObject (fs_serializer) {
Field_08:[string] (required);
}
table TrinityCharacterCreationMasterComponent (fs_serializer) {
Field_00:string (required);
Field_01:byte;
Field_02:float;
Field_03:float;
Field_04:float;
Field_05:byte;
Field_06:float;
Field_07:uint;
Field_08:float;
Field_09:uint;
Field_10:float;
Field_11:uint;
Field_12:float;
Field_13:byte;
Field_14:uint;
Field_15:[TrinityCharacterCreationMasterComponentInner] (required);
Field_16:string;
}
table TrinityCharacterCreationMasterComponentInner (fs_serializer) {
ModelType:string;
ModelPath:string;
Variant:string;
}
root_type TrinitySceneObject;

View File

@ -0,0 +1,14 @@
using System;
namespace pkNX.Structures;
[Flags]
public enum MoveCritStage : int
{
// Valid for Gen4 onwards
OneOverTwentyFour = 0,
OneOverEight = 1,
Half = 2,
Guaranteed = 6
}

View File

@ -0,0 +1,31 @@
using System;
namespace pkNX.Structures;
[Flags]
public enum MoveInflict : int
{
None = 0,
Paralyze = 1,
Sleep = 2,
Freeze = 3,
Burn = 4,
Poison = 5,
Confusion = 6,
Infatuation = 7,
DamageTransiently = 8, // Used in Fire Spin, Wrap, etc.
Nightmare = 9,
Torment = 12,
Disable = 13,
Drowsiness = 14,
HealBlock = 15,
Identify = 17, // Used by Odor Sleuth and whatnot
Seed = 18, // Used by Leech Seed
Embargo = 19,
SingPerish = 20, // Used by Perish Song
Ingrain = 21,
ChopThroat = 24, // Used by Throat Chop
FireWeaken = 42, // Used by Tar Shot
ParalyzeFreezeBurn = 65535 // Used by Tri Attack
}

View File

@ -0,0 +1,18 @@
using System;
namespace pkNX.Structures;
[Flags]
public enum MoveStat : int
{
None = 0,
Attack = 1,
Defense = 2,
SpecialAttack = 3,
SpecialDefense = 4,
Speed = 5,
Accuracy = 6,
Evasiveness = 7,
All = 8 // Used in Ancient Power and the like
}

View File

@ -43,10 +43,11 @@ public TextFile(ReadOnlySpan<byte> data, TextConfig? config = null, bool remapCh
RemapChars = remapChars;
}
public TextFile(IEnumerable<string> lines, TextConfig? config = null, bool remapChars = false)
public TextFile(IEnumerable<string> lines, IEnumerable<ushort> flags, TextConfig? config = null, bool remapChars = false)
: this(config, remapChars)
{
Lines = lines.ToArray();
Flags = flags.ToArray();
}
public byte[] Data;
@ -65,24 +66,26 @@ private TextLine[] LineOffsets
get
{
var result = new TextLine[LineCount];
int sdo = (int)SectionDataOffset;
int lineOffsetsBase = (int)SectionDataOffset + sizeof(uint);
for (int i = 0; i < result.Length; i++)
{
result[i] = new TextLine
{
Offset = BitConverter.ToInt32(Data, (i * 8) + sdo + 4) + sdo,
Length = BitConverter.ToInt16(Data, (i * 8) + sdo + 8),
Offset = BitConverter.ToInt32(Data, lineOffsetsBase + (i * 8)),
Length = BitConverter.ToUInt16(Data, lineOffsetsBase + (i * 8) + 4),
Flags = BitConverter.ToUInt16(Data, lineOffsetsBase + (i * 8) + 6),
};
}
return result;
}
set
{
int sdo = (int)SectionDataOffset;
int lineOffsetsBase = (int)SectionDataOffset + sizeof(uint);
for (int i = 0; i < value.Length; i++)
{
BitConverter.GetBytes(value[i].Offset).CopyTo(Data, (i * 8) + sdo + 4);
BitConverter.GetBytes(value[i].Length).CopyTo(Data, (i * 8) + sdo + 8);
BitConverter.GetBytes(value[i].Offset).CopyTo(Data, lineOffsetsBase + (i * 8));
BitConverter.GetBytes(value[i].Length).CopyTo(Data, lineOffsetsBase + (i * 8) + 4);
BitConverter.GetBytes(value[i].Flags).CopyTo(Data, lineOffsetsBase + (i * 8) + 6);
}
}
}
@ -92,7 +95,8 @@ public byte[] GetEncryptedLine(int index)
ushort key = GetLineKey(index);
var line = LineOffsets[index];
byte[] EncryptedLineData = new byte[line.Length * 2];
Array.Copy(Data, line.Offset, EncryptedLineData, 0, EncryptedLineData.Length);
int sdo = (int)SectionDataOffset;
Array.Copy(Data, sdo + line.Offset, EncryptedLineData, 0, EncryptedLineData.Length);
return CryptLineData(EncryptedLineData, key);
}
@ -112,10 +116,11 @@ public byte[][] LineData
ushort key = KEY_BASE;
var result = new byte[LineCount][];
var lines = LineOffsets;
int sdo = (int)SectionDataOffset;
for (int i = 0; i < lines.Length; i++)
{
byte[] EncryptedLineData = new byte[lines[i].Length * 2];
Array.Copy(Data, lines[i].Offset, EncryptedLineData, 0, EncryptedLineData.Length);
Array.Copy(Data, sdo + lines[i].Offset, EncryptedLineData, 0, EncryptedLineData.Length);
result[i] = CryptLineData(EncryptedLineData, key);
key += KEY_ADVANCE;
@ -126,18 +131,21 @@ public byte[][] LineData
{
// rebuild LineInfo
var lines = new TextLine[value.Length];
int bytesUsed = 0;
int bytesUsed = sizeof(uint) + value.Length * 8; // SectionLength + LineOffsets
for (int i = 0; i < lines.Length; i++)
{
lines[i] = new TextLine { Offset = 4 + (8 * value.Length) + bytesUsed, Length = value[i].Length / 2 };
lines[i] = new TextLine { Offset = bytesUsed, Length = (ushort)(value[i].Length / 2) };
bytesUsed += value[i].Length;
if (bytesUsed % 4 == 2)
bytesUsed += 2;
}
// Apply Line Data
int sdo = (int)SectionDataOffset;
Array.Resize(ref Data, sdo + 4 + (8 * value.Length) + bytesUsed);
Array.Resize(ref Data, sdo + bytesUsed);
LineOffsets = lines;
value.SelectMany(i => i).ToArray().CopyTo(Data, Data.Length - bytesUsed);
for (int i = 0; i < value.Length; i++)
value[i].CopyTo(Data, sdo + lines[i].Offset);
TotalLength = SectionLength = (uint)(Data.Length - sdo);
LineCount = (ushort)value.Length;
}
@ -160,6 +168,18 @@ public string[] Lines
set => LineData = ConvertLinesToData(value);
}
public ushort[] Flags
{
get => LineOffsets.Select(x => x.Flags).ToArray();
set
{
var offsets = LineOffsets;
for (int i = 0; i < value.Length; i++)
offsets[i].Flags = value[i];
LineOffsets = offsets;
}
}
private byte[][] ConvertLinesToData(string?[] value)
{
ushort key = KEY_BASE;
@ -172,8 +192,6 @@ private byte[][] ConvertLinesToData(string?[] value)
var data = GetLineData(Config, RemapChars, text);
CryptLineDataInPlace(data, key);
if (data.Length % 4 == 2)
Array.Resize(ref data, data.Length + 2);
lineData[i] = data;
key += KEY_ADVANCE;
@ -506,8 +524,8 @@ private static void GetVariableParameters(TextConfig config, ReadOnlySpan<char>
catch { return null; }
}
public static byte[] GetBytes(IEnumerable<string> lines, TextConfig? config = null, bool remapChars = false)
public static byte[] GetBytes(IEnumerable<string> lines, IEnumerable<ushort> flags, TextConfig? config = null, bool remapChars = false)
{
return new TextFile(lines, config, remapChars).Data;
return new TextFile(lines, flags, config, remapChars).Data;
}
}

View File

@ -3,5 +3,6 @@ namespace pkNX.Structures;
internal class TextLine
{
public int Offset;
public int Length;
public ushort Length;
public ushort Flags;
}

View File

@ -43,7 +43,8 @@ public void Save()
{
if (Cache[i] is not { } x)
continue;
Container[i] = TextFile.GetBytes(x, Config, Remap);
var flags = new ushort[x.Length]; // TODO: handle properly, for now just zero-out flags
Container[i] = TextFile.GetBytes(x, flags, Config, Remap);
}
}
}

View File

@ -62,6 +62,7 @@
<DataGridTextColumn Header="Line" IsReadOnly="True" Binding="{Binding Line}"/>
<DataGridTextColumn Header="Variable" Binding="{Binding Variable}"/>
<DataGridTextColumn Header="Hash" IsReadOnly="True" Binding="{Binding Hash, StringFormat=0x\{0:X16\}}" Visibility="{Binding Source={x:Reference CHK_ShowHashes}, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}"/>
<DataGridTextColumn Header="Flags" Binding="{Binding Flags}"/>
<DataGridTextColumn Header="Text" Binding="{Binding Text}"/>
</DataGrid.Columns>
</DataGrid>

View File

@ -40,15 +40,17 @@ public string Variable
}
}
public ulong Hash { get; private set; }
public ushort Flags { get; set; }
public string Text { get; set; }
public bool IsReadOnly { get; }
public TextEditorEntry(int line, string variable, ulong hash, string text, bool isLastRow = false)
public TextEditorEntry(int line, string variable, ulong hash, ushort flags, string text, bool isLastRow = false)
{
Line = line;
_variable = variable;
Hash = hash;
Flags = flags;
Text = text;
IsReadOnly = isLastRow;
}
@ -177,12 +179,20 @@ static bool ValidateSeparatorIndex(int nextSeparator, int lineNumber, ReadOnlySp
{
hash = FnvHash.HashFnv1a_64(variable);
}
line = line[(nextSeparator + 1)..]; // Skip over the hash and the third '|'
nextSeparator = line.IndexOf('|');
if (!ValidateSeparatorIndex(nextSeparator, i, line))
return false;
// Skip the first two characters '0x'
ushort.TryParse(line[2..nextSeparator], NumberStyles.HexNumber, null, out var flags);
line = line[(nextSeparator + 1)..]; // Skip over the flags and the fourth '|'
string text = line.ToString(); // The rest of the line is the text
entries.Add(new TextEditorEntry(entries.Count, variable, hash, text));
entries.Add(new TextEditorEntry(entries.Count, variable, hash, flags, text));
}
if (!forceImport && Entries.Count != entries.Count)
@ -199,6 +209,7 @@ static bool ValidateSeparatorIndex(int nextSeparator, int lineNumber, ReadOnlySp
foreach (var entry in entries)
Entries.Add(entry);
Modified = true;
return true;
}
@ -217,7 +228,7 @@ public void ExportTextFile(string fileName, bool replaceNewline)
textString = textString.Replace("\\n", "\n");
}
tw.WriteLine($"|{entry.Variable}|0x{entry.Hash:X16}|{textString}");
tw.WriteLine($"|{entry.Variable}|0x{entry.Hash:X16}|0x{entry.Flags:X4}|{textString}");
}
}
File.WriteAllBytes(fileName, ms.ToArray());
@ -227,7 +238,7 @@ public void InsertEntry(int index)
{
if (Entries.Count == 0 || index < 0 || index >= Entries.Count - 1)
{
Entries.Add(new TextEditorEntry(Entries.Count, "", 0, ""));
Entries.Add(new TextEditorEntry(Entries.Count, "", 0, 0, ""));
DG_Text.SelectedIndex = Entries.Count - 1;
return;
}
@ -241,7 +252,7 @@ public void InsertEntry(int index)
}
// Insert new Row after current row.
int nextLine = index + 1;
Entries.Insert(nextLine, new TextEditorEntry(nextLine, "", 0, ""));
Entries.Insert(nextLine, new TextEditorEntry(nextLine, "", 0, 0, ""));
DG_Text.SelectedIndex = nextLine;
for (int i = nextLine + 1; i < Entries.Count; i++)
@ -277,7 +288,9 @@ public void LoadSelectedFile()
var textFile = TextFiles[LoadedFileIndex];
var tblFile = TableFiles[LoadedFileIndex];
var lines = new TextFile(textFile.ReadAllBytes(), Config).Lines;
var file = new TextFile(textFile.ReadAllBytes(), Config);
var lines = file.Lines;
var flags = file.Flags;
var tbl = new AHTB(tblFile.Open());
// The table has 1 more entry than the dat to show when the table ends
@ -290,7 +303,9 @@ public void LoadSelectedFile()
textFile.Delete(DeleteMode.TopMostWriteableLayer);
tblFile.Delete(DeleteMode.TopMostWriteableLayer);
lines = new TextFile(textFile.ReadAllBytes(), Config).Lines; // Reopen the original file
file = new TextFile(textFile.ReadAllBytes(), Config); // Reopen the original file
lines = file.Lines;
flags = file.Flags;
tbl = new AHTB(tblFile.Open()); // Reopen the original file
Debug.Assert(tbl.Entries.Length == lines.Length + 1);
@ -300,8 +315,9 @@ public void LoadSelectedFile()
{
var label = tbl.Entries[i];
bool isLast = i == tbl.Count - 1;
ushort flag = isLast ? (ushort)0 : flags[i];
var text = isLast ? "" : lines[i];
Entries.Add(new TextEditorEntry(i, label.Name, label.Hash, text, isLast));
Entries.Add(new TextEditorEntry(i, label.Name, label.Hash, flag, text, isLast));
}
}
@ -310,7 +326,7 @@ public void SaveCurrentFile()
if (!Modified)
return;
var textBytes = TextFile.GetBytes(Entries.SkipLast(1).Select(x => x.Text), Config);
var textBytes = TextFile.GetBytes(Entries.SkipLast(1).Select(x => x.Text), Entries.SkipLast(1).Select(x => x.Flags), Config);
TextFiles[LoadedFileIndex].WriteAllBytes(textBytes);
AHTB tbl = new(Entries.ToDictionary(x => x.Hash, y => y.Variable));
@ -448,6 +464,9 @@ private void DG_Text_CellEditEnding(object sender, DataGridCellEditEndingEventAr
Modified |= entry.Variable != text;
break;
case 3:
Modified |= entry.Flags.ToString() != text;
break;
case 4:
Modified |= entry.Text != text;
break;
}