mirror of
https://github.com/fallout2-ce/fallout2-ce.git
synced 2026-07-27 16:47:11 -07:00
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1bbbc98065 | ||
|
|
b0b08ed14e | ||
|
|
5b8281922d | ||
|
|
cd123b1ad3 | ||
|
|
49affe4e80 | ||
|
|
07d1649b63 |
@@ -320,9 +320,7 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86
|
||||
generator-platform: Win32
|
||||
- arch: x64
|
||||
generator-platform: x64
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
@@ -337,18 +335,20 @@ jobs:
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: out
|
||||
key: windows-${{ matrix.arch }}-cmake-v102
|
||||
key: windows-${{ matrix.arch }}-cmake-v103
|
||||
|
||||
- name: Configure
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
|
||||
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products Microsoft.VisualStudio.Product.BuildTools -requires Microsoft.VisualStudio.Component.VC.v141.x86.x64 Microsoft.VisualStudio.Component.VC.v141.xp -property installationPath`) do set "VS_PATH=%%i"
|
||||
call "%VS_PATH%\Common7\Tools\VsDevCmd.bat" ^
|
||||
&& cmake --preset windows-${{ matrix.arch }}
|
||||
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
|
||||
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products Microsoft.VisualStudio.Product.BuildTools -requires Microsoft.VisualStudio.Component.VC.v141.x86.x64 Microsoft.VisualStudio.Component.VC.v141.xp -property installationPath`) do set "VS_PATH=%%i"
|
||||
call "%VS_PATH%\Common7\Tools\VsDevCmd.bat" ^
|
||||
&& cmake --build --preset windows-${{ matrix.arch }}-release
|
||||
|
||||
- name: Change executable file name
|
||||
|
||||
@@ -250,40 +250,40 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86
|
||||
generator-platform: Win32
|
||||
- arch: x64
|
||||
generator-platform: x64
|
||||
|
||||
steps:
|
||||
- name: Clone
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Visual Studio 2019 Build Tools with v141_xp
|
||||
- name: Install Visual Studio 2022 Build Tools with v141_xp
|
||||
shell: cmd
|
||||
run: |
|
||||
choco install visualstudio2019buildtools -y --package-parameters="'--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.v141.x86.x64 --add Microsoft.VisualStudio.Component.VC.v141.xp --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.10240 --add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.WinXP --add Microsoft.VisualStudio.Component.WinXP'"
|
||||
choco install visualstudio2022buildtools -y --package-parameters="'--add Microsoft.VisualStudio.Workload.VCTools --add Microsoft.VisualStudio.Component.VC.v141.x86.x64 --add Microsoft.VisualStudio.Component.VC.v141.xp --add Microsoft.VisualStudio.Component.VC.Tools.x86.x64 --add Microsoft.VisualStudio.Component.Windows10SDK.10240 --add Microsoft.VisualStudio.ComponentGroup.NativeDesktop.WinXP --add Microsoft.VisualStudio.Component.WinXP'"
|
||||
|
||||
- name: Cache cmake build
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: build
|
||||
key: windows-${{ matrix.arch }}-cmake-v1
|
||||
path: out
|
||||
key: windows-${{ matrix.arch }}-cmake-v2
|
||||
|
||||
- name: Configure
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
|
||||
&& cmake -B build -G "Visual Studio 16 2019" -A ${{ matrix.generator-platform }}
|
||||
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products Microsoft.VisualStudio.Product.BuildTools -requires Microsoft.VisualStudio.Component.VC.v141.x86.x64 Microsoft.VisualStudio.Component.VC.v141.xp -property installationPath`) do set "VS_PATH=%%i"
|
||||
call "%VS_PATH%\Common7\Tools\VsDevCmd.bat" ^
|
||||
&& cmake --preset windows-${{ matrix.arch }}
|
||||
|
||||
- name: Build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\Common7\Tools\VsDevCmd.bat" ^
|
||||
&& cmake --build build --config RelWithDebInfo
|
||||
for /f "usebackq tokens=*" %%i in (`"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products Microsoft.VisualStudio.Product.BuildTools -requires Microsoft.VisualStudio.Component.VC.v141.x86.x64 Microsoft.VisualStudio.Component.VC.v141.xp -property installationPath`) do set "VS_PATH=%%i"
|
||||
call "%VS_PATH%\Common7\Tools\VsDevCmd.bat" ^
|
||||
&& cmake --build --preset windows-${{ matrix.arch }}-release
|
||||
|
||||
- name: Upload
|
||||
run: |
|
||||
cd build/RelWithDebInfo
|
||||
cd out/build/windows-${{ matrix.arch }}/RelWithDebInfo
|
||||
7z a fallout2-ce-windows-${{ matrix.arch }}.zip fallout2-ce.exe
|
||||
gh release upload ${{ github.event.release.tag_name }} fallout2-ce-windows-${{ matrix.arch }}.zip
|
||||
rm fallout2-ce-windows-${{ matrix.arch }}.zip
|
||||
|
||||
@@ -8,6 +8,11 @@ For current sfall compatibility status and the remaining work needed to close ga
|
||||
|
||||
The project uses CMake, so building mostly comes down to picking the right preset.
|
||||
|
||||
## Commandline arguments
|
||||
|
||||
- `--scan-unimplemented` - do an analysis of opcodes and hooks used by loaded mods
|
||||
- `--dev-load-game=1` - load the given save game automatically (useful for LLM automation)
|
||||
|
||||
### macOS
|
||||
|
||||
- Install the Command Line Tools if needed: `xcode-select --install`
|
||||
|
||||
+66
-22
@@ -14,6 +14,8 @@
|
||||
namespace fallout {
|
||||
|
||||
static void fileCopy(const char* existingFilePath, const char* newFilePath);
|
||||
static bool copyGzToFile(gzFile inStream, FILE* outStream);
|
||||
static bool copyFileToGz(FILE* inStream, gzFile outStream);
|
||||
|
||||
// 0x452740
|
||||
int fileCopyDecompressed(const char* existingFilePath, const char* newFilePath)
|
||||
@@ -33,13 +35,10 @@ int fileCopyDecompressed(const char* existingFilePath, const char* newFilePath)
|
||||
FILE* outStream = compat_fopen(newFilePath, "wb");
|
||||
|
||||
if (inStream != nullptr && outStream != nullptr) {
|
||||
for (;;) {
|
||||
int ch = gzgetc(inStream);
|
||||
if (ch == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
fputc(ch, outStream);
|
||||
if (!copyGzToFile(inStream, outStream)) {
|
||||
gzclose(inStream);
|
||||
fclose(outStream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gzclose(inStream);
|
||||
@@ -87,14 +86,10 @@ int fileCopyCompressed(const char* existingFilePath, const char* newFilePath)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Copy byte-by-byte.
|
||||
for (;;) {
|
||||
int ch = fgetc(inStream);
|
||||
if (ch == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
gzputc(outStream, ch);
|
||||
if (!copyFileToGz(inStream, outStream)) {
|
||||
fclose(inStream);
|
||||
gzclose(outStream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
fclose(inStream);
|
||||
@@ -130,13 +125,10 @@ int _gzdecompress_file(const char* existingFilePath, const char* newFilePath)
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int ch = gzgetc(gzstream);
|
||||
if (ch == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
fputc(ch, stream);
|
||||
if (!copyGzToFile(gzstream, stream)) {
|
||||
gzclose(gzstream);
|
||||
fclose(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gzclose(gzstream);
|
||||
@@ -180,4 +172,56 @@ static void fileCopy(const char* existingFilePath, const char* newFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
static bool copyGzToFile(gzFile inStream, FILE* outStream)
|
||||
{
|
||||
std::vector<unsigned char> buffer(0xFFFF);
|
||||
|
||||
for (;;) {
|
||||
int bytesRead = gzread(inStream, buffer.data(), buffer.size());
|
||||
if (bytesRead < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bytesRead == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
size_t remaining = bytesRead;
|
||||
while (remaining > 0) {
|
||||
size_t bytesWritten = fwrite(buffer.data() + offset, sizeof(*buffer.data()), remaining, outStream);
|
||||
if (bytesWritten == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset += bytesWritten;
|
||||
remaining -= bytesWritten;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool copyFileToGz(FILE* inStream, gzFile outStream)
|
||||
{
|
||||
std::vector<unsigned char> buffer(0xFFFF);
|
||||
|
||||
for (;;) {
|
||||
size_t bytesRead = fread(buffer.data(), sizeof(*buffer.data()), buffer.size(), inStream);
|
||||
if (bytesRead == 0) {
|
||||
return feof(inStream) != 0;
|
||||
}
|
||||
|
||||
size_t offset = 0;
|
||||
size_t remaining = bytesRead;
|
||||
while (remaining > 0) {
|
||||
int bytesWritten = gzwrite(outStream, buffer.data() + offset, remaining);
|
||||
if (bytesWritten <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
offset += bytesWritten;
|
||||
remaining -= bytesWritten;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
+46
-8
@@ -48,6 +48,8 @@ static void buildNormalizedQwertyKeys();
|
||||
static void _GNW95_process_key(KeyboardData* data);
|
||||
static int inputGetHookMouseButton(int sdlButton);
|
||||
static void inputHandleMouseClickHook(int sdlButton, bool pressed);
|
||||
static void inputResetInternalEventQueue();
|
||||
static void inputHandleProgramActivationChange(bool isActive);
|
||||
|
||||
// 0x51E23C
|
||||
static int gKeyboardKeyRepeatRate = 80;
|
||||
@@ -135,6 +137,39 @@ static void inputHandleMouseClickHook(int sdlButton, bool pressed)
|
||||
ScriptHookCall(HOOK_MOUSECLICK, 0, { pressed ? 1 : 0, hookButton }).call();
|
||||
}
|
||||
|
||||
static void inputHandleProgramActivationChange(bool isActive)
|
||||
{
|
||||
if (gProgramIsActive == isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
gProgramIsActive = isActive;
|
||||
|
||||
// Cmd-tab on macOS can leave modifier/key repeat state and mouse mode out
|
||||
// of sync with SDL's actual app activation state. Drop queued input on
|
||||
// both transitions and reapply the desired mouse mode on return.
|
||||
keyboardReset();
|
||||
mouseReset();
|
||||
windowResetButtonState();
|
||||
inputResetInternalEventQueue();
|
||||
|
||||
if (isActive) {
|
||||
mouseDeviceInitMode();
|
||||
windowRefreshAll(&_scr_size);
|
||||
audioEngineResume();
|
||||
} else {
|
||||
audioEnginePause();
|
||||
}
|
||||
}
|
||||
|
||||
static void inputResetInternalEventQueue()
|
||||
{
|
||||
gInputEventQueueReadIndex = -1;
|
||||
gInputEventQueueWriteIndex = 0;
|
||||
_input_mx = -1;
|
||||
_input_my = -1;
|
||||
}
|
||||
|
||||
// 0x4C8A70
|
||||
int inputInit()
|
||||
{
|
||||
@@ -300,8 +335,7 @@ static int dequeueInputEvent()
|
||||
// 0x4C8D04
|
||||
void inputEventQueueReset()
|
||||
{
|
||||
gInputEventQueueReadIndex = -1;
|
||||
gInputEventQueueWriteIndex = 0;
|
||||
inputResetInternalEventQueue();
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) { } // Clear all input events
|
||||
}
|
||||
@@ -1016,8 +1050,9 @@ void _GNW95_process_message()
|
||||
if (!keyboardIsDisabled()) {
|
||||
keyboardData.key = e.key.keysym.scancode;
|
||||
keyboardData.down = (e.key.state & SDL_PRESSED) != 0;
|
||||
bool syntheticSfallKey = sfall_kb_consume_synthetic_key_event(keyboardData.key, keyboardData.down);
|
||||
|
||||
if (!e.key.repeat) {
|
||||
if (!e.key.repeat && !syntheticSfallKey) {
|
||||
int keyOverride = sfall_kb_handle_key_pressed(keyboardData.key, keyboardData.down);
|
||||
if (keyOverride != SDL_SCANCODE_UNKNOWN) {
|
||||
keyboardData.key = keyOverride;
|
||||
@@ -1034,14 +1069,17 @@ void _GNW95_process_message()
|
||||
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
||||
handleWindowSizeChanged();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
gProgramIsActive = true;
|
||||
case SDL_WINDOWEVENT_SHOWN:
|
||||
case SDL_WINDOWEVENT_RESTORED:
|
||||
windowRefreshAll(&_scr_size);
|
||||
audioEngineResume();
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
inputHandleProgramActivationChange(true);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_HIDDEN:
|
||||
case SDL_WINDOWEVENT_MINIMIZED:
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
gProgramIsActive = false;
|
||||
audioEnginePause();
|
||||
inputHandleProgramActivationChange(false);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
+53
-11
@@ -197,6 +197,7 @@ static const int gLoadSaveFrmIds[LOAD_SAVE_FRM_COUNT] = {
|
||||
const int saveLoadPages = 10;
|
||||
constexpr int slotsPerPage = 10;
|
||||
const int saveLoadTotalSlots = saveLoadPages * slotsPerPage;
|
||||
constexpr int kLoadSaveActionDone = 500;
|
||||
|
||||
// Global variable to track the current slot page
|
||||
static int _currentSlotPage = 0;
|
||||
@@ -204,6 +205,8 @@ static int _currentSlotPage = 0;
|
||||
// 0x5193B8
|
||||
static int _slot_cursor = 0;
|
||||
|
||||
static int gDevLoadGameSlot = -1;
|
||||
|
||||
// 0x5193BC
|
||||
static bool _quick_done = false;
|
||||
|
||||
@@ -645,7 +648,7 @@ int lsgSaveGame(int mode)
|
||||
|
||||
_slot_cursor = clickedSlot;
|
||||
if (clickedSlot == doubleClickSlot) {
|
||||
keyCode = 500;
|
||||
keyCode = kLoadSaveActionDone;
|
||||
soundPlayFile("ib1p1xx1");
|
||||
}
|
||||
|
||||
@@ -672,12 +675,12 @@ int lsgSaveGame(int mode)
|
||||
brightnessDecrease();
|
||||
break;
|
||||
case KEY_RETURN:
|
||||
keyCode = 500;
|
||||
keyCode = kLoadSaveActionDone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == 500) {
|
||||
if (keyCode == kLoadSaveActionDone) {
|
||||
if (_LSstatus[_slot_cursor] == SLOT_STATE_OCCUPIED) {
|
||||
rc = 1;
|
||||
// Save game already exists, overwrite?
|
||||
@@ -1079,6 +1082,12 @@ int lsgLoadGame(int mode)
|
||||
assert(false && "Should be unreachable");
|
||||
}
|
||||
|
||||
int devAutoloadSlot = -1;
|
||||
if (mode == LOAD_SAVE_MODE_FROM_MAIN_MENU && gDevLoadGameSlot != -1) {
|
||||
devAutoloadSlot = gDevLoadGameSlot;
|
||||
gDevLoadGameSlot = -1;
|
||||
}
|
||||
|
||||
touch_set_touchscreen_mode(windowType == LOAD_SAVE_WINDOW_TYPE_LOAD_GAME || windowType == LOAD_SAVE_WINDOW_TYPE_LOAD_GAME_FROM_MAIN_MENU);
|
||||
|
||||
if (lsgWindowInit(windowType) == -1) {
|
||||
@@ -1105,6 +1114,22 @@ int lsgLoadGame(int mode)
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool devAutoloadPending = false;
|
||||
|
||||
if (devAutoloadSlot != -1) {
|
||||
if (devAutoloadSlot >= 0 && devAutoloadSlot < saveLoadTotalSlots) {
|
||||
_slot_cursor = devAutoloadSlot;
|
||||
_currentSlotPage = devAutoloadSlot / slotsPerPage;
|
||||
if (_LSstatus[_slot_cursor] == SLOT_STATE_OCCUPIED) {
|
||||
devAutoloadPending = true;
|
||||
} else {
|
||||
debugPrint("LOADSAVE: dev load slot %d is not occupied\n", _slot_cursor + 1);
|
||||
}
|
||||
} else {
|
||||
debugPrint("LOADSAVE: invalid dev load slot %d\n", devAutoloadSlot + 1);
|
||||
}
|
||||
}
|
||||
|
||||
switch (_LSstatus[_slot_cursor]) {
|
||||
case SLOT_STATE_EMPTY:
|
||||
case SLOT_STATE_ERROR:
|
||||
@@ -1138,12 +1163,14 @@ int lsgLoadGame(int mode)
|
||||
_dbleclkcntr = 24;
|
||||
|
||||
int rc = -1;
|
||||
|
||||
int doubleClickSlot = -1;
|
||||
while (rc == -1) {
|
||||
sharedFpsLimiter.mark();
|
||||
|
||||
unsigned int time = getTicks();
|
||||
int keyCode = inputGetInput();
|
||||
int keyCode = devAutoloadPending ? kLoadSaveActionDone : inputGetInput();
|
||||
devAutoloadPending = false;
|
||||
bool selectionChanged = false;
|
||||
int scrollDirection = LOAD_SAVE_SCROLL_DIRECTION_NONE;
|
||||
|
||||
@@ -1265,7 +1292,7 @@ int lsgLoadGame(int mode)
|
||||
|
||||
_slot_cursor = clickedSlot;
|
||||
if (clickedSlot == doubleClickSlot) {
|
||||
keyCode = 500;
|
||||
keyCode = kLoadSaveActionDone;
|
||||
soundPlayFile("ib1p1xx1");
|
||||
}
|
||||
|
||||
@@ -1283,7 +1310,7 @@ int lsgLoadGame(int mode)
|
||||
brightnessIncrease();
|
||||
break;
|
||||
case KEY_RETURN:
|
||||
keyCode = 500;
|
||||
keyCode = kLoadSaveActionDone;
|
||||
break;
|
||||
case KEY_CTRL_Q:
|
||||
case KEY_CTRL_X:
|
||||
@@ -1296,7 +1323,7 @@ int lsgLoadGame(int mode)
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == 500) {
|
||||
if (keyCode == kLoadSaveActionDone) {
|
||||
if (_LSstatus[_slot_cursor] != SLOT_STATE_EMPTY) {
|
||||
rc = 1;
|
||||
} else {
|
||||
@@ -1500,6 +1527,16 @@ int lsgLoadGame(int mode)
|
||||
return rc;
|
||||
}
|
||||
|
||||
void lsgDevSetLoadGameSlot(int slot)
|
||||
{
|
||||
gDevLoadGameSlot = slot;
|
||||
}
|
||||
|
||||
int lsgGetTotalSlotCount()
|
||||
{
|
||||
return saveLoadTotalSlots;
|
||||
}
|
||||
|
||||
// 0x47D2E4
|
||||
static int lsgWindowInit(int windowType)
|
||||
{
|
||||
@@ -1659,7 +1696,7 @@ static int lsgWindowInit(int windowType)
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
500,
|
||||
kLoadSaveActionDone,
|
||||
_loadsaveFrmImages[LOAD_SAVE_FRM_RED_BUTTON_NORMAL].getData(),
|
||||
_loadsaveFrmImages[LOAD_SAVE_FRM_RED_BUTTON_PRESSED].getData(),
|
||||
nullptr,
|
||||
@@ -1936,6 +1973,11 @@ bool _isLoadingGame()
|
||||
// 0x47DC68
|
||||
static int lsgLoadGameInSlot(int slot)
|
||||
{
|
||||
if (slot < 0 || slot >= saveLoadTotalSlots) {
|
||||
return -1;
|
||||
}
|
||||
assert(slot == _slot_cursor);
|
||||
|
||||
_loadingGame = true;
|
||||
|
||||
if (isInCombat()) {
|
||||
@@ -1950,9 +1992,6 @@ static int lsgLoadGameInSlot(int slot)
|
||||
snprintf(_gmpath, sizeof(_gmpath), "%s\\%s%.2d\\", "SAVEGAME", "SLOT", _slot_cursor + 1);
|
||||
strcat(_gmpath, "SAVE.DAT");
|
||||
|
||||
LoadSaveSlotData* ptr = &(_LSData[slot]);
|
||||
debugPrint("\nLOADSAVE: Load name: %s\n", ptr->description);
|
||||
|
||||
_flptr = fileOpen(_gmpath, "rb");
|
||||
if (_flptr == nullptr) {
|
||||
debugPrint("\nLOADSAVE: ** Error opening load game file for reading! **\n");
|
||||
@@ -1969,6 +2008,9 @@ static int lsgLoadGameInSlot(int slot)
|
||||
return -1;
|
||||
}
|
||||
|
||||
LoadSaveSlotData* ptr = &(_LSData[slot]);
|
||||
debugPrint("\nLOADSAVE: Load name: %s\n", ptr->description);
|
||||
|
||||
debugPrint("LOADSAVE: Load file header size read: %d bytes.\n", fileTell(_flptr) - pos);
|
||||
|
||||
for (int index = 0; index < LOAD_SAVE_HANDLER_COUNT; index += 1) {
|
||||
|
||||
@@ -18,6 +18,8 @@ void _InitLoadSave();
|
||||
void _ResetLoadSave();
|
||||
int lsgSaveGame(int mode);
|
||||
int lsgLoadGame(int mode);
|
||||
void lsgDevSetLoadGameSlot(int slot);
|
||||
int lsgGetTotalSlotCount();
|
||||
bool _isLoadingGame();
|
||||
void lsgInit();
|
||||
int MapDirErase(const char* path, const char* extension);
|
||||
|
||||
+51
-1
@@ -1,6 +1,7 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "art.h"
|
||||
@@ -53,6 +54,8 @@ static void main_exit_system();
|
||||
static int _main_load_new(char* fname);
|
||||
static int main_loadgame_new();
|
||||
static void main_unload_new();
|
||||
static void mainParseCommandLineArguments(int argc, char** argv);
|
||||
static bool mainTryParseDevLoadGameSlot(const char* value, int* slotPtr);
|
||||
static void mainLoop();
|
||||
static void showDeath();
|
||||
static void _main_death_voiceover_callback();
|
||||
@@ -71,6 +74,8 @@ static bool _main_show_death_scene = false;
|
||||
// 0x614838
|
||||
static bool _main_death_voiceover_done;
|
||||
|
||||
static int commandLineDevLoadGameSlot = -1;
|
||||
|
||||
// 0x48099C
|
||||
int falloutMain(int argc, char** argv)
|
||||
{
|
||||
@@ -82,6 +87,8 @@ int falloutMain(int argc, char** argv)
|
||||
return 1;
|
||||
}
|
||||
|
||||
mainParseCommandLineArguments(argc, argv);
|
||||
|
||||
// SFALL: Allow to skip intro movies
|
||||
int skipOpeningMovies = settings.ui.skip_opening_movies;
|
||||
if (skipOpeningMovies < 1) {
|
||||
@@ -102,7 +109,14 @@ int falloutMain(int argc, char** argv)
|
||||
mainMenuWindowUnhide(true);
|
||||
|
||||
mouseShowCursor();
|
||||
int mainMenuRc = mainMenuWindowHandleEvents();
|
||||
int devLoadGameSlot = commandLineDevLoadGameSlot;
|
||||
int mainMenuRc;
|
||||
if (devLoadGameSlot != -1) {
|
||||
commandLineDevLoadGameSlot = -1;
|
||||
mainMenuRc = MAIN_MENU_LOAD_GAME;
|
||||
} else {
|
||||
mainMenuRc = mainMenuWindowHandleEvents();
|
||||
}
|
||||
mouseHideCursor();
|
||||
|
||||
switch (mainMenuRc) {
|
||||
@@ -163,6 +177,9 @@ int falloutMain(int argc, char** argv)
|
||||
// NOTE: Uninline.
|
||||
main_loadgame_new();
|
||||
|
||||
if (devLoadGameSlot != -1) {
|
||||
lsgDevSetLoadGameSlot(devLoadGameSlot);
|
||||
}
|
||||
int loadGameRc = lsgLoadGame(LOAD_SAVE_MODE_FROM_MAIN_MENU);
|
||||
if (loadGameRc == -1) {
|
||||
debugPrint("\n ** Error running LoadGame()! **\n");
|
||||
@@ -243,6 +260,39 @@ static bool falloutInit(int argc, char** argv)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void mainParseCommandLineArguments(int argc, char** argv)
|
||||
{
|
||||
const char* devLoadGamePrefix = "--dev-load-game=";
|
||||
size_t devLoadGamePrefixLength = strlen(devLoadGamePrefix);
|
||||
|
||||
for (int arg = 1; arg < argc; arg += 1) {
|
||||
if (strncmp(argv[arg], devLoadGamePrefix, devLoadGamePrefixLength) == 0) {
|
||||
int slot;
|
||||
if (mainTryParseDevLoadGameSlot(argv[arg] + devLoadGamePrefixLength, &slot)) {
|
||||
commandLineDevLoadGameSlot = slot;
|
||||
} else {
|
||||
debugPrint("MAIN: invalid --dev-load-game value '%s'\n", argv[arg] + devLoadGamePrefixLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool mainTryParseDevLoadGameSlot(const char* value, int* slotPtr)
|
||||
{
|
||||
if (value == nullptr || slotPtr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
long slotNumber = strtol(value, &end, 10);
|
||||
if (end == value || *end != '\0' || slotNumber < 1 || slotNumber > lsgGetTotalSlotCount()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*slotPtr = static_cast<int>(slotNumber - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
// NOTE: Inlined.
|
||||
//
|
||||
// 0x480D0C
|
||||
|
||||
@@ -656,6 +656,15 @@ int mouseGetEvent()
|
||||
return gMouseEvent;
|
||||
}
|
||||
|
||||
void mouseReset()
|
||||
{
|
||||
gMouseEvent = 0;
|
||||
_raw_buttons = 0;
|
||||
last_buttons = 0;
|
||||
gMouseWheelX = 0;
|
||||
gMouseWheelY = 0;
|
||||
}
|
||||
|
||||
// 0x4CAAA8
|
||||
bool cursorIsHidden()
|
||||
{
|
||||
|
||||
@@ -47,6 +47,7 @@ void mouseGetRect(Rect* rect);
|
||||
void mouseGetPosition(int* out_x, int* out_y);
|
||||
void _mouse_set_position(int x, int y);
|
||||
int mouseGetEvent();
|
||||
void mouseReset();
|
||||
bool cursorIsHidden();
|
||||
void _mouse_get_raw_state(int* out_x, int* out_y, int* out_buttons);
|
||||
void mouseSetSensitivity(double value);
|
||||
|
||||
+34
-3
@@ -4,7 +4,9 @@
|
||||
|
||||
#include "game.h"
|
||||
#include "sfall_script_hooks.h"
|
||||
#include "svga.h"
|
||||
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace fallout {
|
||||
@@ -272,6 +274,7 @@ static constexpr SDL_Scancode kDiks[DIK_MAP_COUNT] = {
|
||||
};
|
||||
|
||||
std::unordered_map<SDL_Scancode, int> kScanCodeToDik;
|
||||
std::deque<std::pair<SDL_Scancode, bool>> syntheticKeyEvents;
|
||||
|
||||
/// Translates Sfall key code (DIK or VK constant) to SDL scancode.
|
||||
static SDL_Scancode get_scancode_from_key(int key)
|
||||
@@ -319,13 +322,41 @@ void sfall_kb_press_key(int key)
|
||||
}
|
||||
|
||||
SDL_Event event;
|
||||
event.key.keysym.scancode = scancode;
|
||||
SDL_zero(event);
|
||||
|
||||
event.type = SDL_KEYDOWN;
|
||||
SDL_PushEvent(&event);
|
||||
event.key.timestamp = SDL_GetTicks();
|
||||
event.key.windowID = gSdlWindow != nullptr ? SDL_GetWindowID(gSdlWindow) : 0;
|
||||
event.key.state = SDL_PRESSED;
|
||||
event.key.repeat = 0;
|
||||
event.key.keysym.scancode = scancode;
|
||||
event.key.keysym.sym = SDL_GetKeyFromScancode(scancode);
|
||||
event.key.keysym.mod = SDL_GetModState();
|
||||
if (SDL_PushEvent(&event) == 1) {
|
||||
syntheticKeyEvents.emplace_back(scancode, true);
|
||||
}
|
||||
|
||||
event.type = SDL_KEYUP;
|
||||
SDL_PushEvent(&event);
|
||||
event.key.timestamp = SDL_GetTicks();
|
||||
event.key.state = SDL_RELEASED;
|
||||
if (SDL_PushEvent(&event) == 1) {
|
||||
syntheticKeyEvents.emplace_back(scancode, false);
|
||||
}
|
||||
}
|
||||
|
||||
bool sfall_kb_consume_synthetic_key_event(int sdlScanCode, bool pressed)
|
||||
{
|
||||
if (syntheticKeyEvents.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& [expectedScanCode, expectedPressed] = syntheticKeyEvents.front();
|
||||
if (expectedScanCode != static_cast<SDL_Scancode>(sdlScanCode) || expectedPressed != pressed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
syntheticKeyEvents.pop_front();
|
||||
return true;
|
||||
}
|
||||
|
||||
int sfall_kb_handle_key_pressed(int sdlScanCode, bool pressed)
|
||||
|
||||
@@ -9,6 +9,9 @@ bool sfall_kb_is_key_pressed(int key);
|
||||
/// Simulates pressing `key`.
|
||||
void sfall_kb_press_key(int key);
|
||||
|
||||
/// Returns `true` when the next matching SDL key event was injected by `tap_key`.
|
||||
bool sfall_kb_consume_synthetic_key_event(int sdlScanCode, bool pressed);
|
||||
|
||||
int sfall_kb_handle_key_pressed(int sdlScanCode, bool pressed);
|
||||
|
||||
} // namespace fallout
|
||||
|
||||
@@ -1238,6 +1238,19 @@ int _win_check_all_buttons()
|
||||
return keyCode;
|
||||
}
|
||||
|
||||
void windowResetButtonState()
|
||||
{
|
||||
if (!gWindowSystemInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < gWindowsLength; index++) {
|
||||
Window* window = gWindows[index];
|
||||
window->hoveredButton = nullptr;
|
||||
window->clickedButton = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// 0x4D79DC
|
||||
Button* buttonGetButton(int btn, Window** windowPtr)
|
||||
{
|
||||
|
||||
@@ -195,6 +195,7 @@ int windowGetHeight(int win);
|
||||
int windowGetRect(int win, Rect* rect);
|
||||
int _win_check_all_buttons();
|
||||
int _GNW_check_menu_bars(int input);
|
||||
void windowResetButtonState();
|
||||
void programWindowSetTitle(const char* title);
|
||||
bool showMesageBox(const char* str);
|
||||
int buttonCreate(int win, int x, int y, int width, int height, int mouseEnterEventCode, int mouseExitEventCode, int mouseDownEventCode, int mouseUpEventCode, unsigned char* up, unsigned char* dn, unsigned char* hover, int flags);
|
||||
|
||||
+638
-4
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user