summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormetaphysicsIO <103212704+metaphysicsIO@users.noreply.github.com>2023-04-26 23:31:39 -0500
committermetaphysicsIO <103212704+metaphysicsIO@users.noreply.github.com>2023-04-26 23:31:39 -0500
commit29d8535320ef698dbe455b2849ccb9542058b22a (patch)
tree7e4e848b642822bbdd3ce1756aa158f7880e222f
downloadPSE-29d8535320ef698dbe455b2849ccb9542058b22a.tar.gz
pushing this to git.
-rw-r--r--CHANGELOG.txt12
-rw-r--r--PrivateAccess.cpp31
-rw-r--r--README.txt64
-rwxr-xr-xSE.obin0 -> 45824 bytes
-rw-r--r--SavEdit.cpp157
-rw-r--r--SavEdit.h37
-rw-r--r--TextOptions.cpp68
-rw-r--r--UserOptions.cpp194
-rw-r--r--compile.sh1
-rw-r--r--main.cpp51
-rw-r--r--notes.txt30
11 files changed, 645 insertions, 0 deletions
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
new file mode 100644
index 0000000..c516ca3
--- /dev/null
+++ b/CHANGELOG.txt
@@ -0,0 +1,12 @@
+# April 26, 2023
+
+* Created the changelog file, knowing that I should have been using git.
+* Added checksum subroutine
+* Add text-table conversion
+* I changed vector "offset" to "mem". It made no sense to call it "offset" when
+ it was the set that is mem contains offsets.
+* Tested modMem subroutine (changed the first char in OT name to "Z".): It
+ worked.
+* Tested the save feature. It works.
+* Finally got the money edit to work.
+* Probably going to push this up in a few moments.
diff --git a/PrivateAccess.cpp b/PrivateAccess.cpp
new file mode 100644
index 0000000..36a90ed
--- /dev/null
+++ b/PrivateAccess.cpp
@@ -0,0 +1,31 @@
+#include <iostream>
+
+#include "SavEdit.h"
+
+SavEdit::SavEdit()
+{
+ /*
+ * init
+ */
+
+ filename = "Pokemon - Red Version (USA, Europe) (SGB Enhanced).sav";
+ lang = en_US;
+}
+
+std::string SavEdit::getFilename()
+{
+ /*
+ * Return the file name.
+ */
+
+ return filename;
+}
+
+void SavEdit::setFilename(std::string fname)
+{
+ /*
+ * Set the file name.
+ */
+
+ filename = fname;
+}
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..43090e0
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,64 @@
+# Pokemon Sav Editor (PSE)
+## Mionathraìm SAV
+
+This is a command-line, GNU/Linux supported Pokemon SAV editor.
+
+It currently only supports Pokemon Red and Blue.
+
+I was going to go with my normal naming theme, but thought "Mionathraìm SAV"
+might be too hard to type/pronounce.. So, it'll be "Pokemon Sav Editor (PSE)"
+and in the sub-title we'll call it "Mionathraìm SAV".
+
+## Why a pokemon sav editor?
+
+PKHeX doesn't work on Linux. This does.
+
+## Release
+
+I did compile it using `g++ (Gentoo 12.2.1_p20230304 p13) 12.2.1 20230304`
+so if that will work fine for you, go for it.
+
+
+## What about other generations?
+
+This is just a starting point. Feel free to help though.
+
+
+## What about [thing you want]
+
+Request it, give me the offset addr, or add it in yourself. This is just
+something I'm doing.
+
+
+## How to run
+
+```
+$ sh compile.sh
+$ ./SE.o [FILENAME].sav
+
+```
+
+## Contribute
+
+Make a PR.
+
+## Where are you?
+
+* I'm required to use 2FA apparently so if I'm not back up, that's why. I'll see
+about fixing that when I can.
+
+* I'm also working on two degrees right now. (Mathematics and Computer Science)
+
+* I'll try to be around when I can.
+
+## Goal
+
+The goal of this project is to basically create a PKHeX replacement for Linux.
+I want to go all the way up to modern generations, if possible.
+
+
+## Nice idea
+
+Language support for: ie_gle, ja_jp
+
+
diff --git a/SE.o b/SE.o
new file mode 100755
index 0000000..4db3c26
--- /dev/null
+++ b/SE.o
Binary files differ
diff --git a/SavEdit.cpp b/SavEdit.cpp
new file mode 100644
index 0000000..b8996ab
--- /dev/null
+++ b/SavEdit.cpp
@@ -0,0 +1,157 @@
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "SavEdit.h"
+
+#define SAV_LEN 32767
+
+
+void SavEdit::open()
+{
+ /*
+ * Store save file in a vector.
+ */
+
+ std::ifstream fs(filename, std::ios::in | std::ios::binary);
+
+ // Verify file is opened
+ if(!fs)
+ {
+ // TODO: translation + subroutine needed.
+ std::cout << "Cannot open file." << std::endl;
+ exit(1);
+ }
+
+ // Iterate through file, adding to vector.
+ for(int i = 0; i <= SAV_LEN; ++i)
+ mem.push_back(fs.get());
+}
+
+void SavEdit::read()
+{
+ /*
+ * Iterates through the opened file.
+ * This is mostly just to make sure it
+ * properly loaded everything up. I will
+ * probably be modifying this for editing
+ * later.
+ */
+
+ for(int i = 0; i <= SAV_LEN; ++i)
+ {
+ // offset address
+ std::cout.fill('0');
+ std::cout << "0x";
+ std::cout.width(4);
+ std::cout << std::hex << i;
+ std::cout << ":\t";
+
+ // print hex value
+ std::cout.width(2);
+ std::cout << std::hex << mem.at(i) << std::endl;
+ }
+
+}
+
+void SavEdit::save(std::string fname)
+{
+ /*
+ * This subroutine writes the binary
+ * file using the offset vector.
+ */
+
+ std::ofstream bfs(fname, std::ios::out | std::ios::binary);
+
+ if(!bfs)
+ {
+ // TODO: Translate + subroutine needed.
+ std::cout << "Cannot create file." << std::endl;
+ exit(2);
+ }
+
+ for(int i = 0; i <= SAV_LEN; ++i)
+ bfs.write( (char*)&mem.at(i), 1);
+
+}
+
+void SavEdit::backup()
+{
+ /*
+ * Create the backup of the sav file
+ * before editing.
+ */
+
+ save(getFilename() + ".BAK");
+}
+
+int SavEdit::checksum()
+{
+ /*
+ * Set the checksum for the new SAV
+ */
+
+ // Autodetect if JP later.
+ bool JP = false;
+
+ // The Japanese gen 1 has a different offset
+ int checksum_offset = JP ? 0x3594 : 0x3523;
+
+ // Starting point for Bank One
+ int bankStart = 0x2598;
+
+ // Our new checksum
+ unsigned int check_sum = 0;
+
+ // Take all the data from bank 1, but skip the checksum.
+ // (Non-JP: 0x2000 to 0x3522)
+ for(int i = bankStart; i < checksum_offset; ++i)
+ check_sum += mem.at(i);
+
+ // bitwise NOT
+ check_sum = ~check_sum;
+
+ // bitwise AND_EQUAL
+ check_sum &= 0xff;
+
+ // Update the checksum.
+ mem.at(checksum_offset) = check_sum;
+
+ return check_sum;
+}
+
+int SavEdit::convert(char given)
+{
+ /*
+ * Converts the given character to the proper value
+ * in the text table.
+ */
+
+ // The characters are 63 over their ASCII value.
+ int mod = 63;
+
+ mod += (int)given;
+
+ return mod;
+}
+
+void SavEdit::modMem(int offset, char val)
+{
+ /*
+ * Edits the specified offset to the given value,
+ * after converting the given value to it's proper
+ * format.
+ */
+
+ // note: should this subroutine contain checks?
+ mem.at(offset) = convert(val);
+}
+
+void SavEdit::saveNewFile()
+{
+ /*
+ * This updates the new file and saves it.
+ */
+
+ save(getFilename());
+}
diff --git a/SavEdit.h b/SavEdit.h
new file mode 100644
index 0000000..699cb81
--- /dev/null
+++ b/SavEdit.h
@@ -0,0 +1,37 @@
+#ifndef SAVEDIT_H
+#define SAVEDIT_H
+
+#include <iostream>
+#include <vector>
+
+class SavEdit
+{
+ public:
+ SavEdit();
+ void open();
+ void read();
+ void save(std::string fname);
+ std::string getFilename();
+ void setFilename(std::string fname);
+ void backup();
+ int checksum();
+ int convert(char given);
+ void modMem(int offset, char val);
+ void saveNewFile();
+ void setLang(int i);
+ void greeting();
+ void languageForm();
+ void textOT();
+ void modify(const int BEGIN, const int END);
+ void cashModify();
+ bool select();
+ void clear();
+
+ private:
+ std::string filename;
+ std::vector<int> mem;
+ enum language{en_US=0, ie_gle=1, ja_jp=2};
+ int lang;
+};
+
+#endif
diff --git a/TextOptions.cpp b/TextOptions.cpp
new file mode 100644
index 0000000..e1ce711
--- /dev/null
+++ b/TextOptions.cpp
@@ -0,0 +1,68 @@
+#include <iostream>
+
+#include "SavEdit.h"
+
+void SavEdit::setLang(int i)
+{
+ /*
+ * Set the language.
+ */
+ lang = i;
+}
+
+void SavEdit::greeting()
+{
+ /*
+ * Greet the user in their language of choice.
+ */
+ switch(lang)
+ {
+ case en_US:
+ std::cout << "Hello." << std::endl;
+ break;
+ case ie_gle:
+ std::cout << "dia duit." << std::endl;
+ break;
+ case ja_jp:
+ std::cout << "こんにちは" << std::endl;
+ break;
+ default:
+ std::cout << "Something went wrong." << std::endl;
+ break;
+ }
+}
+void SavEdit::languageForm()
+{
+ /*
+ * Allow the user to switch to their preferred language.
+ */
+ int selection = 0;
+
+ std::cout << "Language selection:\n";
+ std::cout << "\t0. American English.\n";
+ std::cout << "\t1. Gaelige\n";
+ std::cout << "\t2. 日本語\n";
+ std::cout << std::endl;
+
+ std::cout << "Language: ";
+ std::cin >> selection;
+
+ setLang(selection);
+
+}
+
+void SavEdit::textOT()
+{
+ /*
+ * Template subroutine to add language support.
+ */
+ switch(lang)
+ {
+ case en_US:
+ case ie_gle:
+ case ja_jp:
+ default:
+ std::cout << "OT: ";
+ break;
+ }
+}
diff --git a/UserOptions.cpp b/UserOptions.cpp
new file mode 100644
index 0000000..08768b8
--- /dev/null
+++ b/UserOptions.cpp
@@ -0,0 +1,194 @@
+#include <iostream>
+#include <cstring>
+
+#include "SavEdit.h"
+
+void SavEdit::clear()
+{
+ /*
+ * Weird screen clear trick I do not remember where I learned it.
+ */
+ std::cout << "\033[2J\033[1;1H";
+}
+
+void SavEdit::modify(const int BEGIN, const int END)
+{
+ /*
+ * Reusable code for editing sections goes here.
+ */
+
+ // Make sure the input stays within its expected limits.
+ bool correct = false;
+
+ // +1 to include self.
+ const int SIZE_LIMIT = (END - BEGIN) + 1;
+
+ // This is the user input string.
+ std::string s;
+
+ // Prevent the user from breaking limits.
+ while(!correct)
+ {
+ // TODO: Clear / Redraw screen
+ std::cout << "input: ";
+
+ std::cin >> s;
+
+ if((const int)s.length() <= SIZE_LIMIT)
+ correct = true;
+
+ // Clear the buffer.
+ std::cin.clear();
+ }
+
+ for(unsigned long int i = 0; i <= (unsigned long int)(END - BEGIN); ++i)
+ {
+ if(i < s.length())
+ {
+ // Apply one char at a time.
+ const char *c = s.substr(i, 1).c_str();
+ modMem( (BEGIN + i), c[0]);
+ }
+
+ // Some require 0x50 at the end (TRAINER + RIVAL NAME).
+ if(i == s.length())
+ if(BEGIN == 0x2598 || BEGIN == 0x25F6)
+ mem.at(BEGIN + i) = 0x50;
+ }
+
+}
+
+void SavEdit::cashModify()
+{
+ /*
+ * Money is handled differently.
+ */
+
+ // Check for correctness
+ bool correct = false;
+
+ // User input
+ std::string s;
+
+ while(!correct)
+ {
+ std::cout << "input (numbers only): " << std::endl;
+
+ std::cin >> s;
+
+ if(atoi(s.c_str()) <= 999999)
+ {
+ correct = true;
+ }
+
+ // clear the buffer.
+ std::cin.clear();
+ }
+
+ // Pad with zeros
+ s = std::string(6 - s.length(), '0') + s;
+
+ // Apply the values
+ for(int i = 0; i <= 2; ++i)
+ {
+ // Convert to char
+ const char *c = s.substr((i*2), 2).c_str();
+ // Convert to int (the char values are in the way.)
+ int x_val = ((c[0] * 10) + c[1]);
+ // Remove 528 because, for some reason, that's how much higher it is.
+ int x_tot = x_val - 528;
+
+ // BUGFIX: For some reason, we need to remove 1 extra for 1 and 2?
+ if(c[0] == 0)
+ if((c[1] == '1') || (c[1] == '2'))
+ x_tot--;
+
+ // apply
+ mem.at(0x25f3 + i) = x_tot;
+ }
+}
+
+bool SavEdit::select()
+{
+ /*
+ * A giant switch for determining which offsets to edit.
+ *
+ * TODO: May want to break this down into something else.
+ */
+
+ // For the loop. I might not keep it like this.
+ bool finished = false;
+
+ // Check for correct input.
+ bool correct = false;
+ int OPTIONS_LIMIT = 4;
+
+ // user input.
+ int selection;
+
+ // Check if supported.
+ while(!correct)
+ {
+ // TODO: translate + create subroutine
+ // TODO: Clear / Redraw Screen
+ // TODO: Create a better menu
+ std::cout << "EDIT: " << std::endl;
+ std::cout << "\t1. Player Name" << std::endl;
+ std::cout << "\t2. RIVAL Name" << std::endl;
+ std::cout << "\t3. MONEY" << std::endl;
+ std::cout << "\t4. QUIT, DO NOT SAVE" << std::endl;
+ std::cout << "\t0. SAVE AND QUIT" << std::endl;
+ std::cout << "YOUR INPUT: ";
+
+ std::cin >> selection;
+
+ if(selection >= 0)
+ if(selection <= OPTIONS_LIMIT)
+ correct = true;
+
+
+ // clear the buffer.
+ std::cin.clear();
+ }
+
+
+ // TODO: Allow for custom edits?
+
+ switch(selection)
+ {
+ case 0:
+ std::cout << "SAVING FILE..." << std::endl;
+ checksum();
+ saveNewFile();
+ finished = true;
+ break;
+ case 1:
+ std::cout << "EDITING TRAINER NAME:" << std::endl;
+ modify(0x2598, 0x259E);
+ break;
+ case 2:
+ std::cout << "EDITING RIVAL NAME:" << std::endl;
+ modify(0x25F6, 0x25FD);
+ break;
+ case 3:
+ std::cout << "EDITING MONEY:" << std::endl;
+ //std::cout << "SETTING TO 999,999 POKEDOLLARS." << std::endl;
+ //mem.at(0x25F3) = 99;
+ //mem.at(0x25F4) = 99;
+ //mem.at(0x25F5) = 99;
+ cashModify();
+ //modify(0x25F3, 0x25F5);
+ break;
+ case 4:
+ std::cout << "Quitting without saving." << std::endl;
+ finished = true;
+ break;
+ default:
+ std::cout << "NOT AN OPTION" << std::endl;
+ break;
+ }
+
+ // TODO: Display what is currently there
+
+ return finished;
+}
diff --git a/compile.sh b/compile.sh
new file mode 100644
index 0000000..9982efa
--- /dev/null
+++ b/compile.sh
@@ -0,0 +1 @@
+g++ -Wall main.cpp SavEdit.cpp PrivateAccess.cpp UserOptions.cpp TextOptions.cpp -o SE.o
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..eaca369
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,51 @@
+#include <iostream>
+
+#include "SavEdit.h"
+
+int main(int argc, char *argv[])
+{
+ // Create obj
+ SavEdit save;
+
+ if(argc == 2)
+ {
+ // Set the file name
+ save.setFilename(argv[1]);
+ }
+
+
+ // Select language.
+ //save.languageForm();
+
+ // Send greeting.
+ //save.greeting();
+
+ // store file in vector
+ save.open();
+
+ // print vector
+ //save.read();
+
+ // create the backup
+ save.backup();
+
+ // Test OT
+ //save.modify(0x2598, 0x259E);
+
+ // TEST OPTIONS
+ bool done = false;
+ while(!done)
+ done = save.select();
+
+
+ // Test the rival.
+ //save.modify(0x25F6, 0x25FC);
+
+ // Update the checksum.
+ //save.checksum();
+
+ // Save the new file
+ //save.saveNewFile();
+
+ return 0;
+}
diff --git a/notes.txt b/notes.txt
new file mode 100644
index 0000000..26d9ed9
--- /dev/null
+++ b/notes.txt
@@ -0,0 +1,30 @@
+Stuff to keep in mind.
+
+##
+# Random note
+##
+
+I have found three offset points require an ending with 0x50:
+0x259E, 0x25FD, 2C5E
+
+0x259E is the trainer, 0x25FD is the rival, but I am uncertain what 2C5E is at
+this time.
+
+
+##
+# Specific offset points
+##
+
+# TRAINER NAME:
+OFFSET: 0x2598 - 0x259E
+NOTE: It must end with 0x50
+
+# RIVAL NAME:
+OFFSET: 0x25F6 - 0x25FD
+NOTE: It must end with 0x50
+
+# MONEY:
+OFFSET: 0x25F3 - 0x25F5
+NOTE: Reads hex weird. Only accepts 0-9. Basically, do not convert with money.
+
+