julianxhokaxhiu / FF78Launcher

An alternative launcher for FF7/FF8 Steam editions
GNU General Public License v3.0
3 stars 0 forks source link

FF78Launcher can only starts the game with FFNx #1

Closed myst6re closed 3 weeks ago

myst6re commented 1 month ago

When FF78Launcher is used with the unmodified game, a message box appear that ask to use the launcher before starting the game.

For FF8, this message is triggered if some semaphores and the shared memory file do not exist. Those are created by the original Launcher before running the game.

But creating these handles is not enough: you also need to communicate with the game using those semaphores and the shared memory.

Minimum code to launch the Chocobo World

// choco_sharedMemoryWithLauncher for CW or ff8_sharedMemoryWithLauncher for FF8
HANDLE mapping = CreateFileMappingA(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 0x20000, "choco_sharedMemoryWithLauncher");
LPVOID view_of_file = MapViewOfFile(mapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);

HANDLE game_can_read_msg = CreateSemaphoreA(nullptr, 0, 1, "choco_gameCanReadMsgSem");
HANDLE game_did_read_msg = CreateSemaphoreA(nullptr, 0, 1, "choco_gameDidReadMsgSem");
HANDLE launcher_can_read_msg = CreateSemaphoreA(nullptr, 0, 1, "choco_launcherCanReadMsgSem");
HANDLE launcher_did_read_msg = CreateSemaphoreA(nullptr, 0, 1, "choco_launcherDidReadMsgSem");

// Everything above is enough to remove the MessageBox, but not enough to show the game Window

// Run the game: CreateProcess(chocobo.exe)

// Wait for the game ready state
WaitForSingleObject(launcher_can_read_msg, 5000);
// You can check the memory here and see if the game sent GAME_READY: *(uint32_t *)view_of_file
ReleaseSemaphore(launcher_did_read_msg, 1, nullptr);

uint32_t *launcher_memory_part = (uint32_t *)((uint8_t *)view_of_file + 0x10000);
const USER_SAVE_DIR = 9;
const END_USER_INFO = 24;

std::wstring user_save_dir = "mydocuments\\Square Enix\\FINAL FANTASY VIII Steam\\user_99999999999";

*launcher_memory_part = USER_SAVE_DIR; // Set next instruction: send save directory
*(launcher_memory_part + 1) = uint32_t(user_save_dir.size());
memcpy((void *)(launcher_memory_part + 2), user_save_dir.data(), user_save_dir.size() * 2);

// Wait for the game
ReleaseSemaphore(game_can_read_msg, 1, nullptr);
WaitForSingleObject(game_did_read_msg, 5000);

*launcher_memory_part = END_USER_INFO;

// Wait for the game (optional)
ReleaseSemaphore(game_can_read_msg, 1, nullptr);
WaitForSingleObject(game_did_read_msg, 5000);

// Wait for chocobo.exe to stop
// TODO: maybe it is needed to wait for CLOSING or something to end the process

Known messages (FF8)

Known messages (FF7 e-store)

List of messages sent by the launcher when starting the game instance

(Note that the game or the chocobo world starts as soon as the END_USER_INFO is received, but if you miss to send some data, crashes may happen. For the Chocobo World, only USER_SAVE_DIR is actually required)

  1. LOCALE_DATA_DIR
  2. INGAME_TEXTS
  3. CONFIG_KEYS
  4. USER_SAVE_DIR
  5. FF8_DOC_DIR
  6. FF8_INSTALL_DIR
  7. USER_ACHIEVEMENT (several times)
  8. ACHIEVEMENT_DEFINITIONS
  9. GAME_VERSION
  10. DISABLE_CLOUD
  11. BG_PAUSE_ENABLED
  12. END_USER_INFO

About concurrency

The current implementation of the actual FF8_Launcher is a bit more complicated, there is one thread to consume game message and one thread to send messages to the game. The main process push messages through a non-blocking queue, consumed by the thread that send messages to the game.

I choose to not care about this on my implementation, because I assume we don't need concurrency for this task. Eventually we will wait for the game process anyway.

julianxhokaxhiu commented 1 month ago

Thanks a lot for this info. I'll try to make use of it to provide a launcher that works with both the native driver as well as FFNx.

julianxhokaxhiu commented 1 month ago

Ok good news and bad news, I think we need to reverse a bit more the process but we're almost there! With this commit https://github.com/julianxhokaxhiu/FF78Launcher/commit/a2eaac88fb479a87eeaa2fa1de725de15f7c882c I am able to launch the game, however it remains in a frozen state. Any idea? image

julianxhokaxhiu commented 1 month ago

Some more updates: for FF7 I'm able to almost launch the game, however I think we miss the CONFIG_KEYS part which probably tells the driver the resolution and so on how to run.

On top of your list, this is what I am currently sending now:

LOCALE_DATA_DIR - lang-en
USER_SAVE_DIR - C:\Users\USERNAME\Documents\Square Enix\FINAL FANTASY VII Steam\user_XXXXYYYY
DOC_DIR - C:\Users\USERNAME\Documents\Square Enix\FINAL FANTASY VII Steam\
INSTALL_DIR - C:\Program Files (x86)\Steam\steamapps\common\FINAL FANTASY VII
END_USER_INFO
myst6re commented 1 month ago

Maybe the game needs more to work yes. Without sending complex messages, maybe you can try to add:

I looked at what the Launcher seems to answer, and for the main game:

julianxhokaxhiu commented 4 weeks ago

Thanks a lot for coming back. So I added the new opcodes as you mentioned here https://github.com/julianxhokaxhiu/FF78Launcher/commit/cef1da20d592829c6d502e1e42f5c8478b11ba92 however the behavior is still the same.

What I noticed though is that the game (FF7 in this case) for some unknown reason is not reading this configuration despite me sending the data. App.log and cache folder are being created in the game dir instead of the Documents path. This might be the culprit of why it's not starting as expected probably. Any idea what am I doing wrong?

julianxhokaxhiu commented 3 weeks ago

Finally some good news :) We are able to launch the games!

FF7: image

FF8: image