mirror of
https://github.com/kwsch/PKHeX.git
synced 2026-09-11 15:35:20 -05:00
Update XD support
Still not fully implemented; can now load boxes and misc data.
This commit is contained in:
@@ -1426,7 +1426,21 @@ private void populateFilteredDataSources()
|
||||
{
|
||||
string[] items = itemlist;
|
||||
if (SAV.Generation == 3)
|
||||
{
|
||||
items = g3items;
|
||||
if (SAV.Version == GameVersion.COLO)
|
||||
{
|
||||
Array.Resize(ref g3items, SAV.MaxItemID + 1);
|
||||
// TODO: ITEMS
|
||||
}
|
||||
if (SAV.Version == GameVersion.XD)
|
||||
{
|
||||
Array.Resize(ref g3items, SAV.MaxItemID + 1);
|
||||
// TODO: ITEMS
|
||||
}
|
||||
for (int i = 374; i < g3items.Length; i++)
|
||||
g3items[i] = g3items[i] ?? $"ITEM {i}";
|
||||
}
|
||||
if (SAV.Generation == 2)
|
||||
items = g2items;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public XK3(byte[] decryptedData = null, string ident = null)
|
||||
public bool CapturedFlag { get { return (XDPKMFLAGS & (1 << 2)) == 1 << 2; } set { XDPKMFLAGS = XDPKMFLAGS & ~(1 << 2) | (value ? 1 << 2 : 0); } }
|
||||
public bool UnusedFlag3 { get { return (XDPKMFLAGS & (1 << 3)) == 1 << 3; } set { XDPKMFLAGS = XDPKMFLAGS & ~(1 << 3) | (value ? 1 << 3 : 0); } }
|
||||
public bool BlockTrades { get { return (XDPKMFLAGS & (1 << 4)) == 1 << 4; } set { XDPKMFLAGS = XDPKMFLAGS & ~(1 << 4) | (value ? 1 << 4 : 0); } }
|
||||
public override bool Valid => (XDPKMFLAGS & (1 << 5)) == 1 << 5;
|
||||
public override bool Valid => (XDPKMFLAGS & (1 << 5)) == 0; // invalid flag not set
|
||||
public int AbilityNumber { get { return (XDPKMFLAGS >> 6) & 1; } set { XDPKMFLAGS = XDPKMFLAGS & ~(1 << 6) | (value << 6); } }
|
||||
public override bool IsEgg { get { return (XDPKMFLAGS & (1 << 7)) == 1 << 7; } set { XDPKMFLAGS = XDPKMFLAGS & ~(1 << 7) | (value ? 1 << 7 : 0); } }
|
||||
// 0x1E-0x1F Unknown
|
||||
@@ -93,10 +93,10 @@ public XK3(byte[] decryptedData = null, string ident = null)
|
||||
public bool RibbonArtist { get { return (RIB0 & (1 << 03)) == 1 << 03; } set { RIB0 = (ushort)(RIB0 & ~(1 << 03) | (ushort)(value ? 1 << 03 : 0)); } }
|
||||
public bool RibbonEffort { get { return (RIB0 & (1 << 04)) == 1 << 04; } set { RIB0 = (ushort)(RIB0 & ~(1 << 04) | (ushort)(value ? 1 << 04 : 0)); } }
|
||||
public bool RibbonChampionBattle { get { return (RIB0 & (1 << 05)) == 1 << 05; } set { RIB0 = (ushort)(RIB0 & ~(1 << 05) | (ushort)(value ? 1 << 05 : 0)); } }
|
||||
public bool RibbonChampionRegional { get { return (RIB0 & (1 << 06)) == 1 << 06; } set { RIB0 = (ushort)(RIB0 & ~(1 << 06) | (ushort)(value ? 1 << 06 : 0)); } }
|
||||
public bool RibbonChampionRegional { get { return (RIB0 & (1 << 06)) == 1 << 06; } set { RIB0 = (ushort)(RIB0 & ~(1 << 06) | (ushort)(value ? 1 << 06 : 0)); } } // don't think this is right, shouldn't this be National?
|
||||
public bool RibbonChampionNational { get { return (RIB0 & (1 << 07)) == 1 << 07; } set { RIB0 = (ushort)(RIB0 & ~(1 << 07) | (ushort)(value ? 1 << 07 : 0)); } }
|
||||
public bool RibbonCountry { get { return (RIB0 & (1 << 08)) == 1 << 08; } set { RIB0 = (ushort)(RIB0 & ~(1 << 08) | (ushort)(value ? 1 << 08 : 0)); } }
|
||||
public bool RibbonNational { get { return (RIB0 & (1 << 09)) == 1 << 09; } set { RIB0 = (ushort)(RIB0 & ~(1 << 09) | (ushort)(value ? 1 << 09 : 0)); } }
|
||||
public bool RibbonNational { get { return (RIB0 & (1 << 09)) == 1 << 09; } set { RIB0 = (ushort)(RIB0 & ~(1 << 09) | (ushort)(value ? 1 << 09 : 0)); } } // Shouldn't this be Regional? (are all these bits inverse?)
|
||||
public bool RibbonEarth { get { return (RIB0 & (1 << 10)) == 1 << 10; } set { RIB0 = (ushort)(RIB0 & ~(1 << 10) | (ushort)(value ? 1 << 10 : 0)); } }
|
||||
public bool RibbonWorld { get { return (RIB0 & (1 << 11)) == 1 << 11; } set { RIB0 = (ushort)(RIB0 & ~(1 << 11) | (ushort)(value ? 1 << 11 : 0)); } }
|
||||
public bool Unused1 { get { return (RIB0 & (1 << 12)) == 1 << 12; } set { RIB0 = (ushort)(RIB0 & ~(1 << 12) | (ushort)(value ? 1 << 12 : 0)); } }
|
||||
|
||||
@@ -32,7 +32,7 @@ public SAV3Colosseum(byte[] data = null)
|
||||
BAK = (byte[])Data.Clone();
|
||||
Exportable = !Data.SequenceEqual(new byte[Data.Length]);
|
||||
|
||||
if (SaveUtil.getIsG3CXDSAV(Data) != GameVersion.COLO)
|
||||
if (SaveUtil.getIsG3COLOSAV(Data) != GameVersion.COLO)
|
||||
return;
|
||||
|
||||
OriginalData = (byte[])Data.Clone();
|
||||
@@ -113,7 +113,7 @@ public override byte[] Write(bool DSV)
|
||||
public override int MaxMoveID => 354;
|
||||
public override int MaxSpeciesID => 386;
|
||||
public override int MaxAbilityID => 77;
|
||||
public override int MaxItemID => 374;
|
||||
public override int MaxItemID => 547;
|
||||
public override int MaxBallID => 0xC;
|
||||
public override int MaxGameID => 5;
|
||||
|
||||
@@ -291,8 +291,8 @@ public override int PlayedSeconds
|
||||
}
|
||||
|
||||
// Trainer Info (offset 0x78, length 0xB18, end @ 0xB90)
|
||||
public override string OT { get { return PKX.getColoStr(Data, 0x78, 10); } set { PKX.setColoStr(value, 10).CopyTo(Data, 0x78); } }
|
||||
private string OT2 { get { return PKX.getColoStr(Data, 0x8C, 10); } set { PKX.setColoStr(value, 10).CopyTo(Data, 0x8C); OT = value; } }
|
||||
public override string OT { get { return PKX.getColoStr(Data, 0x78, 10); } set { PKX.setColoStr(value, 10).CopyTo(Data, 0x78); OT2 = value; } }
|
||||
private string OT2 { get { return PKX.getColoStr(Data, 0x8C, 10); } set { PKX.setColoStr(value, 10).CopyTo(Data, 0x8C); } }
|
||||
public override ushort SID { get { return BigEndian.ToUInt16(Data, 0xA4); } set { BigEndian.GetBytes(value).CopyTo(Data, 0xA4); } }
|
||||
public override ushort TID { get { return BigEndian.ToUInt16(Data, 0xA6); } set { BigEndian.GetBytes(value).CopyTo(Data, 0xA6); } }
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@ namespace PKHeX
|
||||
{
|
||||
public sealed class SAV3XD : SaveFile
|
||||
{
|
||||
public override string BAKName => $"{FileName} [{Version} #{SaveCount.ToString("0000")}].bak";
|
||||
public override string BAKName => $"{FileName} [{OT} ({Version}) #{SaveCount.ToString("0000")}].bak";
|
||||
public override string Filter => "GameCube Save File|*.gci";
|
||||
public override string Extension => ".gci";
|
||||
|
||||
private const int SLOT_SIZE = 0x1E000;
|
||||
private const int SLOT_SIZE = 0x28000;
|
||||
private const int SLOT_START = 0x6000;
|
||||
private const int SLOT_COUNT = 3;
|
||||
private const int SLOT_COUNT = 2;
|
||||
|
||||
private readonly int SaveCount = -1;
|
||||
private readonly int SaveIndex = -1;
|
||||
@@ -23,7 +23,7 @@ public SAV3XD(byte[] data = null)
|
||||
BAK = (byte[])Data.Clone();
|
||||
Exportable = !Data.SequenceEqual(new byte[Data.Length]);
|
||||
|
||||
if (SaveUtil.getIsG3CXDSAV(Data) != GameVersion.XD)
|
||||
if (SaveUtil.getIsG3XDSAV(Data) != GameVersion.XD)
|
||||
return;
|
||||
|
||||
OriginalData = (byte[])Data.Clone();
|
||||
@@ -42,16 +42,44 @@ public SAV3XD(byte[] data = null)
|
||||
|
||||
// Decrypt most recent save slot
|
||||
{
|
||||
|
||||
byte[] slot = new byte[SLOT_SIZE];
|
||||
int slotOffset = SLOT_START + SaveIndex * SLOT_SIZE;
|
||||
Array.Copy(Data, slotOffset, slot, 0, slot.Length);
|
||||
|
||||
ushort[] keys = new ushort[4];
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
keys[i] = BigEndian.ToUInt16(slot, 8 + i * 2);
|
||||
|
||||
// Decrypt Slot
|
||||
Data = SaveUtil.DecryptGC(slot, 0x00010, 0x27FD8, keys);
|
||||
}
|
||||
|
||||
OFS_PouchHeldItem = 1224;
|
||||
OFS_PouchKeyItem = 1344;
|
||||
OFS_PouchBalls = 1516;
|
||||
OFS_PouchTMHM = 1580;
|
||||
OFS_PouchBerry = 1836;
|
||||
OFS_PouchCologne = 2020;
|
||||
OFS_PouchDisc = 2032;
|
||||
// Get Offset Info
|
||||
ushort[] subLength = new ushort[16];
|
||||
int[] subOffsets = new int[16];
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
subLength[i] = BigEndian.ToUInt16(Data, 0x20 + 2*i);
|
||||
subOffsets[i] = BigEndian.ToUInt16(Data, 0x40 + 4*i) | BigEndian.ToUInt16(Data, 0x40 + 4*i + 2) << 16;
|
||||
}
|
||||
// Offsets are displaced by the 0xA8 savedata region
|
||||
Trainer1 = subOffsets[1] + 0xA8;
|
||||
Party = Trainer1 + 0x30;
|
||||
Box = subOffsets[2] + 0xA8;
|
||||
Daycare = subOffsets[4] + 0xA8;
|
||||
// Memo = subOffsets[5] + 0xA8;
|
||||
// Shadow = subOffsets[7] + 0xA8;
|
||||
// Purifier = subOffsets[14] + 0xA8;
|
||||
|
||||
System.IO.File.WriteAllBytes("xd", Data);
|
||||
|
||||
OFS_PouchHeldItem = Trainer1 + 0x4C8;
|
||||
OFS_PouchKeyItem = Trainer1 + 0x540;
|
||||
OFS_PouchBalls = Trainer1 + 0x5EC;
|
||||
OFS_PouchTMHM = Trainer1 + 0x62C;
|
||||
OFS_PouchBerry = Trainer1 + 0x72C;
|
||||
OFS_PouchCologne = Trainer1 + 0x7E4;
|
||||
OFS_PouchDisc = Trainer1 + 0x7F0;
|
||||
|
||||
LegalItems = Legal.Pouch_Items_XD;
|
||||
LegalKeyItems = Legal.Pouch_Key_XD;
|
||||
@@ -74,7 +102,10 @@ public override byte[] Write(bool DSV)
|
||||
setChecksums();
|
||||
|
||||
// Get updated save slot data
|
||||
byte[] newSAV = null;
|
||||
ushort[] keys = new ushort[4];
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
keys[i] = BigEndian.ToUInt16(Data, 8 + i * 2);
|
||||
byte[] newSAV = SaveUtil.EncryptGC(Data, 0x10, 0x27FD8, keys);
|
||||
|
||||
// Put save slot back in original save data
|
||||
byte[] newFile = (byte[])OriginalData.Clone();
|
||||
@@ -93,7 +124,7 @@ public override byte[] Write(bool DSV)
|
||||
public override int MaxMoveID => 354;
|
||||
public override int MaxSpeciesID => 386;
|
||||
public override int MaxAbilityID => 77;
|
||||
public override int MaxItemID => 374;
|
||||
public override int MaxItemID => 593;
|
||||
public override int MaxBallID => 0xC;
|
||||
public override int MaxGameID => 5;
|
||||
|
||||
@@ -122,7 +153,14 @@ public override string ChecksumInfo
|
||||
}
|
||||
|
||||
// Trainer Info
|
||||
public override GameVersion Version { get { return GameVersion.RSBOX; } protected set { } }
|
||||
public override GameVersion Version { get { return GameVersion.XD; } protected set { } }
|
||||
public override string OT { get { return PKX.getColoStr(Data, Trainer1 + 0x00, 10); } set { PKX.setColoStr(value, 10).CopyTo(Data, Trainer1 + 0x00); } }
|
||||
public override ushort SID { get { return BigEndian.ToUInt16(Data, Trainer1 + 0x2C); } set { BigEndian.GetBytes(value).CopyTo(Data, Trainer1 + 0x2C); } }
|
||||
public override ushort TID { get { return BigEndian.ToUInt16(Data, Trainer1 + 0x2E); } set { BigEndian.GetBytes(value).CopyTo(Data, Trainer1 + 0x2E); } }
|
||||
|
||||
public override int Gender { get { return Data[Trainer1 + 0x8E0]; } set { Data[Trainer1 + 0x8E0] = (byte)value; } }
|
||||
public override uint Money { get { return BigEndian.ToUInt32(Data, Trainer1 + 0x8E4); } set { BigEndian.GetBytes(value).CopyTo(Data, Trainer1 + 0x8E4); } }
|
||||
public uint Coupons { get { return BigEndian.ToUInt32(Data, Trainer1 + 0x8E8); } set { BigEndian.GetBytes(value).CopyTo(Data, Trainer1 + 0x8E8); } }
|
||||
|
||||
// Storage
|
||||
public override int getPartyOffset(int slot)
|
||||
@@ -140,7 +178,7 @@ public override int getBoxWallpaper(int box)
|
||||
}
|
||||
public override string getBoxName(int box)
|
||||
{
|
||||
return PKX.getColoStr(Data, Box + 0x24A4*box, 8);
|
||||
return PKX.getColoStr(Data, Box + (30 * SIZE_STORED + 0x14)*box, 8);
|
||||
}
|
||||
public override void setBoxName(int box, string value)
|
||||
{
|
||||
@@ -183,5 +221,17 @@ public override InventoryPouch[] Inventory
|
||||
p.setPouchBigEndian(ref Data);
|
||||
}
|
||||
}
|
||||
|
||||
// Daycare Structure:
|
||||
// 0x00 -- Occupied
|
||||
// 0x01 -- Deposited Level
|
||||
// 0x02-0x03 -- unused?
|
||||
// 0x04-0x07 -- Initial EXP
|
||||
public override int getDaycareSlotOffset(int loc, int slot) { return Daycare + 8; }
|
||||
public override ulong? getDaycareRNGSeed(int loc) { return null; }
|
||||
public override uint? getDaycareEXP(int loc, int slot) { return null; }
|
||||
public override bool? getDaycareOccupied(int loc, int slot) { return null; }
|
||||
public override void setDaycareEXP(int loc, int slot, uint EXP) { }
|
||||
public override void setDaycareOccupied(int loc, int slot, bool occupied) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,15 +163,7 @@ public static byte[] DecryptPBRSaveData(byte[] input)
|
||||
val -= keys[i];
|
||||
BigEndian.GetBytes(val).CopyTo(output, ofs + i*2);
|
||||
}
|
||||
ushort[] oldKeys = (ushort[])keys.Clone();
|
||||
oldKeys[0] += 0x43;
|
||||
oldKeys[1] += 0x29;
|
||||
oldKeys[2] += 0x17;
|
||||
oldKeys[3] += 0x13;
|
||||
keys[0] = (ushort)((oldKeys[0] & 0xf) | ((oldKeys[1] << 4) & 0xf0) | ((oldKeys[2] << 8) & 0xf00) | ((oldKeys[3] << 12) & 0xf000));
|
||||
keys[1] = (ushort)(((oldKeys[0] >> 4) & 0xf) | (oldKeys[1] & 0xf0) | ((oldKeys[2] << 4) & 0xf00) | ((oldKeys[3] << 8) & 0xf000));
|
||||
keys[2] = (ushort)((oldKeys[2] & 0xf00) | ((oldKeys[1] & 0xf00) >> 4) | ((oldKeys[0] & 0xf00) >> 8) | ((oldKeys[3] << 4) & 0xf000));
|
||||
keys[3] = (ushort)(((oldKeys[0] >> 12) & 0xf) | ((oldKeys[1] >> 8) & 0xf0) | ((oldKeys[2] >> 4) & 0xf00) | (oldKeys[3] & 0xf000));
|
||||
keys = SaveUtil.AdvanceGCKeys(keys);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
@@ -196,15 +188,7 @@ private static byte[] EncryptPBRSaveData(byte[] input)
|
||||
val += keys[i];
|
||||
BigEndian.GetBytes(val).CopyTo(output, ofs + i * 2);
|
||||
}
|
||||
ushort[] oldKeys = (ushort[])keys.Clone();
|
||||
oldKeys[0] += 0x43;
|
||||
oldKeys[1] += 0x29;
|
||||
oldKeys[2] += 0x17;
|
||||
oldKeys[3] += 0x13;
|
||||
keys[0] = (ushort)((oldKeys[0] & 0xf) | ((oldKeys[1] << 4) & 0xf0) | ((oldKeys[2] << 8) & 0xf00) | ((oldKeys[3] << 12) & 0xf000));
|
||||
keys[1] = (ushort)(((oldKeys[0] >> 4) & 0xf) | (oldKeys[1] & 0xf0) | ((oldKeys[2] << 4) & 0xf00) | ((oldKeys[3] << 8) & 0xf000));
|
||||
keys[2] = (ushort)((oldKeys[2] & 0xf00) | ((oldKeys[1] & 0xf00) >> 4) | ((oldKeys[0] & 0xf00) >> 8) | ((oldKeys[3] << 4) & 0xf000));
|
||||
keys[3] = (ushort)(((oldKeys[0] >> 12) & 0xf) | ((oldKeys[1] >> 8) & 0xf0) | ((oldKeys[2] >> 4) & 0xf00) | (oldKeys[3] & 0xf000));
|
||||
keys = SaveUtil.AdvanceGCKeys(keys);
|
||||
}
|
||||
}
|
||||
return output;
|
||||
|
||||
@@ -83,8 +83,10 @@ public static int getSAVGeneration(byte[] data)
|
||||
return 2;
|
||||
if (getIsG3SAV(data) != GameVersion.Invalid)
|
||||
return 3;
|
||||
if (getIsG3CXDSAV(data) != GameVersion.Invalid)
|
||||
if (getIsG3COLOSAV(data) != GameVersion.Invalid)
|
||||
return (int)GameVersion.COLO;
|
||||
if (getIsG3XDSAV(data) != GameVersion.Invalid)
|
||||
return (int)GameVersion.XD;
|
||||
if (getIsG3BOXSAV(data) != GameVersion.Invalid)
|
||||
return (int)GameVersion.RSBOX;
|
||||
if (getIsG4SAV(data) != GameVersion.Invalid)
|
||||
@@ -261,28 +263,40 @@ public static GameVersion getIsG3BOXSAV(byte[] data)
|
||||
/// <summary>Determines the type of 3rd gen Colosseum</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
public static GameVersion getIsG3CXDSAV(byte[] data)
|
||||
public static GameVersion getIsG3COLOSAV(byte[] data)
|
||||
{
|
||||
if (!new[] { SIZE_G3COLO, SIZE_G3COLOGCI }.Contains(data.Length))
|
||||
return GameVersion.Invalid;
|
||||
|
||||
// Check the intro bytes for each save slot
|
||||
byte[] slotintroColo = {0x01, 0x01, 0x00, 0x00};
|
||||
byte[] slotintroXD = {0x01, 0x01, 0x01, 0x00};
|
||||
int offset = data.Length - SIZE_G3COLO;
|
||||
int coloct = 0;
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
var ident = data.Skip(0x6000 + offset + 0x1E000*i).Take(4).ToArray();
|
||||
if (ident.SequenceEqual(slotintroColo))
|
||||
{
|
||||
coloct++;
|
||||
continue;
|
||||
}
|
||||
if (!ident.SequenceEqual(slotintroColo))
|
||||
return GameVersion.Invalid;
|
||||
}
|
||||
return GameVersion.COLO;
|
||||
}
|
||||
/// <summary>Determines the type of 3rd gen XD</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
/// <returns>Version Identifier or Invalid if type cannot be determined.</returns>
|
||||
public static GameVersion getIsG3XDSAV(byte[] data)
|
||||
{
|
||||
if (!new[] { SIZE_G3XD, SIZE_G3XDGCI }.Contains(data.Length))
|
||||
return GameVersion.Invalid;
|
||||
|
||||
// Check the intro bytes for each save slot
|
||||
byte[] slotintroXD = { 0x01, 0x01, 0x01, 0x00 };
|
||||
int offset = data.Length - SIZE_G3XD;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
var ident = data.Skip(0x6000 + offset + 0x28000 * i).Take(4).ToArray();
|
||||
if (!ident.SequenceEqual(slotintroXD))
|
||||
return GameVersion.Invalid;
|
||||
}
|
||||
return coloct == 3 ? GameVersion.COLO : GameVersion.XD;
|
||||
return GameVersion.XD;
|
||||
}
|
||||
/// <summary>Determines the type of 4th gen save</summary>
|
||||
/// <param name="data">Save data of which to determine the type</param>
|
||||
@@ -760,5 +774,54 @@ public static int getG3VersionID(int CXDversion)
|
||||
default: return (int)GameVersion.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static byte[] DecryptGC(byte[] input, int start, int end, ushort[] keys)
|
||||
{
|
||||
byte[] output = (byte[])input.Clone();
|
||||
for (int ofs = start; ofs < end; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
ushort val = BigEndian.ToUInt16(input, ofs + i * 2);
|
||||
val -= keys[i];
|
||||
BigEndian.GetBytes(val).CopyTo(output, ofs + i * 2);
|
||||
}
|
||||
keys = AdvanceGCKeys(keys);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
internal static byte[] EncryptGC(byte[] input, int start, int end, ushort[] keys)
|
||||
{
|
||||
byte[] output = (byte[])input.Clone();
|
||||
for (int ofs = start; ofs < end; ofs += 8)
|
||||
{
|
||||
for (int i = 0; i < keys.Length; i++)
|
||||
{
|
||||
ushort val = BigEndian.ToUInt16(input, ofs + i * 2);
|
||||
val += keys[i];
|
||||
BigEndian.GetBytes(val).CopyTo(output, ofs + i * 2);
|
||||
}
|
||||
keys = AdvanceGCKeys(keys);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static ushort[] AdvanceGCKeys(ushort[] oldKeys)
|
||||
{
|
||||
oldKeys[0] += 0x43;
|
||||
oldKeys[1] += 0x29;
|
||||
oldKeys[2] += 0x17;
|
||||
oldKeys[3] += 0x13;
|
||||
|
||||
ushort[] keys = new ushort[4];
|
||||
|
||||
keys[0] = (ushort)(oldKeys[0] & 0xf | oldKeys[1] << 4 & 0xf0 | oldKeys[2] << 8 & 0xf00 | oldKeys[3] << 12 & 0xf000);
|
||||
keys[1] = (ushort)(oldKeys[0] >> 4 & 0xf | oldKeys[1] & 0xf0 | oldKeys[2] << 4 & 0xf00 | oldKeys[3] << 8 & 0xf000);
|
||||
keys[2] = (ushort)(oldKeys[2] & 0xf00 | (oldKeys[1] & 0xf00) >> 4 | (oldKeys[0] & 0xf00) >> 8 | oldKeys[3] << 4 & 0xf000);
|
||||
keys[3] = (ushort)(oldKeys[0] >> 12 & 0xf | oldKeys[1] >> 8 & 0xf0 | oldKeys[2] >> 4 & 0xf00 | oldKeys[3] & 0xf000);
|
||||
|
||||
return keys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user