mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-05-09 04:24:36 -05:00
Add cgb->psk & misc clarifications
Closes #1699 tested on black2 & white, skin isn't corrupt on game boot and appears correctly still don't like setpixel/System.Drawing reliance (maybe split logic to CGearSkin & CGearSkinVisualizer) for PKHeX.Core compat
This commit is contained in:
parent
029539003b
commit
6caefef181
|
|
@ -728,5 +728,52 @@ public override byte[] SetString(string value, int maxLength, int PadToSize = 0,
|
|||
PadToSize = maxLength + 1;
|
||||
return StringConverter.SetString5(value, maxLength, PadToSize, PadWith);
|
||||
}
|
||||
|
||||
|
||||
// DLC
|
||||
private int CGearSkinInfoOffset => CGearInfoOffset + (B2W2 ? 0x10 : 0) + 0x24;
|
||||
private bool CGearSkinPresent
|
||||
{
|
||||
get => Data[CGearSkinInfoOffset + 2] == 1;
|
||||
set => Data[CGearSkinInfoOffset + 2] = Data[Trainer1 + (B2W2 ? 0x6C : 0x54)] = (byte) (value ? 1 : 0);
|
||||
}
|
||||
public byte[] CGearSkinData
|
||||
{
|
||||
get
|
||||
{
|
||||
byte[] data = new byte[0x2600];
|
||||
if (CGearSkinPresent)
|
||||
Array.Copy(Data, CGearDataOffset, data, 0, data.Length);
|
||||
return data;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
return; // no clearing
|
||||
byte[] dlcfooter = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 };
|
||||
|
||||
byte[] bgdata = value;
|
||||
SetData(bgdata, CGearDataOffset);
|
||||
|
||||
ushort chk = SaveUtil.CRC16_CCITT(bgdata);
|
||||
var chkbytes = BitConverter.GetBytes(chk);
|
||||
int footer = CGearDataOffset + bgdata.Length;
|
||||
|
||||
BitConverter.GetBytes((ushort)1).CopyTo(Data, footer); // block updated once
|
||||
chkbytes.CopyTo(Data, footer + 2); // checksum
|
||||
chkbytes.CopyTo(Data, footer + 0x100); // second checksum
|
||||
dlcfooter.CopyTo(Data, footer + 0x102);
|
||||
ushort skinchkval = SaveUtil.CRC16_CCITT(Data, footer + 0x100, 4);
|
||||
BitConverter.GetBytes(skinchkval).CopyTo(Data, footer + 0x112);
|
||||
|
||||
// Indicate in the save file that data is present
|
||||
BitConverter.GetBytes((ushort)0xC21E).CopyTo(Data, 0x19438);
|
||||
|
||||
chkbytes.CopyTo(Data, CGearSkinInfoOffset);
|
||||
CGearSkinPresent = true;
|
||||
|
||||
Edited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,12 +30,12 @@ public class CGearBackground
|
|||
* The tiles are chosen based on the 16bit index of the tile.
|
||||
* 0x300 * 2 = 0x600!
|
||||
*
|
||||
* CGearBackgrounds tilemap (when stored) employs odd obfuscation.
|
||||
* BW obfuscates by adding 0xA0A0, B2W2 adds 0xA000
|
||||
* CGearBackgrounds tilemap (when stored on BW) employs some obfuscation.
|
||||
* BW obfuscates by adding 0xA0A0.
|
||||
* The obfuscated number is then tweaked by adding 15*(i/17)
|
||||
* To reverse, use a similar reverse calculation
|
||||
* PSK files are basically raw game rips (obfuscated)
|
||||
* CGB files are un-obfuscated.
|
||||
* CGB files are un-obfuscated / B2W2.
|
||||
* Due to BW and B2W2 using different obfuscation adds, PSK files are incompatible between the versions.
|
||||
*/
|
||||
|
||||
|
|
@ -46,7 +46,10 @@ public CGearBackground(byte[] data)
|
|||
|
||||
// decode for easy handling
|
||||
if (!IsCGB(data))
|
||||
_psk = data = PSKtoCGB(data);
|
||||
{
|
||||
_psk = data;
|
||||
data = PSKtoCGB(data);
|
||||
}
|
||||
else
|
||||
_cgb = data;
|
||||
|
||||
|
|
@ -73,11 +76,11 @@ public CGearBackground(byte[] data)
|
|||
|
||||
private byte[] _cgb;
|
||||
private byte[] _psk;
|
||||
private byte[] GetCGB() => _cgb ?? Write();
|
||||
private byte[] GetPSK() => _psk ?? CGBtoPSK(Write());
|
||||
public byte[] GetSkin(bool B2W2) => B2W2 ? GetCGB() : GetPSK();
|
||||
|
||||
public byte[] GetCGB() => _cgb ?? Write();
|
||||
public byte[] GetPSK(bool B2W2) => _psk ?? CGBtoPSK(Write(), B2W2);
|
||||
|
||||
public byte[] Write()
|
||||
private byte[] Write()
|
||||
{
|
||||
byte[] data = new byte[SIZE_CGB];
|
||||
for (int i = 0; i < Tiles.Length; i++)
|
||||
|
|
@ -91,7 +94,7 @@ public byte[] Write()
|
|||
return data;
|
||||
}
|
||||
|
||||
public static bool IsCGB(byte[] data)
|
||||
private static bool IsCGB(byte[] data)
|
||||
{
|
||||
if (data.Length != SIZE_CGB)
|
||||
return false;
|
||||
|
|
@ -102,27 +105,45 @@ public static bool IsCGB(byte[] data)
|
|||
return false;
|
||||
return true;
|
||||
}
|
||||
public static byte[] CGBtoPSK(byte[] cgb, bool B2W2)
|
||||
private static byte[] CGBtoPSK(byte[] cgb)
|
||||
{
|
||||
byte[] psk = (byte[])cgb.Clone();
|
||||
int shiftVal = B2W2 ? 0xA000 : 0xA0A0;
|
||||
for (int i = 0x2000; i < 0x2600; i += 2)
|
||||
{
|
||||
int index = BitConverter.ToUInt16(cgb, i);
|
||||
int val = IndexToVal(index, shiftVal);
|
||||
var tileVal = BitConverter.ToUInt16(cgb, i);
|
||||
int val = GetPSKValue(tileVal);
|
||||
|
||||
psk[i] = (byte)val;
|
||||
psk[i + 1] = (byte)(val >> 8);
|
||||
}
|
||||
return psk;
|
||||
}
|
||||
public static byte[] PSKtoCGB(byte[] psk)
|
||||
private static int GetPSKValue(ushort val)
|
||||
{
|
||||
int rot = val & 0xFF00;
|
||||
int tile = val & 0x00FF;
|
||||
if (tile == 0xFF) // invalid tile?
|
||||
tile = 0;
|
||||
|
||||
int result = tile + 15 * (tile / 17)
|
||||
+ 0xA0A0
|
||||
+ rot;
|
||||
return result;
|
||||
}
|
||||
private static byte[] PSKtoCGB(byte[] psk)
|
||||
{
|
||||
byte[] cgb = (byte[])psk.Clone();
|
||||
for (int i = 0x2000; i < 0x2600; i += 2)
|
||||
{
|
||||
int val = BitConverter.ToUInt16(psk, i);
|
||||
int index = ValToIndex(val);
|
||||
BitConverter.GetBytes((ushort)index).CopyTo(cgb, i);
|
||||
|
||||
byte tile = (byte)index;
|
||||
byte rot = (byte)(index >> 8);
|
||||
if (tile == 0xFF)
|
||||
tile = 0;
|
||||
cgb[i] = tile;
|
||||
cgb[i + 1] = rot;
|
||||
}
|
||||
return cgb;
|
||||
}
|
||||
|
|
@ -329,11 +350,6 @@ internal byte[] Write()
|
|||
}
|
||||
}
|
||||
|
||||
private static int IndexToVal(int index, int shiftVal)
|
||||
{
|
||||
int val = index + shiftVal;
|
||||
return val + 15*(index/17);
|
||||
}
|
||||
private static int ValToIndex(int val)
|
||||
{
|
||||
if ((val & 0x3FF) < 0xA0 || (val & 0x3FF) > 0x280)
|
||||
|
|
|
|||
|
|
@ -17,14 +17,10 @@ public SAV_CGearSkin(SaveFile sav)
|
|||
SAV = (SAV5)(Origin = sav).Clone();
|
||||
InitializeComponent();
|
||||
|
||||
bool cgearPresent = SAV.Data[SAV.CGearInfoOffset + 0x26] == 1;
|
||||
byte[] data = new byte[CGearBackground.SIZE_CGB];
|
||||
if (cgearPresent)
|
||||
Array.Copy(SAV.Data, SAV.CGearDataOffset, data, 0, CGearBackground.SIZE_CGB);
|
||||
byte[] data = SAV.CGearSkinData;
|
||||
bg = new CGearBackground(data);
|
||||
|
||||
PB_Background.Image = bg.GetImage();
|
||||
WinFormsUtil.Alert("Editor is incomplete.", "No guarantee of functionality.");
|
||||
}
|
||||
|
||||
private CGearBackground bg;
|
||||
|
|
@ -85,11 +81,6 @@ private void B_ImportCGB_Click(object sender, EventArgs e)
|
|||
}
|
||||
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
LoadBackground(data);
|
||||
}
|
||||
|
||||
private void LoadBackground(byte[] data)
|
||||
{
|
||||
bg = new CGearBackground(data);
|
||||
PB_Background.Image = bg.GetImage();
|
||||
}
|
||||
|
|
@ -104,42 +95,17 @@ private void B_ExportCGB_Click(object sender, EventArgs e)
|
|||
if (sfd.ShowDialog() != DialogResult.OK)
|
||||
return;
|
||||
|
||||
byte[] data = bg.GetCGB();
|
||||
byte[] data = bg.GetSkin(true);
|
||||
File.WriteAllBytes(sfd.FileName, data);
|
||||
}
|
||||
private void B_Save_Click(object sender, EventArgs e)
|
||||
{
|
||||
byte[] bgdata = bg.GetPSK(SAV.B2W2);
|
||||
if (bgdata.SequenceEqual(new byte[CGearBackground.SIZE_CGB]))
|
||||
return;
|
||||
|
||||
byte[] dlcfooter = { 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14, 0x27, 0x00, 0x00, 0x27, 0x35, 0x05, 0x31, 0x00, 0x00 };
|
||||
|
||||
bgdata.CopyTo(SAV.Data, SAV.CGearDataOffset);
|
||||
ushort chk = SaveUtil.CRC16_CCITT(bgdata);
|
||||
var chkbytes = BitConverter.GetBytes(chk);
|
||||
int footer = SAV.CGearDataOffset + bgdata.Length;
|
||||
|
||||
BitConverter.GetBytes((ushort)1).CopyTo(SAV.Data, footer); // block updated once
|
||||
chkbytes.CopyTo(SAV.Data, footer + 2); // checksum
|
||||
chkbytes.CopyTo(SAV.Data, footer + 0x100); // second checksum
|
||||
|
||||
ushort skinchkval = SaveUtil.CRC16_CCITT(SAV.Data, footer + 0x100, 4);
|
||||
dlcfooter.CopyTo(SAV.Data, footer + 0x104);
|
||||
BitConverter.GetBytes(skinchkval).CopyTo(SAV.Data, footer + 0x112);
|
||||
|
||||
// Indicate in the save file that data is present
|
||||
BitConverter.GetBytes((ushort)0xC21E).CopyTo(SAV.Data, 0x19438);
|
||||
|
||||
int info = SAV.CGearInfoOffset + 0x24;
|
||||
if (SAV.B2W2)
|
||||
info += 0x10;
|
||||
chkbytes.CopyTo(SAV.Data, info);
|
||||
SAV.Data[info + 2] = 1; // data present
|
||||
int flag = SAV.CGearDataOffset + (SAV.B2W2 ? 0x6C : 0x54);
|
||||
SAV.Data[flag] = 1; // data present
|
||||
|
||||
Origin.SetData(SAV.Data, 0);
|
||||
byte[] bgdata = bg.GetSkin(SAV.B2W2);
|
||||
if (!bgdata.All(z => z == 0))
|
||||
{
|
||||
SAV.CGearSkinData = bgdata;
|
||||
Origin.SetData(SAV.Data, 0);
|
||||
}
|
||||
Close();
|
||||
}
|
||||
private void B_Cancel_Click(object sender, EventArgs e)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user