-
Notifications
You must be signed in to change notification settings - Fork 1
Various changes #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
eb4x
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR would have been better if there were a bunch of smaller "atomic" commits, that fixed one issue at a time. That way we could go through the reasoning behind the changes, and I'd see the thought-process a bit clearer.
The C++17 std::filesystem is probably a better option than what we have today with aprinft. I'm not sure about the PATH_SEPARATOR, I think/ works just as well? (Maybe not if you're passing in \ from the commandline? i.e. game_path="C:\dosbox\pm3" and then appending /SAVES to that)
The functions reading the default game, club, player, meta and prefs look really interesting, but aren't getting used anywhere? (Update, I see they get used from PM3000)
Anyway, cool of you to check in. If you like the feedback and having your code reviewed, then you're welcome to submit PRs and I'll gladly take a look.
But if you have a vision for this code and what it can become (which it kinda seems like you do), you have my full blessing to just pursue PM3000. Which is looking great btw!
pm3/pm3.cc
Outdated
| char* append_trailing_slash(const char* path) { | ||
| char* appended_path = const_cast<char *>(path); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you taking in a const char *, and then casting it to a char *? const is there to prevent you from modifying/reassigning it. It's mostly a memo to yourself that "I do not intend to modify this."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch there. That's an oversight on my part. At some point the const made sense further up the stack, but where it finished up, it no longer did.
pm3/pm3.cc
Outdated
| appended_path[len] = PATH_SEPARATOR; | ||
| appended_path[len + 1] = '\0'; // Null-terminate the string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't see a malloc anywhere, so now you're writing beyond the buffer.
I think what you'd want to do something like,
size_t len = strlen(path);
if (path[len - 1] == PATH_SEPARATOR)
return strdup(path);
char new_path = malloc(len + 2);
strcpy(new_path, path);
new_path[len] = PATH_SEPARATOR;
new_path[len + 1] = '\0';
return new_path;But, now you have to track the returned value, and remember to free() it when it's not needed anymore. There are perhaps cleaner ways of doing this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very good point. I've cleaned this up along with your other comments by moving over to use std::filessystem. I think the result is pretty clean.
pm3/pm3.cc
Outdated
| void load_binaries(int game_nr, const char *saves_path) { | ||
| char* full_path = append_trailing_slash(saves_path); | ||
|
|
||
| size_t gamexa_len = asprintf(&gamexa, "%sGAME%.1dA", full_path != nullptr ? full_path : "", game_nr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You'll need a corresponding call to free() for every asprintf as it allocates memory.
pm3/pm3.cc
Outdated
| fread(&gamea, sizeof (struct gamea), 1, fga); | ||
| fclose(fga); | ||
|
|
||
| size_t gamexb_len = asprintf(&gamexb, "%sGAME%.1dB", full_path != nullptr ? full_path : "", game_nr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asprintf missing free
pm3/pm3.cc
Outdated
| fread(&gameb, sizeof (struct gameb), 1, fgb); | ||
| fclose(fgb); | ||
|
|
||
| size_t gamexc_len = asprintf(&gamexc, "%sGAME%.1dC", full_path != nullptr ? full_path : "", game_nr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asprintf missing free
pm3/pm3.cc
Outdated
| fwrite(&saves, sizeof (struct saves), 1, fgs); | ||
| fclose(fgs); | ||
|
|
||
| size_t prefs_len = asprintf(&prefsx, "%sPREFS", full_path != nullptr ? full_path : ""); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
asprintf missing free
main.cc
Outdated
| free(gamexa); | ||
| free(gamexb); | ||
| free(gamexc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No longer relevant here.
| #define STANDARD_SAVES_PATH "SAVES" | ||
| #define DELUXE_SAVES_PATH "saves" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm guessing this really boils down to DOS was case insensitive, and would be better handled with a function that looks up the contents of a directory and case-insensitive looks for a entry, then uses the first result.
char *case_insensitive_path(const char *path, const char *name) {
char *full_path = NULL;
DIR *dirp = opendir(path);
struct dirent *entry = NULL;
while ((entry = readdir(dirp)) != NULL) {
if (0 != strcasecmp(entry->d_name, name))
continue;
size_t res = asprintf(&full_path, "%s%c%s", path, PATH_SEPARATOR, entry->d_name);
break;
}
closedir(dirp);
return full_path;
}Maybe the C++17 std::filesystem can do something similar?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The filename case sensitivity doesn't seem to have a straightforward solution with modern libraries. As there are only two cases "SAVES" and "saves", I could change get_saves_folder to:
const char* get_saves_folder(const char *game_path) {
if (std::filesystem::exists(std::filesystem::path(game_path) / STANDARD_SAVES_PATH)) {
return STANDARD_SAVES_PATH;
} else if (std::filesystem::exists(std::filesystem::path(game_path) / DELUXE_SAVES_PATH)) {
return DELUXE_SAVES_PATH;
} else {
fprintf(stderr, "Saves folder not found\n");
exit(EXIT_FAILURE);
}}
}However, I'm using the get_pm3_game_type() function (which the current get_saves_folder relies on) in PM3000, so I'm tempted to leave it as is?
pm3/pm3.cc
Outdated
| gameb.club[new_club_idx].player_image = gameb.club[old_club_idx].player_image; | ||
|
|
||
| strncpy(gameb.club[new_club_idx].manager, gameb.club[old_club_idx].manager, 16); | ||
| strncpy(gameb.club[old_club_idx].manager, DEFAULT_MANAGER_NAME, 16); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By reading in the default values from game/player/club which you've already done, you can actually fix this to the original club manager instead of "J. Smith".
pm3/pm3.hh
Outdated
|
|
||
| #define HOME 0 | ||
| #define AWAY 1 | ||
| #define DEFAULT_MANAGER_NAME "J.Smith " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See other comment about J. Smith.
|
I've got a WIP splitting up your PR into individual commits at #4. So, I really like these changes. I'm not sure about moving the pm3 files up a directory, but I like the idea of splitting them out into a header and a library, and just keept them at the root. (It just looks a bit weird when you've got The detection of standard vs deluxe seemed to me to be an issue of case sensitive filesystem, so I just wrote the case-insensitive search for the directory. If there's more to it, let me know? I changed the I'll have a go at implementing the rest of the functions tomorrow and compiling it agains pm3k. Let me know what you think 😸 |
|
Your code reviews are massively appreciated. The whole point of this for me is to sharpen my C++ and learn about reverse engineering. PM3000 has been a lot of fun. If you want to try that out and give feedback, it would also be very welcome. Eventually, I'd love to do go deeper into the .exe file and make mods directly to the game code, instead of just manipulating the save games. I would also like to finish decoding the Looks like we were working on this at the same time. I'll add some comments above. |
|
Just a heads up that if you are going to look at pm3k and have already pulled it, I've just pushed a refactor, so you'll want to pull down those changes first. |
Obviously this is a big change, but it's mostly moving around what was already there so I can use it as part of another project (PM3000). I didn't want to mess with your code too much, so I've kept things in the C-style where I could, without leaning too much into modern C++ (although I did end up using some C++17).
Here's an overview:
pm3.hh/pm3.ccaggressionhas a disproportionate weight on a players abilities in the game. If you have a team where all players have anaggressionof 9, they are pretty much unbeatable despite the rest of their stats. I've added a function to level theaggressionof all players in all teams to 5.PREFSfile andSAVES.dirfile.directors_confidence_currentanddirectors_confidence_startbeing reversed.gamedata.dat,clubdata.datandplaydata.dat.I know it's a lot, and if you want to ignore this, no worries, I'll just use my fork for PM3000.
Also, my IDE reformatted the code. If that's a blocker, let me know and I'll restore the original formatting as best I can.