Skip to content
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

Add a command-line (Linux) version, and Auto-resolution mode #1

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

sulix
Copy link

@sulix sulix commented Jun 30, 2021

I couldn't get PharaohResizer.exe running easily on a stock wine install under Linux, so have done this quick-and-dirty command-line Linux version. It works either with an extracted version of the EXE_RESOURCE from the windows PharaohResizer.exe, which it'll decompress and patch the resolution in, or can patch a DRM-unencumbered version of the original Cleopatra Pharaoh.exe (such as the version sold from GOG) from scratch.

It builds as a simple command-line program on Linux (and should be trivially compilable on basically anything else with LZ4 available), with the following command line options:

./PharaohResizer: Run Pharaoh+Cleopatra at different resolutions

Usage:
        ./PharaohResizer input [output] [width] [height]
        input: the Cleopatra 2.1 .exe file, or extracted EXE_RESOURCE from PharaohResizer
        [output]: The .exe file to write, defaults to PharaohNew.exe
        [width]: The width of the resolution to use, otherwise autodetected
        [height]: The height of the resolution to use, otherwise autodetected

It also adds an "Auto-resolution" mode where — if no resolution is specified when patching — some code is patched in to use the current desktop resolution. This is not very well tested, but works okay as a fallback on my machine.

I know you probably don't want to be stuck maintaining something hacky like this, so feel free to ignore it if you'd prefer, but I thought I'd throw it out here in case you or anyone else happen to be interested! Either way, thanks a lot for PharaohResizer: it's a phenomenal piece of patching!

The .NET version doesn't work properly for me under wine/mono, complaining
about some GDIPlus font function not being available.

Hack together a command-line program which does the same thing. The
EXE_RESOURCE needs to be extracted and passed as an arguement. This can
be done with wrestool on the windows/.NET PharaohResizer.exe file:

wrestool PharaohResizer.exe -x --type='EXECUTABLE' --name=101

The new PharaohResizer.c program will lz4 decompress this, and patch the
resolution in.
As extracting the EXE_RESOURCE is a bit of a pain, add support for using
an original (albeit stripped of any DRM wrappers) version of the
Pharaoh.exe from Cleopatra as a base, and apply the patches directly to
that.

If such an executable is not provided, the input is assumed to be the
extracted EXE_RESOURCE.

The various patches applied to PharaohResizer.exe's embedded version are
replicated here in the DoCrudeliosPatches() function: it's an impressive
bit of patching I admit to not fully understanding, so kudos!

This was tested against the GOG executable -- which is the only version
of Cleopatra I have -- and it produced an executable identical to the
embedded one save for GOG's patched website address.
If a resolution is not specified, instead patch in some code to
determine it at runtime from calls to GetSystemMetrics().

This seems to work fine on my machine, but probably needs testing on
others: it could be less reliable at some strange resolutions, or on
multi-monitor setups which are different to mine (i.e., wine with two
1440x900 monitors side-by-side).

This mode can be activated by simply not passing a width and height into
the program.

(I've also added a default output filename of PharaohNew.exe, so that
you only need to pass the path to Pharaoh.exe or the extracted
EXE_RESOURCE to get something which works. This should even -- in theory
-- allow windows users to drag-and-drop Pharaoh.exe onto this binary,
were it compiled for windows, and have a PharaohNew.exe appear.)
@crudelios
Copy link
Owner

This is really nice, thank you very much!

I'll have a look at the code eventually, though I'm not sure exacly when as right now I'm drowned in work and really tired.

I do like your changes though as they don't require a previously patched exe and they can be easily adapted for Windows as well.

@sulix
Copy link
Author

sulix commented Jul 1, 2021

No worries: things are getting pretty busy here at the moment, too, so I'm definitely not in any kind of hurry to see anything. :-)

I may push some more changes to this branch/PR if I do get time to do anything else — it doesn't seem like it'd be worth splitting anything into separate PRs at this point. But I can't think of anything likely to happen soon.

@crudelios
Copy link
Owner

I have to be honest, I totally forgot about this PR.

I'll have a look at it ASAP! 👍

@Vartaghan
Copy link

I am interested in this Linux version as I have the EXE file outputed by the resizer running on wine crashes after the Breakaway Game logo when I start the game.

Moreover, I use the French version of the game and I have seen on the now closed wsgf forum that a version of the resizer for the French version of the game had been made.

I am willing to help make a Linux FR version any way I can. Let me know if there is any way I can help.

@matteobin
Copy link

matteobin commented Mar 22, 2023

Impressive work you've done, @sulix!
May I ask you how you extracted crudelios' patches from the SML file?

By the way, what is a SML file? I tried to decompress it with lz4, but I
got a header error.

It would be nice if @crudelios commented his patches. I would be very
interested in reading his explanation.

@crudelios
Copy link
Owner

@matteobin I did this years ago, but basically the sml file was my own file format, it's a simple raw compression of the exe using the library provided with this tool.

When an exe is generated using the resizer, it simply decompresses the sml file and changes the bytes for the resolution.

So you can use a generated exe from pharaohresizer as a base for another resolution changer.

@Vartaghan The PR version of pharaoh is completely different than the English one. I did provide a modified French version, but it's an older patch with way fewer features.

Considering updating the patch to be on par with the English version requires an awful amount of hacking, I don't think it's worth doing it.

@sulix
Copy link
Author

sulix commented Mar 23, 2023

Yeah: "extracting" the patches involved doing a diff of a patched and unpatched executable, then sanity checking the changes against a disassembly. Alas, I never got around to working out the details of them any further than the comments in that function.

If I recall, the game had two ways of dealing with the resolution: some parts used a "resolution id" (1 == 640×480, 2 == 800×600, 3 == 1024×768), and then there were actual width and height variables. (In fact, there were a few copies of each of these).

Most of my personal reverse-engineering of Pharaoh was from the CD version, without the Cleopatra expansion, though, so I don't actually have a good label for what @crudelios's patches actually did. I imagine they mostly replaced code which looked up the resolution ID, and placed things in hardcoded locations with code which calculated a position based on the width and height.

Similarly, I don't actually have a French executable around (and, alas, probably wouldn't have the time to patch it properly even if I did), but I'd expect it'd involve:

  • Disassembling the game.
  • Finding the various resolution variables and functions. Try looking at the function calls between references to the strings "ERR:Trying default display mode" and "Old res=%d,%d; Color mode=%d". The code there reads the "desired resolution id" and calls the function which translates it to width and height. It also saves the "desired" id into the "actual" id. Looking up references to those variables should help you find the different places which need patching.
  • Wherever there's a reference to the "actual resolution id", replace it with code to calculate an equivalent value based on the width / height.

As you can tell, it's a fair bit of machine-code patching. And you're still left with issues like crashes on multiprocessor systems (upgrade the Miles DLLs and/or pin the process to one core), and Linux stack incompatibilities (though wine works around this).

@crudelios
Copy link
Owner

@sulix That's exactly what I did. The game checked for static resolution values and did everything statically, while I made everything dynamic. As you guessed, that's a lot of new code.

I did think about commenting my changes but never got around to it, and now, 7 years later, I don't really remember what I did lol.

Curiously, I found a bug with the empire map where the bottomost 16 or so pixels of the map wouldn't register mouse inputs. This is nearly irrelevant on the default resolutions, but the amount of unregistered pixels increases with the resolution. So at things like 1920x1080, the bottom third of the empire map was unclickable until I fixed the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants