## Link to GitHub Issue or related Pull Request, if one exists
n/a
## Description of change
Adds a flat-C, headers-only library for writing DLL plugins with spice.
This is meant to be an alternative to writing applications over spice
API which doesn't suit some scenarios like writing custom
high-performance I/O modules.
All you have to do is:
1. Create a 32-bit or 64-bit DLL with `spice_sdk_entry_point` as a
DLLExport
1. Include `spicesdk.h`, and optionally `spicesdk_io.h` (see
`extras\sdk\include`)
1. `spice_sdk_entry_point` will be called when hooks get initialized.
Call `init` with valid parameters.
1. When `init` returns, `SPICE_SDK_V0` will be filled out with a series
of function pointers; your DLL can now start calling into it.
1. Compile your DLL and add it as a DLL hook (`-k`) in spice when
launching the game.
See `v0_cpp.cpp` for an example.
V0 implements the following functions:
```
log;
get_game_info;
get_avs_info;
get_button;
set_button;
get_analog;
set_analog;
get_light;
set_light;
set_touch;
clear_touch;
insert_card;
set_keypad;
```
V0 is focused on providing common utility functions needed in a lot of
DLL hooks (get AVS info, log messages to file), and implementing I/O.
In the future, we could have more interesting things like:
* scanning and modifying memory
* installing DLL hooks
* having direct hooks into I/O emulation modules
* getting full tape LEDs
* having a callback into DX9 `Present` call
* drawing an ImGui window
More detailed documentation is coming soon in form of a wiki page.
## Testing
See included sample DLL.
125 lines
2.9 KiB
C++
125 lines
2.9 KiB
C++
// included early to avoid warning
|
|
#include <winsock2.h>
|
|
|
|
#include "shutdown.h"
|
|
|
|
#include "api/controller.h"
|
|
#include "easrv/easrv.h"
|
|
#include "rawinput/rawinput.h"
|
|
#include "hooks/audio/audio.h"
|
|
#include "hooks/graphics/graphics.h"
|
|
#include "util/deferlog.h"
|
|
#include "util/logging.h"
|
|
|
|
#include "launcher.h"
|
|
#include "logger.h"
|
|
#include "nvapi/nvapi.h"
|
|
#include "sdk/sdk.h"
|
|
|
|
namespace launcher {
|
|
|
|
void stop_subsystems() {
|
|
// note that it is possible for stop_subsystems to be called multiple times
|
|
// (e.g., crashing, and then closing the window)
|
|
// therefore, subsystems need to be guarded against multiple unload attempts
|
|
log_info("launcher", "stopping subsystems");
|
|
|
|
sdk::fini_sdk_modules();
|
|
|
|
// reset monitor settings
|
|
reset_monitor_on_exit();
|
|
|
|
// before shutting down logger, dump any deferred log messages
|
|
deferredlogs::dump_to_logger();
|
|
|
|
// flush/stop logger
|
|
logger::stop();
|
|
|
|
// stop ea server
|
|
easrv_shutdown();
|
|
|
|
// free api sockets
|
|
if (API_CONTROLLER) {
|
|
API_CONTROLLER->free_socket();
|
|
}
|
|
|
|
// notify audio hook
|
|
hooks::audio::stop();
|
|
|
|
// stop raw input
|
|
if (RI_MGR) {
|
|
RI_MGR->stop();
|
|
}
|
|
|
|
// unload nvapi and free library (if loaded)
|
|
nvapi::unload();
|
|
}
|
|
|
|
void kill(UINT exit_code) {
|
|
|
|
// terminate
|
|
TerminateProcess(GetCurrentProcess(), exit_code);
|
|
}
|
|
|
|
void shutdown(UINT exit_code) {
|
|
|
|
// force exit after 1s
|
|
std::thread force_thread([exit_code] {
|
|
Sleep(1000);
|
|
log_info("launcher", "force shutdown");
|
|
kill(exit_code);
|
|
return nullptr;
|
|
});
|
|
force_thread.detach();
|
|
|
|
// stop all subsystems
|
|
stop_subsystems();
|
|
|
|
// terminate
|
|
kill(exit_code);
|
|
}
|
|
|
|
static void restart_spawn() {
|
|
|
|
// never do this twice
|
|
static bool already_done = false;
|
|
if (already_done) {
|
|
return;
|
|
} else {
|
|
already_done = true;
|
|
}
|
|
|
|
// start the process using the same args
|
|
if (LAUNCHER_ARGC > 0) {
|
|
|
|
// build cmd line
|
|
std::string cmd_line = "START \"\" ";
|
|
for (int i = 0; i < LAUNCHER_ARGC; i++)
|
|
cmd_line += " \"" + std::string(LAUNCHER_ARGV[i]) + "\"";
|
|
|
|
// run command
|
|
system(cmd_line.c_str());
|
|
}
|
|
}
|
|
|
|
void restart() {
|
|
|
|
// force restart after 1s
|
|
std::thread force_thread([] {
|
|
Sleep(1000);
|
|
log_info("launcher", "force restart");
|
|
restart_spawn();
|
|
launcher::kill(0);
|
|
return nullptr;
|
|
});
|
|
force_thread.detach();
|
|
|
|
// clean up before restart so resources can be reclaimed
|
|
stop_subsystems();
|
|
|
|
// spawn new and terminate this process
|
|
restart_spawn();
|
|
launcher::kill(0);
|
|
}
|
|
}
|