DS-Pokemon-Rom-Editor/DS_Map/ROMFiles/LevelScriptFile.cs
Miguel Terol Espino 5bfbde28e9 Added the levelscript editor
Co-authored-by: Cuddlyogre <cuddlyogre@users.noreply.github.com>
2024-03-06 00:37:15 +01:00

140 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
namespace DSPRE.ROMFiles {
public class LevelScriptFile {
public int ID;
public BindingList<LevelScriptTrigger> bufferSet = new BindingList<LevelScriptTrigger>();
public LevelScriptFile() { }
public LevelScriptFile(int id) {
this.ID = id;
string path1 = Filesystem.scripts;
string path = Path.Combine(path1, this.ID.ToString("D4"));
parse_file(path);
}
public void parse_file(string path) {
FileStream fs = new FileStream(path, FileMode.Open);
using (BinaryReader br = new BinaryReader(fs)) {
bool hasConditionalStructure = false;
//conditionalStructureOffset is used to ensure the structure of the file is correct
int conditionalStructureOffset = -1;
while (true) {
//first byte is the script type
//if not a valid script type, break loop
byte triggerType = br.ReadByte();
if (!LevelScriptTrigger.IsValidTriggerType(triggerType)) break;
//subtract triggerType length from conditionalStructureOffset
if (hasConditionalStructure) conditionalStructureOffset -= sizeof(byte);
//if trigger type is a variable value, that doesn't immediately mean we're processing that trigger
//the trigger data is processed last if it is there
if (triggerType == LevelScriptTrigger.VARIABLEVALUE) {
hasConditionalStructure = true;
conditionalStructureOffset = (int)br.ReadUInt32();
continue;
}
//map screen load trigger doesn't have a value or variable
uint scriptToTrigger = br.ReadUInt32();
bufferSet.Add(new MapScreenLoadTrigger(triggerType, (int)scriptToTrigger));
//subtract scriptToTrigger length from conditionalStructureOffset
if (hasConditionalStructure) conditionalStructureOffset -= sizeof(UInt32);
}
//the earliest position a trigger can be
const int SMALLEST_TRIGGER_SIZE = 5;
//if triggerType is invalid
//and next uint16 == 0
//and the file stream length is shorter than the earliest position a trigger can be
if (br.BaseStream.Position == 1 && br.ReadUInt16() == 0 && fs.Length < SMALLEST_TRIGGER_SIZE) {
return;
throw new InvalidDataException("This level script does nothing."); // "Interesting..."
}
//br.BaseStream.Position == 3
//triggerType is valid,
//stream position is earlier than the first possible trigger, or
//there is no start script condition specified
if (br.BaseStream.Position < SMALLEST_TRIGGER_SIZE) {
throw new InvalidDataException("Parser failure: The input file you attempted to load is either malformed or not a Level Script file.");
}
//there are no instances of a variable value trigger
if (!hasConditionalStructure) {
return;
}
//if there's a variable value trigger but the offset is incorrect, the file is corrupt
if (conditionalStructureOffset != 1) {
throw new InvalidDataException($"Field error: The Level Script file you attempted to load is broken. {conditionalStructureOffset}");
}
//get the variable value trigger parts
while (true) {
//there are no variables below 1
int variableID = br.ReadUInt16();
if (variableID <= 0) break;
int varExpectedValue = br.ReadUInt16();
int scriptToTrigger = br.ReadUInt16();
bufferSet.Add(new VariableValueTrigger(scriptToTrigger, variableID, varExpectedValue));
}
}
}
public long write_file(string path, bool word_alignment_padding = false) {
FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
using (BinaryWriter bw = new BinaryWriter(fs)) {
HashSet<MapScreenLoadTrigger> mapScreenLoadTriggers = new HashSet<MapScreenLoadTrigger>();
HashSet<VariableValueTrigger> variableValueTriggers = new HashSet<VariableValueTrigger>();
foreach (LevelScriptTrigger item in bufferSet) {
if (item is VariableValueTrigger variableValueTrigger) {
variableValueTriggers.Add(variableValueTrigger);
}
else if (item is MapScreenLoadTrigger mapScreenLoadTrigger) {
mapScreenLoadTriggers.Add(mapScreenLoadTrigger);
}
}
foreach (MapScreenLoadTrigger item in mapScreenLoadTriggers) {
bw.Write((byte)item.triggerType);
bw.Write((UInt32)item.scriptTriggered);
}
if (variableValueTriggers.Count > 0) {
bw.Write((byte)LevelScriptTrigger.VARIABLEVALUE);
bw.Write((UInt32)1);
bw.Write((byte)0);
foreach (VariableValueTrigger item in variableValueTriggers) {
bw.Write((UInt16)item.variableToWatch);
bw.Write((UInt16)item.expectedValue);
bw.Write((UInt16)item.scriptTriggered);
}
}
bw.Write((UInt16)0);
if (word_alignment_padding) {
long missing_bytes = bw.BaseStream.Position % 4;
for (int i = 0; i < 4 - missing_bytes; i++) {
bw.Write((byte)0);
}
}
return bw.BaseStream.Position;
}
}
}
}