mirror of
https://github.com/AdAstra-LD/DS-Pokemon-Rom-Editor.git
synced 2026-05-09 12:51:54 -05:00
395 lines
16 KiB
C#
395 lines
16 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Windows.Forms;
|
|
using static DSPRE.RomInfo;
|
|
|
|
namespace DSPRE.ROMFiles {
|
|
/* ---------------------- HEADER DATA STRUCTURE (DPPt):----------------------------
|
|
|
|
0x0 // byte: Area data value
|
|
0x1 // byte: Unknown value
|
|
0x2 // ushort: Matrix number
|
|
0x4 // ushort: Script file number
|
|
0x6 // ushort: Level script file number
|
|
0x8 // ushort: Text Archive number
|
|
0xA // ushort: Day music track number
|
|
0xC // ushort: Night music track number
|
|
0xE // ushort: Wild Pokémon file number
|
|
0x10 // ushort: Event file number
|
|
|
|
* D/P:
|
|
0x12 // ushort: Index of map name in Text Archive #382 (US version)
|
|
|
|
* Plat:
|
|
0x12 // byte: Index of map name in Text Archive #382 (US version)
|
|
0x13 // byte: Map name textbox type value
|
|
|
|
0x14 // byte: Weather value
|
|
0x15 // byte: Camera value
|
|
0x16 // byte: Boolean flag: show name when entering map
|
|
0x17 // byte: Bitwise permission flags:
|
|
|
|
----------------- 1: Allow Fly
|
|
----------------- 2: ?
|
|
----------------- 3: ?
|
|
----------------- 4: Allow Bike usage
|
|
----------------- 5: ?
|
|
----------------- 6: ?
|
|
----------------- 7: Esc. Rope
|
|
----------------- 8: ?
|
|
|
|
/* ---------------------- HEADER DATA STRUCTURE (HGSS):----------------------------
|
|
|
|
0x0 // byte: Wild Pokémon file number
|
|
0x1 // byte: Area data value
|
|
0x2 // byte: ?
|
|
0x3 // byte: ?
|
|
0x4 // ushort: Matrix number
|
|
0x6 // ushort: Script file number
|
|
0x8 // ushort: Level script file
|
|
0xA // ushort: Text Archive number
|
|
0xC // ushort: Day music track number
|
|
0xE // ushort: Night music track number
|
|
0x10 // ushort: Event file number
|
|
0x12 // byte: Index of map name in Text Archive #382 (US version)
|
|
0x13 // byte: Map name textbox type value
|
|
0x14 // byte: Weather value
|
|
0x15 // byte: Camera value
|
|
0x16 // byte: Follow mode (for the Pokémon following hero)
|
|
0x17 // byte: Bitwise permission flags:
|
|
|
|
DPPT
|
|
----------------- 1: Allow Fly
|
|
----------------- 2: Allow Esc. Rope
|
|
----------------- 3: Allow Running
|
|
----------------- 4: Allow Bike
|
|
----------------- 5: Battle BG b4
|
|
----------------- 6: Battle BG b3
|
|
----------------- 7: Battle BG b2
|
|
----------------- 8: Battle BG b1
|
|
|
|
HGSS
|
|
----------------- 1: ?
|
|
----------------- 2: ?
|
|
----------------- 3: ?
|
|
----------------- 4: Allow Fly
|
|
----------------- 5: Allow Esc. Rope
|
|
----------------- 6: ?
|
|
----------------- 7: Allow Bicycle
|
|
----------------- 8: ?
|
|
|
|
----------------------------------------------------------------------------------*/
|
|
|
|
/// <summary>
|
|
/// General class to store common map header data across all Gen IV Pokémon NDS games
|
|
/// </summary>
|
|
public abstract class MapHeader : RomFile {
|
|
/*System*/
|
|
public static readonly string DefaultFilter = "DSPRE Header File (*.dsh; *.bin)|*.dsh;*.bin";
|
|
public ushort ID { get; set; }
|
|
public static readonly byte length = 24;
|
|
public static readonly string nameSeparator = " - ";
|
|
public const int HGSS_NULL_ENCOUNTER_FILE_ID = 0xff;
|
|
public const int DPPT_NULL_ENCOUNTER_FILE_ID = 0xffff;
|
|
|
|
public enum SearchableFields: byte {
|
|
AreaDataID,
|
|
CameraAngleID,
|
|
EventFileID,
|
|
InternalName,
|
|
LevelScriptID,
|
|
MatrixID,
|
|
MusicDayID,
|
|
//MusicDayName,
|
|
MusicNightID,
|
|
//MusicNightName,
|
|
ScriptFileID,
|
|
TextArchiveID,
|
|
WeatherID,
|
|
};
|
|
|
|
|
|
|
|
#region Fields (10)
|
|
public byte areaDataID { get; set; }
|
|
public byte cameraAngleID { get; set; }
|
|
public ushort eventFileID { get; set; }
|
|
public ushort levelScriptID { get; set; }
|
|
public ushort matrixID { get; set; }
|
|
public ushort scriptFileID { get; set; }
|
|
public ushort musicDayID { get; set; }
|
|
public ushort musicNightID { get; set; }
|
|
public byte locationSpecifier { get; set; }
|
|
public byte battleBackground { get; set; }
|
|
public ushort textArchiveID { get; set; }
|
|
public byte weatherID { get; set; }
|
|
public byte flags { get; set; }
|
|
public ushort wildPokemon { get; set; }
|
|
#endregion Fields
|
|
|
|
#region Methods (1)
|
|
public static MapHeader LoadFromByteArray(byte[] headerData, ushort headerNumber, gFamEnum gameFamily = gFamEnum.NULL) {
|
|
/* Encapsulate header data into the class appropriate for the gameVersion */
|
|
if (headerData.Length < MapHeader.length) {
|
|
MessageBox.Show("File of header " + headerNumber + " is too small and can't store header data.", "Header file too small", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
return null;
|
|
}
|
|
|
|
if (gameFamily == gFamEnum.NULL) {
|
|
gameFamily = RomInfo.gameFamily;
|
|
}
|
|
|
|
switch (gameFamily) {
|
|
case gFamEnum.DP:
|
|
return new HeaderDP(headerNumber, new MemoryStream(headerData));
|
|
case gFamEnum.Plat:
|
|
return new HeaderPt(headerNumber, new MemoryStream(headerData));
|
|
default:
|
|
return new HeaderHGSS(headerNumber, new MemoryStream(headerData));
|
|
}
|
|
}
|
|
public static MapHeader LoadFromFile(string filename, ushort headerNumber, long offsetInFile, gFamEnum gameFamily = gFamEnum.NULL) {
|
|
/* Calculate header offset and load data */
|
|
byte[] headerData = DSUtils.ReadFromFile(filename, offsetInFile, MapHeader.length);
|
|
return LoadFromByteArray(headerData, headerNumber, gameFamily);
|
|
}
|
|
public static MapHeader LoadFromARM9(ushort headerNumber, gFamEnum gameFamily = gFamEnum.NULL) {
|
|
long headerOffset = RomInfo.headerTableOffset + MapHeader.length * headerNumber;
|
|
return LoadFromFile(RomInfo.arm9Path, headerNumber, headerOffset, gameFamily);
|
|
}
|
|
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class to store map header data from Pokémon D and P
|
|
/// </summary>
|
|
public class HeaderDP : MapHeader {
|
|
#region Fields (5)
|
|
public byte unknown1 { get; set; }
|
|
public ushort locationName { get; set; }
|
|
#endregion Fields
|
|
|
|
#region Constructors (1)
|
|
public HeaderDP(ushort headerNumber, Stream data) {
|
|
this.ID = headerNumber;
|
|
using (BinaryReader reader = new BinaryReader(data)) {
|
|
areaDataID = reader.ReadByte();
|
|
unknown1 = reader.ReadByte();
|
|
matrixID = reader.ReadUInt16();
|
|
scriptFileID = reader.ReadUInt16();
|
|
levelScriptID = reader.ReadUInt16();
|
|
textArchiveID = reader.ReadUInt16();
|
|
musicDayID = reader.ReadUInt16();
|
|
musicNightID = reader.ReadUInt16();
|
|
wildPokemon = reader.ReadUInt16();
|
|
eventFileID = reader.ReadUInt16();
|
|
locationName = reader.ReadUInt16();
|
|
weatherID = reader.ReadByte();
|
|
cameraAngleID = reader.ReadByte();
|
|
locationSpecifier = reader.ReadByte();
|
|
|
|
byte mapSettings = reader.ReadByte();
|
|
battleBackground = (byte)(mapSettings & 0b_1111);
|
|
flags = (byte)(mapSettings >> 4 & 0b_1111);
|
|
}
|
|
}
|
|
#endregion Constructors
|
|
|
|
#region Methods (1)
|
|
public override byte[] ToByteArray() {
|
|
MemoryStream newData = new MemoryStream();
|
|
using (BinaryWriter writer = new BinaryWriter(newData)) {
|
|
writer.Write(areaDataID);
|
|
writer.Write(unknown1);
|
|
writer.Write(matrixID);
|
|
writer.Write(scriptFileID);
|
|
writer.Write(levelScriptID);
|
|
writer.Write(textArchiveID);
|
|
writer.Write(musicDayID);
|
|
writer.Write(musicNightID);
|
|
writer.Write(wildPokemon);
|
|
writer.Write(eventFileID);
|
|
writer.Write(locationName);
|
|
writer.Write(weatherID);
|
|
writer.Write(cameraAngleID);
|
|
writer.Write(locationSpecifier);
|
|
|
|
byte mapSettings = (byte)((battleBackground & 0b_1111) + ((flags & 0b_1111) << 4));
|
|
writer.Write(mapSettings);
|
|
}
|
|
return newData.ToArray();
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class to store map header data from Pokémon Plat
|
|
/// </summary>
|
|
public class HeaderPt : MapHeader {
|
|
#region Fields (5)
|
|
public byte areaIcon { get; set; }
|
|
public byte locationName { get; set; }
|
|
public byte unknown1 { get; set; }
|
|
#endregion Fields
|
|
|
|
#region Constructors (1)
|
|
public HeaderPt(ushort headerNumber, Stream data) {
|
|
this.ID = headerNumber;
|
|
using (BinaryReader reader = new BinaryReader(data)) {
|
|
try {
|
|
areaDataID = reader.ReadByte();
|
|
unknown1 = reader.ReadByte();
|
|
matrixID = reader.ReadUInt16();
|
|
scriptFileID = reader.ReadUInt16();
|
|
levelScriptID = reader.ReadUInt16();
|
|
textArchiveID = reader.ReadUInt16();
|
|
musicDayID = reader.ReadUInt16();
|
|
musicNightID = reader.ReadUInt16();
|
|
wildPokemon = reader.ReadUInt16();
|
|
eventFileID = reader.ReadUInt16();
|
|
locationName = reader.ReadByte();
|
|
areaIcon = reader.ReadByte();
|
|
weatherID = reader.ReadByte();
|
|
cameraAngleID = reader.ReadByte();
|
|
|
|
ushort mapSettings = reader.ReadUInt16();
|
|
locationSpecifier = (byte)(mapSettings & 0b_1111_111);
|
|
battleBackground = (byte)(mapSettings >> 7 & 0b_1111_1);
|
|
flags = (byte)(mapSettings >> 12 & 0b_1111);
|
|
|
|
} catch (EndOfStreamException) {
|
|
MessageBox.Show("Error loading header " + ID + '.', "Unexpected EOF", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
}
|
|
}
|
|
}
|
|
#endregion Constructors
|
|
|
|
#region Methods(1)
|
|
public override byte[] ToByteArray() {
|
|
MemoryStream newData = new MemoryStream();
|
|
using (BinaryWriter writer = new BinaryWriter(newData)) {
|
|
writer.Write(areaDataID);
|
|
writer.Write(unknown1);
|
|
writer.Write(matrixID);
|
|
writer.Write(scriptFileID);
|
|
writer.Write(levelScriptID);
|
|
writer.Write(textArchiveID);
|
|
writer.Write(musicDayID);
|
|
writer.Write(musicNightID);
|
|
writer.Write(wildPokemon);
|
|
writer.Write(eventFileID);
|
|
writer.Write(locationName);
|
|
writer.Write(areaIcon);
|
|
writer.Write(weatherID);
|
|
writer.Write(cameraAngleID);
|
|
|
|
ushort mapSettings = (ushort)((locationSpecifier & 0b_1111_111) + ((battleBackground & 0b_1111_1) << 7) + ((flags & 0b_1111) << 12));
|
|
writer.Write(mapSettings);
|
|
}
|
|
return newData.ToArray();
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Class to store map header data from Pokémon HG and SS
|
|
/// </summary>
|
|
public class HeaderHGSS : MapHeader {
|
|
#region Fields (7)
|
|
public byte areaIcon { get; set; }
|
|
public byte followMode { get; set; }
|
|
public byte locationName { get; set; }
|
|
public byte locationType { get; set; } //4 bits only
|
|
public byte unknown0 { get; set; } //4 bits only
|
|
public byte unknown1 { get; set; } //4 bits only
|
|
public byte worldmapX { get; set; } //6 bits only
|
|
public byte worldmapY { get; set; } //6 bits only
|
|
public bool kantoFlag { get; set; }
|
|
#endregion
|
|
|
|
#region Constructors (1)
|
|
public HeaderHGSS(ushort headerNumber, Stream data) {
|
|
this.ID = headerNumber;
|
|
using (BinaryReader reader = new BinaryReader(data)) {
|
|
try {
|
|
wildPokemon = reader.ReadByte();
|
|
areaDataID = reader.ReadByte();
|
|
|
|
ushort coords = reader.ReadUInt16();
|
|
unknown0 = (byte)(coords & 0b_1111); //get 4 bits
|
|
worldmapX = (byte)((coords >> 4) & 0b_1111_11); //get 6 bits after the first 4
|
|
worldmapY = (byte)((coords >> 10) & 0b_1111_11); //get 6 bits after the first 10
|
|
|
|
matrixID = reader.ReadUInt16();
|
|
scriptFileID = reader.ReadUInt16();
|
|
levelScriptID = reader.ReadUInt16();
|
|
textArchiveID = reader.ReadUInt16();
|
|
musicDayID = reader.ReadUInt16();
|
|
musicNightID = reader.ReadUInt16();
|
|
eventFileID = reader.ReadUInt16();
|
|
locationName = reader.ReadByte();
|
|
|
|
byte areaProperties = reader.ReadByte();
|
|
areaIcon = (byte)(areaProperties & 0b_1111); //get 4 bits
|
|
unknown1 = (byte)((areaProperties >> 4) & 0b_1111); //get 4 bits after the first 4
|
|
|
|
uint last32 = reader.ReadUInt32();
|
|
kantoFlag = (last32 & 0b_1) == 1; //get 1 bit
|
|
weatherID = (byte)((last32 >> 1) & 0b_1111_111); //get 7 bits after the first one
|
|
|
|
locationType = (byte)((last32 >> 8) & 0b_1111); //get 4 bits after the first 8
|
|
cameraAngleID = (byte)((last32 >> 12) & 0b_1111_11); //get 6 bits after the first 12
|
|
followMode = (byte)((last32 >> 18) & 0b_11); //get 2 bits after the first 17
|
|
battleBackground = (byte)((last32 >> 20) & 0b_1111_1); //get 5 bits after the first 19
|
|
flags = (byte)(last32 >> 25 & 0b_1111_111); //get 7 bits after the first 24
|
|
|
|
} catch (EndOfStreamException) {
|
|
MessageBox.Show("Error loading header " + ID + '.', "Unexpected EOF", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
ID = ushort.MaxValue;
|
|
}
|
|
}
|
|
}
|
|
#endregion Constructors
|
|
|
|
#region Methods(1)
|
|
public override byte[] ToByteArray() {
|
|
MemoryStream newData = new MemoryStream();
|
|
using (BinaryWriter writer = new BinaryWriter(newData)) {
|
|
writer.Write((byte)wildPokemon);
|
|
writer.Write(areaDataID);
|
|
|
|
ushort worldMapCoordinates = (ushort)((unknown0 & 0b_1111) + ((worldmapX & 0b_1111_11) << 4) + ((worldmapY & 0b_1111_11) << 10));
|
|
writer.Write(worldMapCoordinates);
|
|
|
|
writer.Write(matrixID);
|
|
writer.Write(scriptFileID);
|
|
writer.Write(levelScriptID);
|
|
writer.Write(textArchiveID);
|
|
writer.Write(musicDayID);
|
|
writer.Write(musicNightID);
|
|
writer.Write(eventFileID);
|
|
writer.Write(locationName);
|
|
|
|
byte areaProperties = (byte)((areaIcon & 0b_1111) + ((unknown1 & 0b_1111) << 4));
|
|
writer.Write(areaProperties);
|
|
|
|
uint last32 = (uint)(((weatherID & 0b_1111_111) << 1) +
|
|
((locationType & 0b_1111) << 8) +
|
|
((cameraAngleID & 0b_1111_1) << 12) +
|
|
((followMode & 0b_11) << 18) +
|
|
((battleBackground & 0b_1111_1) << 20) +
|
|
((flags & 0b_1111_111) << 25));
|
|
|
|
if (kantoFlag) {
|
|
last32++;
|
|
}
|
|
|
|
writer.Write(last32);
|
|
}
|
|
return newData.ToArray();
|
|
}
|
|
#endregion
|
|
}
|
|
} |