Skip to content

Commit

Permalink
First commit
Browse files Browse the repository at this point in the history
  • Loading branch information
RainingChain committed Oct 6, 2023
0 parents commit ec6a8f2
Show file tree
Hide file tree
Showing 27 changed files with 2,886 additions and 0 deletions.
402 changes: 402 additions & 0 deletions .gitignore

Large diffs are not rendered by default.

596 changes: 596 additions & 0 deletions LICENSE

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions PkCompletionist.Core/Command.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using PKHeX.Core;
using System.Runtime.InteropServices.JavaScript;
using System.Collections.Generic;

namespace PkCompletionist.Core;
partial class Command
{
public Command()
{
LastCommand = this;
}

public static Command? LastCommand = null;

List<string> Messages = new();
public SaveFile? savA = null;
public SaveFile? savB = null;

public void AddMessage(string msg)
{
Messages.Add(msg);
}

public void AddError(string msg)
{
AddMessage(msg);
}

public SaveFile? SetSavA(byte[] data)
{
savA = SaveUtil.GetVariantSAV(data);
if (savA == null)
Messages.Add("Error: Failed to parse the content of the save file.");
return savA;
}
public SaveFile? SetSavB(byte[] data)
{
savB = SaveUtil.GetVariantSAV(data);
if (savB == null)
Messages.Add("Error: Failed to parse the content of the save file.");
return savB;
}

[JSExport]
public static string[] GetMessages()
{
if (LastCommand == null)
return System.Array.Empty<string>();
return LastCommand.Messages.ToArray();
}

[JSExport]
public static byte[] GetSavA()
{
if (LastCommand?.savA == null)
return System.Array.Empty<byte>();
return LastCommand.savA.Write();
}

[JSExport]
public static byte[] GetSavB()
{
if (LastCommand?.savB == null)
return System.Array.Empty<byte>();
return LastCommand.savB.Write();
}

}
70 changes: 70 additions & 0 deletions PkCompletionist.Core/CompletionValidator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using PKHeX.Core;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.JavaScript;

namespace PkCompletionist.Core;

partial class CompletionValidator : Command
{
static public CompletionValidatorX? LastValidatorX = null;

[JSExport]
static public bool Execute(byte[] savData, bool living)
{
var validator = new CompletionValidator();
var savA = validator.SetSavA(savData);
if (savA == null)
return false;

var validatorX = validator.CreateValidatorX(savA, living);
CompletionValidator.LastValidatorX = validatorX;

validatorX.GenerateAll();

return true;
}

CompletionValidatorX CreateValidatorX(SaveFile savA, bool living)
{
if (savA is SAV1)
return new CompletionValidator1(this, (savA as SAV1)!, living);

if (savA is SAV2)
return new CompletionValidator2(this, (savA as SAV2)!, living);

if (savA is SAV3)
return new CompletionValidator3(this, (savA as SAV3)!, living);

return new CompletionValidatorX(this, savA, living);
}

[JSExport]
static public string[] GetLastObtainedStatus()
{
if (LastValidatorX == null)
return System.Array.Empty<string>();

string[] Res = new string[LastValidatorX.owned.Count * 3];

int i = 0;
foreach (var Info in LastValidatorX.owned)
{
Res[i] = Info.Key;
List<string> obtained = new();
List<string> notObtained = new();

foreach (var Info2 in Info.Value)
{
if (Info2.Value)
obtained.Add(Info2.Key);
else
notObtained.Add(Info2.Key);
}
Res[i + 1] = System.String.Join(',', obtained);
Res[i + 2] = System.String.Join(',', notObtained);
i += 3;
}
return Res;
}
}
63 changes: 63 additions & 0 deletions PkCompletionist.Core/CompletionValidatorX.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using PKHeX.Core;
using System.Collections.Generic;
using System.Linq;

namespace PkCompletionist.Core;
partial class CompletionValidatorX
{
protected SaveFile sav;
public Dictionary<string, Dictionary<string, bool>> owned = new ();
protected bool living = false;
protected List<int> unobtainableItems = new ();
Command command;

public CompletionValidatorX(Command command, SaveFile sav, bool living)
{
this.command = command;
this.sav = sav;
this.living = living;
}


public void AddMessage(string msg)
{
this.command.AddMessage(msg);
}

public virtual void GenerateAll()
{
Generate_pokemon();
Generate_item();
}

public virtual void Generate_pokemon()
{
var ow = new Dictionary<string, bool>();
owned["pokemon"] = ow;

if (this.living)
{
var pkms = sav.GetAllPKM();
for (ushort i = 1; i <= sav.MaxSpeciesID; i++)
ow[i.ToString()] = pkms.Any(pkm => pkm.Species == i);
}
else
{
for (ushort i = 1; i <= sav.MaxSpeciesID; i++)
ow[i.ToString()] = sav.GetCaught(i);
}
}

public virtual void Generate_item()
{
var ow = new Dictionary<string, bool>();
owned["item"] = ow;

for (ushort i = 1; i <= sav.MaxItemID; i++)
{
if (this.unobtainableItems.Contains(i))
continue;
ow[i.ToString()] = SavUtils.HasItem(sav, i);
}
}
}
108 changes: 108 additions & 0 deletions PkCompletionist.Core/EventSimulator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using PKHeX.Core;
using System.Linq;
using System.Runtime.InteropServices.JavaScript;

namespace PkCompletionist.Core;

enum PK_EVENT
{
Gen1_Mew, //Classic: Toys "R" Us Mew ᅠ https://projectpokemon.org/home/files/file/1791-classic-toys-r-us-mew/
Gen1_PikachuSurf, // manually created using info from bulbapedia
Gen2_Mew, //Celebi Present Campaign, Shiny Mew, https://projectpokemon.org/home/files/file/4073-pcny-distribution-machine-savefiles/
Gen2_Celebi, //Celebi Tour Spain 2001, Celebi https://projectpokemon.org/home/forums/topic/40507-gen-2-celebi-tour-celebi/
Gen2_GSBall,
Gen2_EggTicket,
Gen2_BlueskyMail,
Gen2_MirageMail,
}

partial class EventSimulator : Command
{
[JSExport]
public static bool Execute(string evtStr, byte[] savA)
{
var simul = new EventSimulator();
if (simul.SetSavA(savA) == null)
return false;

if (evtStr == "*")
simul.ExecAllEvents();
else
{
if (!System.Enum.TryParse<PK_EVENT>(evtStr, out var evt))
{
simul.AddError("Error: Invalid event name.");
return false;
}
var err = simul.ExecEvent(evt);
if (err != null)
{
simul.AddError(err);
return false;
}
}
return true;
}

void ExecAllEvents()
{
foreach (PK_EVENT evt in System.Enum.GetValues(typeof(PK_EVENT)))
{
var err = ExecEvent(evt);
if (err != null)
AddError(err);
}
}

string? ExecEvent(PK_EVENT evt)
{
if (savA?.Generation == 1)
{
if (evt == PK_EVENT.Gen1_Mew)
return SavUtils.AddPkm(savA, "Gen1_Mew.pk1");
if (evt == PK_EVENT.Gen1_PikachuSurf)
return SavUtils.AddPkm(savA, "Gen1_PikachuSurf.pk1");
}

if (savA?.Generation == 2)
{
if (evt == PK_EVENT.Gen2_Mew)
return SavUtils.AddPkm(savA, "Gen2_Mew.pk2");

if (evt == PK_EVENT.Gen2_Celebi)
return SavUtils.AddPkm(savA, "Gen2_Celebi.pk2");

if (evt == PK_EVENT.Gen2_GSBall)
{
SAV2? sav2 = savA as SAV2;
if (sav2 == null)
return "Error: The save file provided is not Generation 2.";

var allPkms = sav2.GetAllPKM();
var celebi = allPkms.Any(pk =>
{
return pk.Species == 251 && pk.TID16 == sav2.TID16 && pk.OT_Name == sav2.OT;
});

if (celebi || sav2.GetEventFlag(832) || sav2.GetEventFlag(190) || sav2.GetEventFlag(191) || sav2.GetEventFlag(192))
return "Error: You already obtained GS Ball.";

var err = SavUtils.AddItem(sav2, 115 /*GS Ball*/);
if (err != null)
return err;

sav2.SetEventFlag(832, true);
}

if (evt == PK_EVENT.Gen2_EggTicket)
return SavUtils.AddItem(savA, 129 /*Egg Ticket*/);

if (evt == PK_EVENT.Gen2_BlueskyMail)
return SavUtils.AddItem(savA, 187 /*Bluesky Mail*/);

if (evt == PK_EVENT.Gen2_MirageMail)
return SavUtils.AddItem(savA, 189 /*Mirage Mail*/);
}
return null;
}
}
74 changes: 74 additions & 0 deletions PkCompletionist.Core/FlagAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using PKHeX.Core;
using System.Collections.Generic;
using System.Runtime.InteropServices.JavaScript;

namespace PkCompletionist.Core;

partial class FlagAnalyzer : Command
{
int changeRangeStart;
int changeRangeEnd;

[JSExport]
public static bool Execute(byte[] savA, byte[] savB, int changeRangeStart, int changeRangeEnd)
{
var simul = new FlagAnalyzer(changeRangeStart, changeRangeEnd);
if (simul.SetSavA(savA) == null)
return false;
if (simul.SetSavB(savB) == null)
return false;
return simul.Execute();
}

public FlagAnalyzer(int changeRangeStart, int changeRangeEnd)
{
this.changeRangeStart = changeRangeStart;
this.changeRangeEnd = changeRangeEnd;
}

public bool Execute()
{
if(savA == null || savB == null)
return false;

byte[] savAData = savA.Data;
byte[] savBData = savB.Data;

if (savAData.Length != savBData.Length)
{
AddError("this.sav1.Data.Length != this.sav2.Data.Length");
return false;
}

// ignore play time, checksum, for US version only
List<int> toIgnore = new();
if(savA.Version == GameVersion.C)
toIgnore = new() { 0x1252, 0x1253, 0x1254, 0x1255, 0x1F0D, 0x1F0E, 0x2052, 0x2053, 0x2054, 0x2055, 0x2D0D, 0x2D0E };

List<int> diffIdx = new List<int>();
for (var i = 0; i < savAData.Length; i++)
{
if (savA.Version == GameVersion.C && toIgnore.IndexOf(i) != -1)
continue;

if (savAData[i] != savBData[i])
diffIdx.Add(i);
}
AddMessage($"Change count + 1: {diffIdx.Count + 1}");

for (var i = changeRangeStart; i < changeRangeEnd; i++)
{
if (i >= diffIdx.Count)
continue;

int idx = diffIdx[i];

if (changeRangeEnd - changeRangeStart < 15)
AddMessage($"this.sav1.Data[0x{idx:X}] = 0x{savBData[idx]:X} (old=0x{savAData[idx]:X})");

savAData[idx] = savBData[idx];
}
this.savA.SetData(savAData, 0);
return true;
}
}
Loading

0 comments on commit ec6a8f2

Please sign in to comment.