Initial commit

This commit is contained in:
Alexander Batalov 2022-05-19 11:51:26 +03:00
commit 55661742a2
235 changed files with 140795 additions and 0 deletions

2
.clang-format Normal file
View File

@ -0,0 +1,2 @@
BasedOnStyle: WebKit
AllowShortIfStatementsOnASingleLine: WithoutElse

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_white_space = true

12
.gitattributes vendored Normal file
View File

@ -0,0 +1,12 @@
# Force LF
*.c text eol=lf
*.h text eol=lf
*.md text eol=lf
*.json text eol=lf
*.yml text eol=lf
.clang-format text eol=lf
.editorconfig text eol=lf
.gitattributes text eol=lf
.gitignore text eol=lf
CMakeLists.txt text eol=lf
LICENSE text eol=lf

42
.github/workflows/Build.yml vendored Normal file
View File

@ -0,0 +1,42 @@
name: Build
on:
push:
paths:
- '.github/workflows/Build.yml'
- 'src/**.c'
- 'src/**.h'
- '**/CMakeLists.txt'
pull_request:
paths:
- '.github/workflows/Build.yml'
- 'src/**.c'
- 'src/**.h'
- '**/CMakeLists.txt'
defaults:
run:
shell: bash
jobs:
Build:
runs-on: windows-latest
steps:
- name: Clone
uses: actions/checkout@v2
- name: Prepare
run: cmake -B Build -A Win32
- name: Release build
run: cmake --build Build --config Release
- name: Artifact
uses: actions/upload-artifact@v3
with:
name: fallout2-ce
path: |
Build/*/fallout2-ce.exe
retention-days: 7

392
.gitignore vendored Normal file
View File

@ -0,0 +1,392 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Dd]ebug-Remote/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Nuget personal access tokens and Credentials
nuget.config
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
.idea/
*.sln.iml
# CMake
/out

239
CMakeLists.txt Normal file
View File

@ -0,0 +1,239 @@
cmake_minimum_required(VERSION 3.13)
project(fallout2-ce LANGUAGES C)
add_executable(fallout2-ce WIN32
"src/actions.c"
"src/actions.h"
"src/animation.c"
"src/animation.h"
"src/args.c"
"src/args.h"
"src/art.c"
"src/art.h"
"src/audio_file.c"
"src/audio_file.h"
"src/audio.c"
"src/audio.h"
"src/automap.c"
"src/automap.h"
"src/autorun.c"
"src/autorun.h"
"src/cache.c"
"src/cache.h"
"src/character_editor.c"
"src/character_editor.h"
"src/character_selector.c"
"src/character_selector.h"
"src/color.c"
"src/color.h"
"src/combat_ai_defs.h"
"src/combat_ai.c"
"src/combat_ai.h"
"src/combat_defs.h"
"src/combat.c"
"src/combat.h"
"src/config.c"
"src/config.h"
"src/core.c"
"src/core.h"
"src/credits.c"
"src/credits.h"
"src/critter.c"
"src/critter.h"
"src/cycle.c"
"src/cycle.h"
"src/datafile.c"
"src/datafile.h"
"src/db.c"
"src/db.h"
"src/dbox.c"
"src/dbox.h"
"src/debug.c"
"src/debug.h"
"src/dfile.c"
"src/dfile.h"
"src/dialog.c"
"src/dialog.h"
"src/dictionary.c"
"src/dictionary.h"
"src/dinput.c"
"src/dinput.h"
"src/display_monitor.c"
"src/display_monitor.h"
"src/draw.c"
"src/draw.h"
"src/electronic_registration.c"
"src/electronic_registration.h"
"src/elevator.c"
"src/elevator.h"
"src/endgame.c"
"src/endgame.h"
"src/export.c"
"src/export.h"
"src/file_find.c"
"src/file_find.h"
"src/file_utils.c"
"src/file_utils.h"
"src/font_manager.c"
"src/font_manager.h"
"src/game_config.c"
"src/game_config.h"
"src/game_dialog.c"
"src/game_dialog.h"
"src/game_memory.c"
"src/game_memory.h"
"src/game_mouse.c"
"src/game_mouse.h"
"src/game_movie.c"
"src/game_movie.h"
"src/game_palette.c"
"src/game_palette.h"
"src/game_sound.c"
"src/game_sound.h"
"src/game_vars.h"
"src/game.c"
"src/game.h"
"src/geometry.c"
"src/geometry.h"
"src/graph_lib.c"
"src/graph_lib.h"
"src/grayscale.c"
"src/grayscale.h"
"src/heap.c"
"src/heap.h"
"src/interface.c"
"src/interface.h"
"src/interpreter_extra.c"
"src/interpreter_extra.h"
"src/interpreter_lib.c"
"src/interpreter_lib.h"
"src/interpreter.c"
"src/interpreter.h"
"src/inventory.c"
"src/inventory.h"
"src/item.c"
"src/item.h"
"src/light.c"
"src/light.h"
"src/lips.c"
"src/lips.h"
"src/loadsave.c"
"src/loadsave.h"
"src/main.c"
"src/main.h"
"src/map_defs.h"
"src/map.c"
"src/map.h"
"src/memory_defs.h"
"src/memory_manager.c"
"src/memory_manager.h"
"src/memory.c"
"src/memory.h"
"src/message.c"
"src/message.h"
"src/mmx.c"
"src/mmx.h"
"src/mouse_manager.c"
"src/mouse_manager.h"
"src/movie_effect.c"
"src/movie_effect.h"
"src/movie_lib.c"
"src/movie_lib.h"
"src/movie.c"
"src/movie.h"
"src/nevs.c"
"src/nevs.h"
"src/obj_types.h"
"src/object.c"
"src/object.h"
"src/options.c"
"src/options.h"
"src/palette.c"
"src/palette.h"
"src/party_member.c"
"src/party_member.h"
"src/perk_defs.h"
"src/perk.c"
"src/perk.h"
"src/pipboy.c"
"src/pipboy.h"
"src/proto_instance.c"
"src/proto_instance.h"
"src/proto_types.h"
"src/proto.c"
"src/proto.h"
"src/queue.c"
"src/queue.h"
"src/random.c"
"src/random.h"
"src/reaction.c"
"src/reaction.h"
"src/region.c"
"src/region.h"
"src/scripts.c"
"src/scripts.h"
"src/select_file_list.c"
"src/select_file_list.h"
"src/selfrun.c"
"src/selfrun.h"
"src/skill_defs.h"
"src/skill.c"
"src/skill.h"
"src/skilldex.c"
"src/skilldex.h"
"src/sound_decoder.c"
"src/sound_decoder.h"
"src/sound_effects_cache.c"
"src/sound_effects_cache.h"
"src/sound_effects_list.c"
"src/sound_effects_list.h"
"src/sound.c"
"src/sound.h"
"src/stat_defs.h"
"src/stat.c"
"src/stat.h"
"src/string_parsers.c"
"src/string_parsers.h"
"src/text_font.c"
"src/text_font.h"
"src/text_object.c"
"src/text_object.h"
"src/tile.c"
"src/tile.h"
"src/trait_defs.h"
"src/trait.c"
"src/trait.h"
"src/trap.c"
"src/trap.h"
"src/version.c"
"src/version.h"
"src/widget.c"
"src/widget.h"
"src/win32.c"
"src/win32.h"
"src/window_manager_private.c"
"src/window_manager_private.h"
"src/window_manager.c"
"src/window_manager.h"
"src/window.c"
"src/window.h"
"src/word_wrap.c"
"src/word_wrap.h"
"src/world_map.c"
"src/world_map.h"
"src/xfile.c"
"src/xfile.h"
)
target_compile_definitions(fallout2-ce PUBLIC
_CRT_SECURE_NO_WARNINGS
_CRT_NONSTDC_NO_WARNINGS
)
target_link_libraries(fallout2-ce
winmm
)
add_subdirectory("third_party/fpattern")
add_subdirectory("third_party/zlib")

26
CMakeSettings.json Normal file
View File

@ -0,0 +1,26 @@
{
"configurations": [
{
"name": "x86-Debug",
"generator": "Visual Studio 16 2019",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x86" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
},
{
"name": "x86-Release",
"generator": "Visual Studio 16 2019",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x86" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "",
"ctestCommandArgs": ""
}
]
}

26
README.md Normal file
View File

@ -0,0 +1,26 @@
# Fallout 2 Community Edition
## Installation
You must own the game to play. Purchase your copy on [GoG](https://www.gog.com/game/fallout_2) or [Steam](https://store.steampowered.com/app/38410). Download latest build or build from source. The `fallout2-ce.exe` serves as a drop-in replacement for `fallout2.exe`. Copy it to your Fallout 2 directory and run.
## Contributing
For now there are three major areas.
### Intergrating Sfall
There are literally hundreds if not thousands of fixes and features in sfall. I guess not all of them are needed in Community Edition, but for the sake of compatibility with big mods out there, let's integrate them all.
### SDL
Migrate DirectX stuff to SDL. This is the shortest path to native Linux version.
### Prepare to 64-bit
Modern macOS requires apps to be 64-bit, so even if we have SDL, the scripting part of the game will not work, because of builtin SSL interpreter. It stores pointers (both functions and variables) as 32-bit integers, so 64-bit pointers will not fit into stack. Since the stack is shared for both instructions and data, it needs some attention.
## Legal & License
See [Fallout 2 Reference Edition](https://github.com/alexbatalov/fallout2-re). Same conditions apply until the source code in this repository is changed significantly.

2028
src/actions.c Normal file

File diff suppressed because it is too large Load Diff

52
src/actions.h Normal file
View File

@ -0,0 +1,52 @@
#ifndef ACTIONS_H
#define ACTIONS_H
#include "combat_defs.h"
#include "obj_types.h"
#include "proto_types.h"
#include <stdbool.h>
extern int _action_in_explode;
extern const int gNormalDeathAnimations[DAMAGE_TYPE_COUNT];
extern const int gMaximumBloodDeathAnimations[DAMAGE_TYPE_COUNT];
int actionKnockdown(Object* obj, int* anim, int maxDistance, int rotation, int delay);
int _action_blood(Object* obj, int anim, int delay);
int _pick_death(Object* attacker, Object* defender, Object* weapon, int damage, int anim, bool isFallingBack);
int _check_death(Object* obj, int anim, int minViolenceLevel, bool isFallingBack);
int _internal_destroy(Object* a1, Object* a2);
void _show_damage_to_object(Object* a1, int damage, int flags, Object* weapon, bool isFallingBack, int knockbackDistance, int knockbackRotation, int a8, Object* a9, int a10);
int _show_death(Object* obj, int anim);
int _show_damage_extras(Attack* attack);
void _show_damage(Attack* attack, int a2, int a3);
int _action_attack(Attack* attack);
int _action_melee(Attack* attack, int a2);
int _action_ranged(Attack* attack, int a2);
int _is_next_to(Object* a1, Object* a2);
int _action_climb_ladder(Object* a1, Object* a2);
int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3);
int _action_use_an_object(Object* a1, Object* a2);
int actionPickUp(Object* critter, Object* item);
int _action_loot_container(Object* critter, Object* container);
int _action_skill_use(int a1);
int actionUseSkill(Object* a1, Object* a2, int skill);
bool _is_hit_from_front(Object* a1, Object* a2);
bool _can_see(Object* a1, Object* a2);
int _pick_fall(Object* obj, int anim);
bool _action_explode_running();
int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object* a5, bool a6);
int _report_explosion(Attack* attack, Object* a2);
int _finished_explosion(Object* a1, Object* a2);
int _compute_explosion_damage(int min, int max, Object* a3, int* a4);
int actionTalk(Object* a1, Object* a2);
int _can_talk_to(Object* a1, Object* a2);
int _talk_to(Object* a1, Object* a2);
void _action_dmg(int tile, int elevation, int minDamage, int maxDamage, int damageType, bool animated, bool bypassArmor);
int _report_dmg(Attack* attack, Object* a2);
int _compute_dmg_damage(int min, int max, Object* obj, int* a4, int damage_type);
bool actionCheckPush(Object* a1, Object* a2);
int actionPush(Object* a1, Object* a2);
int _action_can_talk_to(Object* a1, Object* a2);
#endif /* ACTIONS_H */

2853
src/animation.c Normal file

File diff suppressed because it is too large Load Diff

287
src/animation.h Normal file
View File

@ -0,0 +1,287 @@
#ifndef ANIMATION_H
#define ANIMATION_H
#include "art.h"
#include "combat_defs.h"
#include "obj_types.h"
#include "sound.h"
#include <stdbool.h>
#define ANIMATION_SEQUENCE_LIST_CAPACITY (32)
#define ANIMATION_DESCRIPTION_LIST_CAPACITY (55)
typedef enum AnimKind {
ANIM_KIND_OBJ_MOVE_TO_OBJ = 0,
ANIM_KIND_OBJ_MOVE_TO_TILE = 1,
ANIM_KIND_2 = 2,
ANIM_KIND_KNOCKDOWN = 3,
ANIM_KIND_ANIMATE = 4,
ANIM_KIND_ANIMATE_REVERSE = 5,
ANIM_KIND_6 = 6,
ANIM_KIND_SET_ROTATION_TO_TILE = 7,
ANIM_KIND_ROTATE_CLOCKWISE = 8,
ANIM_KIND_ROTATE_COUNTER_CLOCKWISE = 9,
ANIM_KIND_HIDE = 10,
ANIM_KIND_EXEC = 11,
ANIM_KIND_EXEC_2 = 12,
ANIM_KIND_14 = 14,
ANIM_KIND_15 = 15,
ANIM_KIND_16 = 16,
ANIM_KIND_17 = 17,
ANIM_KIND_18 = 18,
ANIM_KIND_19 = 19,
ANIM_KIND_20 = 20,
ANIM_KIND_23 = 23,
ANIM_KIND_24 = 24,
ANIM_KIND_ANIMATE_FOREVER = 25,
ANIM_KIND_26 = 26,
ANIM_KIND_27 = 27,
ANIM_KIND_28 = 28,
} AnimKind;
// Basic animations: 0-19
// Knockdown and death: 20-35
// Change positions: 36-37
// Weapon: 38-47
// Single-frame death animations (the last frame of knockdown and death animations): 48-63
typedef enum AnimationType {
ANIM_STAND = 0,
ANIM_WALK = 1,
ANIM_JUMP_BEGIN = 2,
ANIM_JUMP_END = 3,
ANIM_CLIMB_LADDER = 4,
ANIM_FALLING = 5,
ANIM_UP_STAIRS_RIGHT = 6,
ANIM_UP_STAIRS_LEFT = 7,
ANIM_DOWN_STAIRS_RIGHT = 8,
ANIM_DOWN_STAIRS_LEFT = 9,
ANIM_MAGIC_HANDS_GROUND = 10,
ANIM_MAGIC_HANDS_MIDDLE = 11,
ANIM_MAGIC_HANDS_UP = 12,
ANIM_DODGE_ANIM = 13,
ANIM_HIT_FROM_FRONT = 14,
ANIM_HIT_FROM_BACK = 15,
ANIM_THROW_PUNCH = 16,
ANIM_KICK_LEG = 17,
ANIM_THROW_ANIM = 18,
ANIM_RUNNING = 19,
ANIM_FALL_BACK = 20,
ANIM_FALL_FRONT = 21,
ANIM_BAD_LANDING = 22,
ANIM_BIG_HOLE = 23,
ANIM_CHARRED_BODY = 24,
ANIM_CHUNKS_OF_FLESH = 25,
ANIM_DANCING_AUTOFIRE = 26,
ANIM_ELECTRIFY = 27,
ANIM_SLICED_IN_HALF = 28,
ANIM_BURNED_TO_NOTHING = 29,
ANIM_ELECTRIFIED_TO_NOTHING = 30,
ANIM_EXPLODED_TO_NOTHING = 31,
ANIM_MELTED_TO_NOTHING = 32,
ANIM_FIRE_DANCE = 33,
ANIM_FALL_BACK_BLOOD = 34,
ANIM_FALL_FRONT_BLOOD = 35,
ANIM_PRONE_TO_STANDING = 36,
ANIM_BACK_TO_STANDING = 37,
ANIM_TAKE_OUT = 38,
ANIM_PUT_AWAY = 39,
ANIM_PARRY_ANIM = 40,
ANIM_THRUST_ANIM = 41,
ANIM_SWING_ANIM = 42,
ANIM_POINT = 43,
ANIM_UNPOINT = 44,
ANIM_FIRE_SINGLE = 45,
ANIM_FIRE_BURST = 46,
ANIM_FIRE_CONTINUOUS = 47,
ANIM_FALL_BACK_SF = 48,
ANIM_FALL_FRONT_SF = 49,
ANIM_BAD_LANDING_SF = 50,
ANIM_BIG_HOLE_SF = 51,
ANIM_CHARRED_BODY_SF = 52,
ANIM_CHUNKS_OF_FLESH_SF = 53,
ANIM_DANCING_AUTOFIRE_SF = 54,
ANIM_ELECTRIFY_SF = 55,
ANIM_SLICED_IN_HALF_SF = 56,
ANIM_BURNED_TO_NOTHING_SF = 57,
ANIM_ELECTRIFIED_TO_NOTHING_SF = 58,
ANIM_EXPLODED_TO_NOTHING_SF = 59,
ANIM_MELTED_TO_NOTHING_SF = 60,
ANIM_FIRE_DANCE_SF = 61,
ANIM_FALL_BACK_BLOOD_SF = 62,
ANIM_FALL_FRONT_BLOOD_SF = 63,
ANIM_CALLED_SHOT_PIC = 64,
ANIM_COUNT = 65,
FIRST_KNOCKDOWN_AND_DEATH_ANIM = ANIM_FALL_BACK,
LAST_KNOCKDOWN_AND_DEATH_ANIM = ANIM_FALL_FRONT_BLOOD,
FIRST_SF_DEATH_ANIM = ANIM_FALL_BACK_SF,
LAST_SF_DEATH_ANIM = ANIM_FALL_FRONT_BLOOD_SF,
} AnimationType;
typedef int AnimationProc(Object*, Object*);
typedef int AnimationSoundProc(Sound*);
typedef int AnimationProc2(Object*, Object*, void*);
typedef struct AnimationDescription {
int type;
Object* owner;
union {
Object* destinationObj;
Sound* sound;
};
union {
int tile;
int fid; // for type == 17
int weaponAnimationCode; // for type == 18
int lightDistance; // for type == 19
};
int elevation;
int anim; // anim
int delay; // delay
union {
AnimationProc* proc;
AnimationSoundProc* soundProc;
};
AnimationProc2* field_20; // func
int field_24;
union {
int field_28; // actionPoints
Object* field_28_obj; // obj in type == 12
void* field_28_void;
};
CacheEntry* field_2C;
} AnimationDescription;
typedef struct AnimationSequence {
int field_0;
// Index of current animation in [animations] array or -1 if animations in
// this sequence is not playing.
int animationIndex;
// Number of scheduled animations in [animations] array.
int length;
int flags;
AnimationDescription animations[ANIMATION_DESCRIPTION_LIST_CAPACITY];
} AnimationSequence;
typedef struct PathNode {
int tile;
int from;
// actual type is likely char
int rotation;
int field_C;
int field_10;
} PathNode;
typedef struct STRUCT_530014_28 {
int tile;
int elevation;
int x;
int y;
} STRUCT_530014_28;
typedef struct STRUCT_530014 {
int flags; // flags
Object* obj;
int fid; // fid
int field_C;
int field_10;
int field_14; // animation speed?
int animationSequenceIndex;
int field_1C; // length of field_28
int field_20; // current index in field_28
int field_24;
union {
unsigned char rotations[3200];
STRUCT_530014_28 field_28[200];
};
} STRUCT_530014;
static_assert(sizeof(STRUCT_530014) == 3240, "wrong size");
typedef Object* PathBuilderCallback(Object* object, int tile, int elevation);
extern int _curr_sad;
extern int gAnimationSequenceCurrentIndex;
extern int _anim_in_init;
extern bool _anim_in_anim_stop;
extern bool _anim_in_bk;
extern int _lastDestination;
extern unsigned int _last_time_;
extern unsigned int _next_time;
extern STRUCT_530014 _sad[24];
extern PathNode gClosedPathNodeList[2000];
extern AnimationSequence gAnimationSequences[32];
extern unsigned char gPathfinderProcessedTiles[5000];
extern PathNode gOpenPathNodeList[2000];
extern int gAnimationDescriptionCurrentIndex;
extern Object* dword_56C7E0[100];
void animationInit();
void animationReset();
void animationExit();
int reg_anim_begin(int a1);
int _anim_free_slot(int a1);
int _register_priority(int a1);
int reg_anim_clear(Object* a1);
int reg_anim_end();
void _anim_cleanup();
int _check_registry(Object* obj);
int animationIsBusy(Object* a1);
int reg_anim_obj_move_to_obj(Object* a1, Object* a2, int actionPoints, int delay);
int reg_anim_obj_run_to_obj(Object* owner, Object* destination, int actionPoints, int delay);
int reg_anim_obj_move_to_tile(Object* obj, int tile_num, int elev, int actionPoints, int delay);
int reg_anim_obj_run_to_tile(Object* obj, int tile_num, int elev, int actionPoints, int delay);
int reg_anim_2(Object* obj, int tile_num, int elev, int a4, int a5);
int reg_anim_knockdown(Object* obj, int tile, int elev, int anim, int delay);
int reg_anim_animate(Object* obj, int anim, int delay);
int reg_anim_animate_reverse(Object* obj, int anim, int delay);
int reg_anim_6(Object* obj, int anim, int delay);
int reg_anim_set_rotation_to_tile(Object* owner, int tile);
int reg_anim_rotate_clockwise(Object* obj);
int reg_anim_rotate_counter_clockwise(Object* obj);
int reg_anim_hide(Object* obj);
int reg_anim_11_0(Object* a1, Object* a2, AnimationProc* proc, int delay);
int reg_anim_12(Object* a1, Object* a2, void* a3, AnimationProc2* proc, int delay);
int reg_anim_11_1(Object* a1, Object* a2, AnimationProc* proc, int delay);
int reg_anim_15(Object* obj, int a2, int a3);
int reg_anim_17(Object* obj, int fid, int a3);
int reg_anim_18(Object* obj, int a2, int a3);
int reg_anim_update_light(Object* obj, int fid, int a3);
int reg_anim_play_sfx(Object* obj, const char* a2, int a3);
int reg_anim_animate_forever(Object* obj, int a2, int a3);
int reg_anim_26(int a1, int a2);
int animationRunSequence(int a1);
int _anim_set_continue(int a1, int a2);
int _anim_set_end(int a1);
bool canUseDoor(Object* critter, Object* door);
int _make_path(Object* object, int from, int to, unsigned char* a4, int a5);
int pathfinderFindPath(Object* object, int from, int to, unsigned char* rotations, int a5, PathBuilderCallback* callback);
int _idist(int a1, int a2, int a3, int a4);
int _tile_idistance(int tile1, int tile2);
int _make_straight_path(Object* a1, int from, int to, STRUCT_530014_28* pathNodes, Object** a5, int a6);
int _make_straight_path_func(Object* a1, int from, int to, STRUCT_530014_28* a4, Object** a5, int a6, Object* (*a7)(Object*, int, int));
int animateMoveObjectToObject(Object* a1, Object* a2, int a3, int a4, int a5);
int animateMoveObjectToTile(Object* obj, int tile_num, int elev, int a4, int a5, int a6);
int _anim_move(Object* obj, int tile, int elev, int a3, int a4, int a5, int animationSequenceIndex);
int _anim_move_straight_to_tile(Object* obj, int a2, int a3, int fid, int a5, int a6);
int _anim_move_on_stairs(Object* obj, int a2, int a3, int fid, int a5);
int _check_for_falling(Object* obj, int a2, int a3);
void _object_move(int index);
void _object_straight_move(int a1);
int _anim_animate(Object* obj, int a2, int a3, int a4);
void _object_animate();
void _object_anim_compact();
int _check_move(int* a1);
int _dude_move(int a1);
int _dude_run(int a1);
void _dude_fidget();
void _dude_stand(Object* obj, int rotation, int fid);
void _dude_standup(Object* a1);
int actionRotate(Object* obj, int a2, int a3);
int _anim_change_fid(Object* obj, int a2, int fid);
void _anim_stop();
int _check_gravity(int tile, int elevation);
unsigned int _compute_tpf(Object* object, int fid);
#endif /* ANIMATION_H */

114
src/args.c Normal file
View File

@ -0,0 +1,114 @@
#include "args.h"
#include <stdlib.h>
#include <string.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// 0x4E3B90
void argsInit(CommandLineArguments* commandLineArguments)
{
if (commandLineArguments != NULL) {
commandLineArguments->argc = 0;
commandLineArguments->argv = NULL;
}
}
// 0x4E3BA4
bool argsParse(CommandLineArguments* commandLineArguments, char* commandLine)
{
const char* delim = " \t";
int argc = 0;
// Get the number of arguments in command line.
if (*commandLine != '\0') {
char* copy = strdup(commandLine);
if (copy == NULL) {
argsFree(commandLineArguments);
return false;
}
char* tok = strtok(copy, delim);
while (tok != NULL) {
argc++;
tok = strtok(NULL, delim);
}
free(copy);
}
// Make a room for argv[0] - program name.
argc++;
commandLineArguments->argc = argc;
commandLineArguments->argv = malloc(sizeof(*commandLineArguments->argv) * argc);
if (commandLineArguments->argv == NULL) {
argsFree(commandLineArguments);
return false;
}
for (int arg = 0; arg < argc; arg++) {
commandLineArguments->argv[arg] = NULL;
}
// Copy program name into argv[0].
char moduleFileName[MAX_PATH];
int moduleFileNameLength = GetModuleFileNameA(NULL, moduleFileName, MAX_PATH);
if (moduleFileNameLength == 0) {
argsFree(commandLineArguments);
return false;
}
if (moduleFileNameLength >= MAX_PATH) {
moduleFileNameLength = MAX_PATH - 1;
}
moduleFileName[moduleFileNameLength] = '\0';
commandLineArguments->argv[0] = strdup(moduleFileName);
if (commandLineArguments->argv[0] == NULL) {
argsFree(commandLineArguments);
return false;
}
// Copy arguments from command line into argv.
if (*commandLine != '\0') {
char* copy = strdup(commandLine);
if (copy == NULL) {
argsFree(commandLineArguments);
return false;
}
int arg = 1;
char* tok = strtok(copy, delim);
while (tok != NULL) {
commandLineArguments->argv[arg] = strdup(tok);
tok = strtok(NULL, delim);
arg++;
}
free(copy);
}
return true;
}
// 0x4E3D3C
void argsFree(CommandLineArguments* commandLineArguments)
{
if (commandLineArguments->argv != NULL) {
// NOTE: Compiled code is slightly different - it decrements argc.
for (int index = 0; index < commandLineArguments->argc; index++) {
if (commandLineArguments->argv[index] != NULL) {
free(commandLineArguments->argv[index]);
}
}
free(commandLineArguments->argv);
}
commandLineArguments->argc = 0;
commandLineArguments->argv = NULL;
}

15
src/args.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef ARGS_H
#define ARGS_H
#include <stdbool.h>
typedef struct CommandLineArguments {
int argc;
char** argv;
} CommandLineArguments;
void argsInit(CommandLineArguments* commandLineArguments);
bool argsParse(CommandLineArguments* commandLineArguments, char* commandLine);
void argsFree(CommandLineArguments* commandLineArguments);
#endif /* ARGS_H */

1115
src/art.c Normal file

File diff suppressed because it is too large Load Diff

187
src/art.h Normal file
View File

@ -0,0 +1,187 @@
#ifndef ART_H
#define ART_H
#include "cache.h"
#include "db.h"
#include "heap.h"
#include "obj_types.h"
#include "proto_types.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef enum Head {
HEAD_INVALID,
HEAD_MARCUS,
HEAD_MYRON,
HEAD_ELDER,
HEAD_LYNETTE,
HEAD_HAROLD,
HEAD_TANDI,
HEAD_COM_OFFICER,
HEAD_SULIK,
HEAD_PRESIDENT,
HEAD_HAKUNIN,
HEAD_BOSS,
HEAD_DYING_HAKUNIN,
HEAD_COUNT,
} Head;
typedef enum HeadAnimation {
HEAD_ANIMATION_VERY_GOOD_REACTION = 0,
FIDGET_GOOD = 1,
HEAD_ANIMATION_GOOD_TO_NEUTRAL = 2,
HEAD_ANIMATION_NEUTRAL_TO_GOOD = 3,
FIDGET_NEUTRAL = 4,
HEAD_ANIMATION_NEUTRAL_TO_BAD = 5,
HEAD_ANIMATION_BAD_TO_NEUTRAL = 6,
FIDGET_BAD = 7,
HEAD_ANIMATION_VERY_BAD_REACTION = 8,
HEAD_ANIMATION_GOOD_PHONEMES = 9,
HEAD_ANIMATION_NEUTRAL_PHONEMES = 10,
HEAD_ANIMATION_BAD_PHONEMES = 11,
} HeadAnimation;
typedef enum Background {
BACKGROUND_0,
BACKGROUND_1,
BACKGROUND_2,
BACKGROUND_HUB,
BACKGROUND_NECROPOLIS,
BACKGROUND_BROTHERHOOD,
BACKGROUND_MILITARY_BASE,
BACKGROUND_JUNK_TOWN,
BACKGROUND_CATHEDRAL,
BACKGROUND_SHADY_SANDS,
BACKGROUND_VAULT,
BACKGROUND_MASTER,
BACKGROUND_FOLLOWER,
BACKGROUND_RAIDERS,
BACKGROUND_CAVE,
BACKGROUND_ENCLAVE,
BACKGROUND_WASTELAND,
BACKGROUND_BOSS,
BACKGROUND_PRESIDENT,
BACKGROUND_TENT,
BACKGROUND_ADOBE,
BACKGROUND_COUNT,
} Background;
#pragma pack(2)
typedef struct Art {
int field_0;
short framesPerSecond;
short actionFrame;
short frameCount;
short xOffsets[6];
short yOffsets[6];
int dataOffsets[6];
int field_3A;
unsigned char data[];
} Art;
#pragma pack()
static_assert(sizeof(Art) == 62, "wrong size");
typedef struct ArtFrame {
short width;
short height;
int size;
short x;
short y;
unsigned char data[];
} ArtFrame;
typedef struct ArtListDescription {
int flags;
char name[16];
char* fileNames; // dynamic array of null terminated strings 13 bytes long each
void* field_18;
int fileNamesLength; // number of entries in list
} ArtListDescription;
typedef struct HeadDescription {
int goodFidgetCount;
int neutralFidgetCount;
int badFidgetCount;
} HeadDescription;
typedef enum WeaponAnimation {
WEAPON_ANIMATION_NONE,
WEAPON_ANIMATION_KNIFE, // d
WEAPON_ANIMATION_CLUB, // e
WEAPON_ANIMATION_HAMMER, // f
WEAPON_ANIMATION_SPEAR, // g
WEAPON_ANIMATION_PISTOL, // h
WEAPON_ANIMATION_SMG, // i
WEAPON_ANIMATION_SHOTGUN, // j
WEAPON_ANIMATION_LASER_RIFLE, // k
WEAPON_ANIMATION_MINIGUN, // l
WEAPON_ANIMATION_LAUNCHER, // m
WEAPON_ANIMATION_COUNT,
} WeaponAnimation;
typedef enum DudeNativeLook {
// Hero looks as one the tribals (before finishing Temple of Trails).
DUDE_NATIVE_LOOK_TRIBAL,
// Hero have finished Temple of Trails and received Vault Jumpsuit.
DUDE_NATIVE_LOOK_JUMPSUIT,
DUDE_NATIVE_LOOK_COUNT,
} DudeNativeLook;
extern ArtListDescription gArtListDescriptions[OBJ_TYPE_COUNT];
extern bool gArtLanguageInitialized;
extern const char* _head1;
extern const char* _head2;
extern int _art_vault_guy_num;
extern int _art_vault_person_nums[DUDE_NATIVE_LOOK_COUNT][GENDER_COUNT];
extern int _art_mapper_blank_tile;
extern char gArtLanguage[32];
extern Cache gArtCache;
extern char _art_name[MAX_PATH];
extern HeadDescription* gHeadDescriptions;
extern int* _anon_alias;
extern int* gArtCritterFidShoudRunData;
int artInit();
void artReset();
void artExit();
char* artGetObjectTypeName(int objectType);
int artIsObjectTypeHidden(int objectType);
int artGetFidgetCount(int headFid);
void artRender(int fid, unsigned char* dest, int width, int height, int pitch);
Art* artLock(int fid, CacheEntry** cache_entry);
unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry** out_cache_entry);
unsigned char* artLockFrameDataReturningSize(int fid, CacheEntry** out_cache_entry, int* widthPtr, int* heightPtr);
int artUnlock(CacheEntry* cache_entry);
int artCacheFlush();
int artCopyFileName(int a1, int a2, char* a3);
int _art_get_code(int a1, int a2, char* a3, char* a4);
char* artBuildFilePath(int a1);
int artReadList(const char* path, char** out_arr, int* out_count);
int artGetFramesPerSecond(Art* art);
int artGetActionFrame(Art* art);
int artGetFrameCount(Art* art);
int artGetWidth(Art* art, int frame, int direction);
int artGetHeight(Art* art, int frame, int direction);
int artGetSize(Art* art, int frame, int direction, int* out_width, int* out_height);
int artGetFrameOffsets(Art* art, int frame, int direction, int* a4, int* a5);
int artGetRotationOffsets(Art* art, int rotation, int* out_offset_x, int* out_offset_y);
unsigned char* artGetFrameData(Art* art, int frame, int direction);
ArtFrame* artGetFrame(Art* art, int frame, int direction);
bool artExists(int fid);
bool _art_fid_valid(int fid);
int _art_alias_num(int a1);
int artCritterFidShouldRun(int a1);
int _art_alias_fid(int a1);
int artCacheGetFileSizeImpl(int a1, int* out_size);
int artCacheReadDataImpl(int a1, int* a2, unsigned char* data);
void artCacheFreeImpl(void* ptr);
int buildFid(int a1, int a2, int a3, int a4, int a5);
int artReadFrameData(unsigned char* data, File* stream, int count);
int artReadHeader(Art* art, File* stream);
int artRead(const char* path, unsigned char* data);
#endif

250
src/audio.c Normal file
View File

@ -0,0 +1,250 @@
#include "audio.h"
#include "db.h"
#include "debug.h"
#include "memory_manager.h"
#include "sound.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
// 0x5108BC
AudioFileIsCompressedProc* _queryCompressedFunc = _defaultCompressionFunc;
// 0x56CB00
int gAudioListLength;
// 0x56CB04
AudioFile* gAudioList;
// 0x41A2B0
bool _defaultCompressionFunc(char* filePath)
{
char* pch = strrchr(filePath, '.');
if (pch != NULL) {
strcpy(pch + 1, "war");
}
return false;
}
// 0x41A2D0
int audioSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size)
{
return fileRead(buffer, 1, size, (File*)fileHandle);
}
// AudioOpen
// 0x41A2EC
int audioOpen(const char* fname, int flags, ...)
{
char path[80];
sprintf(path, fname);
int compression;
if (_queryCompressedFunc(path)) {
compression = 2;
} else {
compression = 0;
}
char mode[4];
memset(mode, 0, 4);
// NOTE: Original implementation is slightly different, it uses separate
// variable to track index where to set 't' and 'b'.
char* pm = mode;
if (flags & 1) {
*pm++ = 'w';
} else if (flags & 2) {
*pm++ = 'w';
*pm++ = '+';
} else {
*pm++ = 'r';
}
if (flags & 0x100) {
*pm++ = 't';
} else if (flags & 0x200) {
*pm++ = 'b';
}
File* stream = fileOpen(path, mode);
if (stream == NULL) {
debugPrint("AudioOpen: Couldn't open %s for read\n", path);
return -1;
}
int index;
for (index = 0; index < gAudioListLength; index++) {
if ((gAudioList[index].flags & AUDIO_FILE_IN_USE) == 0) {
break;
}
}
if (index == gAudioListLength) {
if (gAudioList != NULL) {
gAudioList = internal_realloc_safe(gAudioList, sizeof(*gAudioList) * (gAudioListLength + 1), __FILE__, __LINE__); // "..\int\audio.c", 216
} else {
gAudioList = internal_malloc_safe(sizeof(*gAudioList), __FILE__, __LINE__); // "..\int\audio.c", 218
}
gAudioListLength++;
}
AudioFile* audioFile = &(gAudioList[index]);
audioFile->flags = AUDIO_FILE_IN_USE;
audioFile->fileHandle = (int)stream;
if (compression == 2) {
audioFile->flags |= AUDIO_FILE_COMPRESSED;
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->fileSize *= 2;
} else {
audioFile->fileSize = fileGetSize(stream);
}
audioFile->position = 0;
return index + 1;
}
// 0x41A50C
int audioClose(int fileHandle)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
fileClose((File*)audioFile->fileHandle);
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
soundDecoderFree(audioFile->soundDecoder);
}
memset(audioFile, 0, sizeof(AudioFile));
return 0;
}
// 0x41A574
int audioRead(int fileHandle, void* buffer, unsigned int size)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
int bytesRead;
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
bytesRead = soundDecoderDecode(audioFile->soundDecoder, buffer, size);
} else {
bytesRead = fileRead(buffer, 1, size, (File*)audioFile->fileHandle);
}
audioFile->position += bytesRead;
return bytesRead;
}
// 0x41A5E0
int audioSeek(int fileHandle, long offset, int origin)
{
int pos;
unsigned char* buf;
int v10;
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
switch (origin) {
case SEEK_SET:
pos = offset;
break;
case SEEK_CUR:
pos = offset + audioFile->position;
break;
case SEEK_END:
pos = offset + audioFile->fileSize;
break;
default:
assert(false && "Should be unreachable");
}
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
if (pos < audioFile->position) {
soundDecoderFree(audioFile->soundDecoder);
fileSeek((File*)audioFile->fileHandle, 0, SEEK_SET);
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->position = 0;
audioFile->fileSize *= 2;
if (pos != 0) {
buf = internal_malloc_safe(4096, __FILE__, __LINE__); // "..\int\audio.c", 361
while (pos > 4096) {
pos -= 4096;
audioRead(fileHandle, buf, 4096);
}
if (pos != 0) {
audioRead(fileHandle, buf, pos);
}
internal_free_safe(buf, __FILE__, __LINE__); // // "..\int\audio.c", 367
}
} else {
buf = internal_malloc_safe(1024, __FILE__, __LINE__); // "..\int\audio.c", 321
v10 = audioFile->position - pos;
while (v10 > 1024) {
v10 -= 1024;
audioRead(fileHandle, buf, 1024);
}
if (v10 != 0) {
audioRead(fileHandle, buf, v10);
}
// TODO: Probably leaks memory.
}
return audioFile->position;
} else {
return fileSeek((File*)audioFile->fileHandle, offset, origin);
}
}
// 0x41A78C
long audioGetSize(int fileHandle)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
return audioFile->fileSize;
}
// 0x41A7A8
long audioTell(int fileHandle)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
return audioFile->position;
}
// AudioWrite
// 0x41A7C4
int audioWrite(int handle, const void* buf, unsigned int size)
{
debugPrint("AudioWrite shouldn't be ever called\n");
return 0;
}
// 0x41A7D4
int audioInit(AudioFileIsCompressedProc* isCompressedProc)
{
_queryCompressedFunc = isCompressedProc;
gAudioList = NULL;
gAudioListLength = 0;
return soundSetDefaultFileIO(audioOpen, audioClose, audioRead, audioWrite, audioSeek, audioTell, audioGetSize);
}
// 0x41A818
void audioExit()
{
if (gAudioList != NULL) {
internal_free_safe(gAudioList, __FILE__, __LINE__); // "..\int\audio.c", 406
}
gAudioListLength = 0;
gAudioList = NULL;
}

23
src/audio.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef AUDIO_H
#define AUDIO_H
#include "audio_file.h"
extern AudioFileIsCompressedProc* _queryCompressedFunc;
extern int gAudioListLength;
extern AudioFile* gAudioList;
bool _defaultCompressionFunc(char* filePath);
int audioSoundDecoderReadHandler(int fileHandle, void* buf, unsigned int size);
int audioOpen(const char* fname, int mode, ...);
int audioClose(int fileHandle);
int audioRead(int fileHandle, void* buffer, unsigned int size);
int audioSeek(int fileHandle, long offset, int origin);
long audioGetSize(int fileHandle);
long audioTell(int fileHandle);
int audioWrite(int handle, const void* buf, unsigned int size);
int audioInit(AudioFileIsCompressedProc* isCompressedProc);
void audioExit();
#endif /* AUDIO_H */

252
src/audio_file.c Normal file
View File

@ -0,0 +1,252 @@
#include "audio_file.h"
#include "debug.h"
#include "memory_manager.h"
#include "sound.h"
#include <assert.h>
#include <io.h>
#include <stdio.h>
#include <string.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
static_assert(sizeof(AudioFile) == 28, "wrong size");
// 0x5108C0
AudioFileIsCompressedProc* _queryCompressedFunc_2 = _defaultCompressionFunc__;
// 0x56CB10
AudioFile* gAudioFileList;
// 0x56CB14
int gAudioFileListLength;
// 0x41A850
bool _defaultCompressionFunc__(char* filePath)
{
char* pch = strrchr(filePath, '.');
if (pch != NULL) {
strcpy(pch + 1, "war");
}
return false;
}
// 0x41A870
int audioFileSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size)
{
return fread(buffer, 1, size, (FILE*)fileHandle);
}
// 0x41A88C
int audioFileOpen(const char* fname, int flags, ...)
{
char path[MAX_PATH];
strcpy(path, fname);
int compression;
if (_queryCompressedFunc_2(path)) {
compression = 2;
} else {
compression = 0;
}
char mode[4];
memset(mode, '\0', 4);
// NOTE: Original implementation is slightly different, it uses separate
// variable to track index where to set 't' and 'b'.
char* pm = mode;
if (flags & 0x01) {
*pm++ = 'w';
} else if (flags & 0x02) {
*pm++ = 'w';
*pm++ = '+';
} else {
*pm++ = 'r';
}
if (flags & 0x0100) {
*pm++ = 't';
} else if (flags & 0x0200) {
*pm++ = 'b';
}
FILE* stream = fopen(path, mode);
if (stream == NULL) {
return -1;
}
int index;
for (index = 0; index < gAudioFileListLength; index++) {
if ((gAudioFileList[index].flags & AUDIO_FILE_IN_USE) == 0) {
break;
}
}
if (index == gAudioFileListLength) {
if (gAudioFileList != NULL) {
gAudioFileList = internal_realloc_safe(gAudioFileList, sizeof(*gAudioFileList) * (gAudioFileListLength + 1), __FILE__, __LINE__); // "..\int\audiof.c", 207
} else {
gAudioFileList = internal_malloc_safe(sizeof(*gAudioFileList), __FILE__, __LINE__); // "..\int\audiof.c", 209
}
gAudioFileListLength++;
}
AudioFile* audioFile = &(gAudioFileList[index]);
audioFile->flags = AUDIO_FILE_IN_USE;
audioFile->fileHandle = (int)stream;
if (compression == 2) {
audioFile->flags |= AUDIO_FILE_COMPRESSED;
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->fileSize *= 2;
} else {
audioFile->fileSize = filelength(fileno(stream));
}
audioFile->position = 0;
return index + 1;
}
// 0x41AAA0
int audioFileClose(int fileHandle)
{
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
fclose((FILE*)audioFile->fileHandle);
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
soundDecoderFree(audioFile->soundDecoder);
}
// Reset audio file (which also resets it's use flag).
memset(audioFile, 0, sizeof(*audioFile));
return 0;
}
// 0x41AB08
int audioFileRead(int fileHandle, void* buffer, unsigned int size)
{
AudioFile* ptr = &(gAudioFileList[fileHandle - 1]);
int bytesRead;
if ((ptr->flags & AUDIO_FILE_COMPRESSED) != 0) {
bytesRead = soundDecoderDecode(ptr->soundDecoder, buffer, size);
} else {
bytesRead = fread(buffer, 1, size, (FILE*)ptr->fileHandle);
}
ptr->position += bytesRead;
return bytesRead;
}
// 0x41AB74
int audioFileSeek(int fileHandle, long offset, int origin)
{
void* buf;
int remaining;
int a4;
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
switch (origin) {
case SEEK_SET:
a4 = offset;
break;
case SEEK_CUR:
a4 = audioFile->fileSize + offset;
break;
case SEEK_END:
a4 = audioFile->position + offset;
break;
default:
assert(false && "Should be unreachable");
}
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
if (a4 <= audioFile->position) {
soundDecoderFree(audioFile->soundDecoder);
fseek((FILE*)audioFile->fileHandle, 0, 0);
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->fileSize *= 2;
audioFile->position = 0;
if (a4) {
buf = internal_malloc_safe(4096, __FILE__, __LINE__); // "..\int\audiof.c", 364
while (a4 > 4096) {
audioFileRead(fileHandle, buf, 4096);
a4 -= 4096;
}
if (a4 != 0) {
audioFileRead(fileHandle, buf, a4);
}
internal_free_safe(buf, __FILE__, __LINE__); // "..\int\audiof.c", 370
}
} else {
buf = internal_malloc_safe(0x400, __FILE__, __LINE__); // "..\int\audiof.c", 316
remaining = audioFile->position - a4;
while (remaining > 1024) {
audioFileRead(fileHandle, buf, 1024);
remaining -= 1024;
}
if (remaining != 0) {
audioFileRead(fileHandle, buf, remaining);
}
// TODO: Obiously leaks memory.
}
return audioFile->position;
}
return fseek((FILE*)audioFile->fileHandle, offset, origin);
}
// 0x41AD20
long audioFileGetSize(int fileHandle)
{
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
return audioFile->fileSize;
}
// 0x41AD3C
long audioFileTell(int fileHandle)
{
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
return audioFile->position;
}
// AudiofWrite
// 0x41AD58
int audioFileWrite(int fileHandle, const void* buffer, unsigned int size)
{
debugPrint("AudiofWrite shouldn't be ever called\n");
return 0;
}
// 0x41AD68
int audioFileInit(AudioFileIsCompressedProc* isCompressedProc)
{
_queryCompressedFunc_2 = isCompressedProc;
gAudioFileList = NULL;
gAudioFileListLength = 0;
return soundSetDefaultFileIO(audioFileOpen, audioFileClose, audioFileRead, audioFileWrite, audioFileSeek, audioFileTell, audioFileGetSize);
}
// 0x41ADAC
void audioFileExit()
{
if (gAudioFileList != NULL) {
internal_free_safe(gAudioFileList, __FILE__, __LINE__); // "..\int\audiof.c", 405
}
gAudioFileListLength = 0;
gAudioFileList = NULL;
}

42
src/audio_file.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef AUDIO_FILE_H
#define AUDIO_FILE_H
#include "sound_decoder.h"
#include <stdbool.h>
typedef enum AudioFileFlags {
AUDIO_FILE_IN_USE = 0x01,
AUDIO_FILE_COMPRESSED = 0x02,
} AudioFileFlags;
typedef struct AudioFile {
int flags;
int fileHandle;
SoundDecoder* soundDecoder;
int fileSize;
int field_10;
int field_14;
int position;
} AudioFile;
typedef bool(AudioFileIsCompressedProc)(char* filePath);
extern AudioFileIsCompressedProc* _queryCompressedFunc_2;
extern AudioFile* gAudioFileList;
extern int gAudioFileListLength;
bool _defaultCompressionFunc__(char* filePath);
int audioFileSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size);
int audioFileOpen(const char* fname, int flags, ...);
int audioFileClose(int a1);
int audioFileRead(int a1, void* buf, unsigned int size);
int audioFileSeek(int handle, long offset, int origin);
long audioFileGetSize(int a1);
long audioFileTell(int a1);
int audioFileWrite(int handle, const void* buf, unsigned int size);
int audioFileInit(AudioFileIsCompressedProc* isCompressedProc);
void audioFileExit();
#endif /* AUDIO_FILE_H */

1118
src/automap.c Normal file

File diff suppressed because it is too large Load Diff

102
src/automap.h Normal file
View File

@ -0,0 +1,102 @@
#ifndef AUTOMAP_H
#define AUTOMAP_H
#include "db.h"
#include "map_defs.h"
#include <stdbool.h>
#define AUTOMAP_DB ("AUTOMAP.DB")
#define AUTOMAP_TMP ("AUTOMAP.TMP")
// The number of map entries that is stored in automap.db.
//
// NOTE: I don't know why this value is not equal to the number of maps.
#define AUTOMAP_MAP_COUNT (160)
#define AUTOMAP_OFFSET_COUNT (AUTOMAP_MAP_COUNT * ELEVATION_COUNT)
#define AUTOMAP_WINDOW_X (75)
#define AUTOMAP_WINDOW_Y (0)
#define AUTOMAP_WINDOW_WIDTH (519)
#define AUTOMAP_WINDOW_HEIGHT (480)
#define AUTOMAP_PIPBOY_VIEW_X (238)
#define AUTOMAP_PIPBOY_VIEW_Y (105)
// View options for rendering automap for map window. These are stored in
// [gAutomapFlags] and is saved in save game file.
typedef enum AutomapFlags {
// NOTE: This is a special flag to denote the map is activated in the game (as
// opposed to the mapper). It's always on. Turning it off produces nice color
// coded map with all objects and their types visible, however there is no way
// you can do it within the game UI.
AUTOMAP_IN_GAME = 0x01,
// High details is on.
AUTOMAP_WTH_HIGH_DETAILS = 0x02,
// Scanner is active.
AUTOMAP_WITH_SCANNER = 0x04,
} AutomapFlags;
typedef enum AutomapFrm {
AUTOMAP_FRM_BACKGROUND,
AUTOMAP_FRM_BUTTON_UP,
AUTOMAP_FRM_BUTTON_DOWN,
AUTOMAP_FRM_SWITCH_UP,
AUTOMAP_FRM_SWITCH_DOWN,
AUTOMAP_FRM_COUNT,
} AutomapFrm;
typedef struct AutomapHeader {
unsigned char version;
// The size of entire automap database (including header itself).
int dataSize;
// Offsets from the beginning of the automap database file into
// entries data.
//
// These offsets are specified for every map/elevation combination. A value
// of 0 specifies that there is no data for appropriate map/elevation
// combination.
int offsets[AUTOMAP_MAP_COUNT][ELEVATION_COUNT];
} AutomapHeader;
typedef struct AutomapEntry {
int dataSize;
unsigned char isCompressed;
unsigned char* compressedData;
unsigned char* data;
} AutomapEntry;
extern const int _defam[AUTOMAP_MAP_COUNT][ELEVATION_COUNT];
extern const int _displayMapList[AUTOMAP_MAP_COUNT];
extern const int gAutomapFrmIds[AUTOMAP_FRM_COUNT];
extern int gAutomapFlags;
extern AutomapHeader gAutomapHeader;
extern AutomapEntry gAutomapEntry;
int automapInit();
int automapReset();
void automapExit();
int automapLoad(File* stream);
int automapSave(File* stream);
int _automapDisplayMap(int map);
void automapShow(bool isInGame, bool isUsingScanner);
void automapRenderInMapWindow(int window, int elevation, unsigned char* backgroundData, int flags);
int automapRenderInPipboyWindow(int win, int map, int elevation);
int automapSaveCurrent();
int automapSaveEntry(File* stream);
int automapLoadEntry(int map, int elevation);
int automapSaveHeader(File* stream);
int automapLoadHeader(File* stream);
void _decode_map_data(int elevation);
int automapCreate();
int _copy_file_data(File* stream1, File* stream2, int length);
int automapGetHeader(AutomapHeader** automapHeaderPtr);
#endif /* AUTOMAP_H */

24
src/autorun.c Normal file
View File

@ -0,0 +1,24 @@
#include "autorun.h"
// 0x530010
HANDLE gInterplayGenericAutorunMutex;
// 0x4139C0
bool autorunMutexCreate()
{
gInterplayGenericAutorunMutex = CreateMutexA(NULL, FALSE, "InterplayGenericAutorunMutex");
if (GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(gInterplayGenericAutorunMutex);
return false;
}
return true;
}
// 0x413A00
void autorunMutexClose()
{
if (gInterplayGenericAutorunMutex != NULL) {
CloseHandle(gInterplayGenericAutorunMutex);
}
}

14
src/autorun.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef AUTORUN_H
#define AUTORUN_H
#include <stdbool.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
extern HANDLE gInterplayGenericAutorunMutex;
bool autorunMutexCreate();
void autorunMutexClose();
#endif /* AUTORUN_H */

598
src/cache.c Normal file
View File

@ -0,0 +1,598 @@
#include "cache.h"
#include "debug.h"
#include "memory.h"
#include "sound.h"
#include <limits.h>
#include <stdio.h>
#include <string.h>
static_assert(sizeof(CacheEntry) == 32, "wrong size");
static_assert(sizeof(Cache) == 84, "wrong size");
// 0x510938
int _lock_sound_ticker = 0;
// cache_init
// 0x41FCC0
bool cacheInit(Cache* cache, CacheSizeProc* sizeProc, CacheReadProc* readProc, CacheFreeProc* freeProc, int maxSize)
{
if (!heapInit(&(cache->heap), maxSize)) {
return false;
}
cache->size = 0;
cache->maxSize = maxSize;
cache->entriesLength = 0;
cache->entriesCapacity = CACHE_ENTRIES_INITIAL_CAPACITY;
cache->hits = 0;
cache->entries = internal_malloc(sizeof(*cache->entries) * cache->entriesCapacity);
cache->sizeProc = sizeProc;
cache->readProc = readProc;
cache->freeProc = freeProc;
if (cache->entries == NULL) {
return false;
}
memset(cache->entries, 0, sizeof(*cache->entries) * cache->entriesCapacity);
return true;
}
// cache_exit
// 0x41FD50
bool cacheFree(Cache* cache)
{
if (cache == NULL) {
return false;
}
cacheClean(cache);
cacheFlush(cache);
heapFree(&(cache->heap));
cache->size = 0;
cache->maxSize = 0;
cache->entriesLength = 0;
cache->entriesCapacity = 0;
cache->hits = 0;
if (cache->entries != NULL) {
internal_free(cache->entries);
cache->entries = NULL;
}
cache->sizeProc = NULL;
cache->readProc = NULL;
cache->freeProc = NULL;
return true;
}
// 0x41FDE8
bool cacheLock(Cache* cache, int key, void** data, CacheEntry** cacheEntryPtr)
{
if (cache == NULL || data == NULL || cacheEntryPtr == NULL) {
return false;
}
*cacheEntryPtr = NULL;
int index;
int rc = cacheFindIndexForKey(cache, key, &index);
if (rc == 2) {
// Use existing cache entry.
CacheEntry* cacheEntry = cache->entries[index];
cacheEntry->hits++;
} else if (rc == 3) {
// New cache entry is required.
if (cache->entriesLength >= INT_MAX) {
return false;
}
if (!cacheFetchEntryForKey(cache, key, &index)) {
return false;
}
_lock_sound_ticker %= 4;
if (_lock_sound_ticker == 0) {
soundContinueAll();
}
} else {
return false;
}
CacheEntry* cacheEntry = cache->entries[index];
if (cacheEntry->referenceCount == 0) {
if (!heapLock(&(cache->heap), cacheEntry->heapHandleIndex, &(cacheEntry->data))) {
return false;
}
}
cacheEntry->referenceCount++;
cache->hits++;
cacheEntry->mru = cache->hits;
if (cache->hits == UINT_MAX) {
cacheResetStatistics(cache);
}
*data = cacheEntry->data;
*cacheEntryPtr = cacheEntry;
return true;
}
// 0x4200B8
bool cacheUnlock(Cache* cache, CacheEntry* cacheEntry)
{
if (cache == NULL || cacheEntry == NULL) {
return false;
}
if (cacheEntry->referenceCount == 0) {
return false;
}
cacheEntry->referenceCount--;
if (cacheEntry->referenceCount == 0) {
heapUnlock(&(cache->heap), cacheEntry->heapHandleIndex);
}
return true;
}
// cache_flush
// 0x42012C
bool cacheFlush(Cache* cache)
{
if (cache == NULL) {
return false;
}
// Loop thru cache entries and mark those with no references for eviction.
for (int index = 0; index < cache->entriesLength; index++) {
CacheEntry* cacheEntry = cache->entries[index];
if (cacheEntry->referenceCount == 0) {
cacheEntry->flags |= CACHE_ENTRY_MARKED_FOR_EVICTION;
}
}
// Sweep cache entries marked earlier.
cacheSweep(cache);
// Shrink cache entries array if it's too big.
int optimalCapacity = cache->entriesLength + CACHE_ENTRIES_GROW_CAPACITY;
if (optimalCapacity < cache->entriesCapacity) {
cacheSetCapacity(cache, optimalCapacity);
}
return true;
}
// 0x42019C
bool cachePrintStats(Cache* cache, char* dest)
{
if (cache == NULL || dest == NULL) {
return false;
}
sprintf(dest, "Cache stats are disabled.%s", "\n");
return true;
}
// Fetches entry for the specified key into the cache.
//
// 0x4203AC
bool cacheFetchEntryForKey(Cache* cache, int key, int* indexPtr)
{
CacheEntry* cacheEntry = internal_malloc(sizeof(*cacheEntry));
if (cacheEntry == NULL) {
return false;
}
if (!cacheEntryInit(cacheEntry)) {
return false;
}
do {
int size;
if (cache->sizeProc(key, &size) != 0) {
break;
}
if (!cacheEnsureSize(cache, size)) {
break;
}
bool allocated = false;
int cacheEntrySize = size;
for (int attempt = 0; attempt < 10; attempt++) {
if (heapBlockAllocate(&(cache->heap), &(cacheEntry->heapHandleIndex), size, 1)) {
allocated = true;
break;
}
cacheEntrySize = (int)((double)cacheEntrySize + (double)size * 0.25);
if (cacheEntrySize > cache->maxSize) {
break;
}
if (!cacheEnsureSize(cache, cacheEntrySize)) {
break;
}
}
if (!allocated) {
cacheFlush(cache);
allocated = true;
if (!heapBlockAllocate(&(cache->heap), &(cacheEntry->heapHandleIndex), size, 1)) {
if (!heapBlockAllocate(&(cache->heap), &(cacheEntry->heapHandleIndex), size, 0)) {
allocated = false;
}
}
}
if (!allocated) {
break;
}
do {
if (!heapLock(&(cache->heap), cacheEntry->heapHandleIndex, &(cacheEntry->data))) {
break;
}
if (cache->readProc(key, &size, cacheEntry->data) != 0) {
break;
}
heapUnlock(&(cache->heap), cacheEntry->heapHandleIndex);
cacheEntry->size = size;
cacheEntry->key = key;
bool isNewKey = true;
if (*indexPtr < cache->entriesLength) {
if (key < cache->entries[*indexPtr]->key) {
if (*indexPtr == 0 || key > cache->entries[*indexPtr - 1]->key) {
isNewKey = false;
}
}
}
if (isNewKey) {
if (cacheFindIndexForKey(cache, key, indexPtr) != 3) {
break;
}
}
if (!cacheInsertEntryAtIndex(cache, cacheEntry, *indexPtr)) {
break;
}
return true;
} while (0);
heapUnlock(&(cache->heap), cacheEntry->heapHandleIndex);
} while (0);
// NOTE: Uninline.
cacheEntryFree(cache, cacheEntry);
return false;
}
// 0x4205E8
bool cacheInsertEntryAtIndex(Cache* cache, CacheEntry* cacheEntry, int index)
{
// Ensure cache have enough space for new entry.
if (cache->entriesLength == cache->entriesCapacity - 1) {
if (!cacheSetCapacity(cache, cache->entriesCapacity + CACHE_ENTRIES_GROW_CAPACITY)) {
return false;
}
}
// Move entries below insertion point.
memmove(&(cache->entries[index + 1]), &(cache->entries[index]), sizeof(*cache->entries) * (cache->entriesLength - index));
cache->entries[index] = cacheEntry;
cache->entriesLength++;
cache->size += cacheEntry->size;
return true;
}
// Finds index for given key.
//
// Returns 2 if entry already exists in cache, or 3 if entry does not exist. In
// this case indexPtr represents insertion point.
//
// 0x420654
int cacheFindIndexForKey(Cache* cache, int key, int* indexPtr)
{
int length = cache->entriesLength;
if (length == 0) {
*indexPtr = 0;
return 3;
}
int r = length - 1;
int l = 0;
int mid;
int cmp;
do {
mid = (l + r) / 2;
cmp = key - cache->entries[mid]->key;
if (cmp == 0) {
*indexPtr = mid;
return 2;
}
if (cmp > 0) {
l = l + 1;
} else {
r = r - 1;
}
} while (r >= l);
if (cmp < 0) {
*indexPtr = mid;
} else {
*indexPtr = mid + 1;
}
return 3;
}
// 0x420708
bool cacheEntryInit(CacheEntry* cacheEntry)
{
cacheEntry->key = 0;
cacheEntry->size = 0;
cacheEntry->data = NULL;
cacheEntry->referenceCount = 0;
cacheEntry->hits = 0;
cacheEntry->flags = 0;
cacheEntry->mru = 0;
return true;
}
// NOTE: Inlined.
//
// 0x420740
bool cacheEntryFree(Cache* cache, CacheEntry* cacheEntry)
{
if (cacheEntry->data != NULL) {
heapBlockDeallocate(&(cache->heap), &(cacheEntry->heapHandleIndex));
}
internal_free(cacheEntry);
return true;
}
// 0x420764
bool cacheClean(Cache* cache)
{
Heap* heap = &(cache->heap);
for (int index = 0; index < cache->entriesLength; index++) {
CacheEntry* cacheEntry = cache->entries[index];
// NOTE: Original code is slightly different. For unknown reason it uses
// inner loop to decrement `referenceCount` one by one. Probably using
// some inlined function.
if (cacheEntry->referenceCount != 0) {
heapUnlock(heap, cacheEntry->heapHandleIndex);
cacheEntry->referenceCount = 0;
}
}
return true;
}
// 0x4207D4
bool cacheResetStatistics(Cache* cache)
{
if (cache == NULL) {
return false;
}
CacheEntry** entries = internal_malloc(sizeof(*entries) * cache->entriesLength);
if (entries == NULL) {
return false;
}
memcpy(entries, cache->entries, sizeof(*entries) * cache->entriesLength);
qsort(entries, cache->entriesLength, sizeof(*entries), cacheEntriesCompareByMostRecentHit);
for (int index = 0; index < cache->entriesLength; index++) {
CacheEntry* cacheEntry = entries[index];
cacheEntry->mru = index;
}
cache->hits = cache->entriesLength;
// FIXME: Obviously leak `entries`.
return true;
}
// Prepare cache for storing new entry with the specified size.
//
// 0x42084C
bool cacheEnsureSize(Cache* cache, int size)
{
if (size > cache->maxSize) {
// The entry of given size is too big for caching, no matter what.
return false;
}
if (cache->maxSize - cache->size >= size) {
// There is space available for entry of given size, there is no need to
// evict anything.
return true;
}
CacheEntry** entries = internal_malloc(sizeof(*entries) * cache->entriesLength);
if (entries != NULL) {
memcpy(entries, cache->entries, sizeof(*entries) * cache->entriesLength);
qsort(entries, cache->entriesLength, sizeof(*entries), cacheEntriesCompareByUsage);
// The sweeping threshold is 20% of cache size plus size for the new
// entry. Once the threshold is reached the marking process stops.
int threshold = size + (int)((double)cache->size * 0.2);
int accum = 0;
int index;
for (index = 0; index < cache->entriesLength; index++) {
CacheEntry* entry = entries[index];
if (entry->referenceCount == 0) {
if (entry->size >= threshold) {
entry->flags |= CACHE_ENTRY_MARKED_FOR_EVICTION;
// We've just found one huge entry, there is no point to
// mark individual smaller entries in the code path below,
// reset the accumulator to skip it entirely.
accum = 0;
break;
} else {
accum += entry->size;
if (accum >= threshold) {
break;
}
}
}
}
if (accum != 0) {
// The loop below assumes index to be positioned on the entry, where
// accumulator stopped. If we've reached the end, reposition
// it to the last entry.
if (index == cache->entriesLength) {
index -= 1;
}
// Loop backwards from the point we've stopped and mark all
// unreferenced entries for sweeping.
for (; index >= 0; index--) {
CacheEntry* entry = entries[index];
if (entry->referenceCount == 0) {
entry->flags |= CACHE_ENTRY_MARKED_FOR_EVICTION;
}
}
}
internal_free(entries);
}
cacheSweep(cache);
if (cache->maxSize - cache->size >= size) {
return true;
}
return false;
}
// 0x42099C
bool cacheSweep(Cache* cache)
{
for (int index = 0; index < cache->entriesLength; index++) {
CacheEntry* cacheEntry = cache->entries[index];
if ((cacheEntry->flags & CACHE_ENTRY_MARKED_FOR_EVICTION) != 0) {
if (cacheEntry->referenceCount != 0) {
// Entry was marked for eviction but still has references,
// unmark it.
cacheEntry->flags &= ~CACHE_ENTRY_MARKED_FOR_EVICTION;
} else {
int cacheEntrySize = cacheEntry->size;
// NOTE: Uninline.
cacheEntryFree(cache, cacheEntry);
// Move entries up.
memmove(&(cache->entries[index]), &(cache->entries[index + 1]), sizeof(*cache->entries) * ((cache->entriesLength - index) - 1));
cache->entriesLength--;
cache->size -= cacheEntrySize;
// The entry was removed, compensate index.
index--;
}
}
}
return true;
}
// 0x420A40
bool cacheSetCapacity(Cache* cache, int newCapacity)
{
if (newCapacity < cache->entriesLength) {
return false;
}
CacheEntry** entries = internal_realloc(cache->entries, sizeof(*cache->entries) * newCapacity);
if (entries == NULL) {
return false;
}
cache->entries = entries;
cache->entriesCapacity = newCapacity;
return true;
}
// 0x420A74
int cacheEntriesCompareByUsage(const void* a1, const void* a2)
{
CacheEntry* v1 = *(CacheEntry**)a1;
CacheEntry* v2 = *(CacheEntry**)a2;
if (v1->referenceCount != 0 && v2->referenceCount == 0) {
return 1;
}
if (v2->referenceCount != 0 && v1->referenceCount == 0) {
return -1;
}
if (v1->hits < v2->hits) {
return -1;
} else if (v1->hits > v2->hits) {
return 1;
}
if (v1->mru < v2->mru) {
return -1;
} else if (v1->mru > v2->mru) {
return 1;
}
return 0;
}
// 0x420AE8
int cacheEntriesCompareByMostRecentHit(const void* a1, const void* a2)
{
CacheEntry* v1 = *(CacheEntry**)a1;
CacheEntry* v2 = *(CacheEntry**)a2;
if (v1->mru < v2->mru) {
return 1;
} else if (v1->mru > v2->mru) {
return -1;
} else {
return 0;
}
}

89
src/cache.h Normal file
View File

@ -0,0 +1,89 @@
#ifndef CACHE_H
#define CACHE_H
#include "heap.h"
#include <stdbool.h>
#define INVALID_CACHE_ENTRY ((CacheEntry*)-1)
// The initial number of cache entries in new cache.
#define CACHE_ENTRIES_INITIAL_CAPACITY (100)
// The number of cache entries added when cache capacity is reached.
#define CACHE_ENTRIES_GROW_CAPACITY (50)
typedef enum CacheEntryFlags {
// Specifies that cache entry has no references as should be evicted during
// the next sweep operation.
CACHE_ENTRY_MARKED_FOR_EVICTION = 0x01,
} CacheEntryFlags;
typedef int CacheSizeProc(int key, int* sizePtr);
typedef int CacheReadProc(int key, int* sizePtr, unsigned char* buffer);
typedef void CacheFreeProc(void* ptr);
typedef struct CacheEntry {
int key;
int size;
unsigned char* data;
unsigned int referenceCount;
// Total number of hits that this cache entry received during it's
// lifetime.
unsigned int hits;
CacheEntryFlags flags;
// The most recent hit in terms of cache hit counter. Used to track most
// recently used entries in eviction strategy.
unsigned int mru;
int heapHandleIndex;
} CacheEntry;
typedef struct Cache {
// Current size of entries in cache.
int size;
// Maximum size of entries in cache.
int maxSize;
// The length of `entries` array.
int entriesLength;
// The capacity of `entries` array.
int entriesCapacity;
// Total number of hits during cache lifetime.
unsigned int hits;
// List of cache entries.
CacheEntry** entries;
CacheSizeProc* sizeProc;
CacheReadProc* readProc;
CacheFreeProc* freeProc;
Heap heap;
} Cache;
bool cacheInit(Cache* cache, CacheSizeProc* sizeProc, CacheReadProc* readProc, CacheFreeProc* freeProc, int maxSize);
bool cacheFree(Cache* cache);
bool cacheLock(Cache* cache, int key, void** data, CacheEntry** cacheEntryPtr);
bool cacheUnlock(Cache* cache, CacheEntry* cacheEntry);
bool cacheFlush(Cache* cache);
bool cachePrintStats(Cache* cache, char* dest);
bool cacheFetchEntryForKey(Cache* cache, int key, int* indexPtr);
bool cacheInsertEntryAtIndex(Cache* cache, CacheEntry* cacheEntry, int index);
int cacheFindIndexForKey(Cache* cache, int key, int* indexPtr);
bool cacheEntryInit(CacheEntry* cacheEntry);
bool cacheEntryFree(Cache* cache, CacheEntry* cacheEntry);
bool cacheClean(Cache* cache);
bool cacheResetStatistics(Cache* cache);
bool cacheEnsureSize(Cache* cache, int size);
bool cacheSweep(Cache* cache);
bool cacheSetCapacity(Cache* cache, int newCapacity);
int cacheEntriesCompareByUsage(const void* a1, const void* a2);
int cacheEntriesCompareByMostRecentHit(const void* a1, const void* a2);
#endif /* CACHE_H */

6680
src/character_editor.c Normal file

File diff suppressed because it is too large Load Diff

313
src/character_editor.h Normal file
View File

@ -0,0 +1,313 @@
#ifndef CHARACTER_EDITOR_H
#define CHARACTER_EDITOR_H
#include "art.h"
#include "db.h"
#include "geometry.h"
#include "message.h"
#include "perk_defs.h"
#include "proto_types.h"
#include "skill_defs.h"
#include "stat_defs.h"
#include "trait_defs.h"
#define DIALOG_PICKER_NUM_OPTIONS max(PERK_COUNT, TRAIT_COUNT)
#define TOWN_REPUTATION_COUNT 19
#define ADDICTION_REPUTATION_COUNT 8
typedef enum EditorFolder {
EDITOR_FOLDER_PERKS,
EDITOR_FOLDER_KARMA,
EDITOR_FOLDER_KILLS,
} EditorFolder;
enum {
EDITOR_DERIVED_STAT_ARMOR_CLASS,
EDITOR_DERIVED_STAT_ACTION_POINTS,
EDITOR_DERIVED_STAT_CARRY_WEIGHT,
EDITOR_DERIVED_STAT_MELEE_DAMAGE,
EDITOR_DERIVED_STAT_DAMAGE_RESISTANCE,
EDITOR_DERIVED_STAT_POISON_RESISTANCE,
EDITOR_DERIVED_STAT_RADIATION_RESISTANCE,
EDITOR_DERIVED_STAT_SEQUENCE,
EDITOR_DERIVED_STAT_HEALING_RATE,
EDITOR_DERIVED_STAT_CRITICAL_CHANCE,
EDITOR_DERIVED_STAT_COUNT,
};
enum {
EDITOR_FIRST_PRIMARY_STAT,
EDITOR_HIT_POINTS = 43,
EDITOR_POISONED,
EDITOR_RADIATED,
EDITOR_EYE_DAMAGE,
EDITOR_CRIPPLED_RIGHT_ARM,
EDITOR_CRIPPLED_LEFT_ARM,
EDITOR_CRIPPLED_RIGHT_LEG,
EDITOR_CRIPPLED_LEFT_LEG,
EDITOR_FIRST_DERIVED_STAT,
EDITOR_FIRST_SKILL = EDITOR_FIRST_DERIVED_STAT + EDITOR_DERIVED_STAT_COUNT,
EDITOR_TAG_SKILL = EDITOR_FIRST_SKILL + SKILL_COUNT,
EDITOR_SKILLS,
EDITOR_OPTIONAL_TRAITS,
EDITOR_FIRST_TRAIT,
EDITOR_BUTTONS_COUNT = EDITOR_FIRST_TRAIT + TRAIT_COUNT,
};
enum {
EDITOR_GRAPHIC_BIG_NUMBERS,
EDITOR_GRAPHIC_AGE_MASK,
EDITOR_GRAPHIC_AGE_OFF,
EDITOR_GRAPHIC_DOWN_ARROW_OFF,
EDITOR_GRAPHIC_DOWN_ARROW_ON,
EDITOR_GRAPHIC_NAME_MASK,
EDITOR_GRAPHIC_NAME_ON,
EDITOR_GRAPHIC_NAME_OFF,
EDITOR_GRAPHIC_FOLDER_MASK, // mask for all three folders
EDITOR_GRAPHIC_SEX_MASK,
EDITOR_GRAPHIC_SEX_OFF,
EDITOR_GRAPHIC_SEX_ON,
EDITOR_GRAPHIC_SLIDER, // image containing small plus/minus buttons appeared near selected skill
EDITOR_GRAPHIC_SLIDER_MINUS_OFF,
EDITOR_GRAPHIC_SLIDER_MINUS_ON,
EDITOR_GRAPHIC_SLIDER_PLUS_OFF,
EDITOR_GRAPHIC_SLIDER_PLUS_ON,
EDITOR_GRAPHIC_SLIDER_TRANS_MINUS_OFF,
EDITOR_GRAPHIC_SLIDER_TRANS_MINUS_ON,
EDITOR_GRAPHIC_SLIDER_TRANS_PLUS_OFF,
EDITOR_GRAPHIC_SLIDER_TRANS_PLUS_ON,
EDITOR_GRAPHIC_UP_ARROW_OFF,
EDITOR_GRAPHIC_UP_ARROW_ON,
EDITOR_GRAPHIC_LITTLE_RED_BUTTON_UP,
EDITOR_GRAPHIC_LILTTLE_RED_BUTTON_DOWN,
EDITOR_GRAPHIC_AGE_ON,
EDITOR_GRAPHIC_AGE_BOX, // image containing right and left buttons with age stepper in the middle
EDITOR_GRAPHIC_ATTRIBOX, // ??? black image with two little arrows (up and down) in the right-top corner
EDITOR_GRAPHIC_ATTRIBWN, // ??? not sure where and when it's used
EDITOR_GRAPHIC_CHARWIN, // ??? looks like metal plate
EDITOR_GRAPHIC_DONE_BOX, // metal plate holding DONE button
EDITOR_GRAPHIC_FEMALE_OFF,
EDITOR_GRAPHIC_FEMALE_ON,
EDITOR_GRAPHIC_MALE_OFF,
EDITOR_GRAPHIC_MALE_ON,
EDITOR_GRAPHIC_NAME_BOX, // placeholder for name
EDITOR_GRAPHIC_LEFT_ARROW_UP,
EDITOR_GRAPHIC_LEFT_ARROW_DOWN,
EDITOR_GRAPHIC_RIGHT_ARROW_UP,
EDITOR_GRAPHIC_RIGHT_ARROW_DOWN,
EDITOR_GRAPHIC_BARARRWS, // ??? two arrows up/down with some strange knob at the top, probably for scrollbar
EDITOR_GRAPHIC_OPTIONS_BASE, // options metal plate
EDITOR_GRAPHIC_OPTIONS_BUTTON_OFF,
EDITOR_GRAPHIC_OPTIONS_BUTTON_ON,
EDITOR_GRAPHIC_KARMA_FOLDER_SELECTED, // all three folders with middle folder selected (karma)
EDITOR_GRAPHIC_KILLS_FOLDER_SELECTED, // all theee folders with right folder selected (kills)
EDITOR_GRAPHIC_PERKS_FOLDER_SELECTED, // all three folders with left folder selected (perks)
EDITOR_GRAPHIC_KARMAFDR_PLACEOLDER, // ??? placeholder for traits folder image <- this is comment from intrface.lst
EDITOR_GRAPHIC_TAG_SKILL_BUTTON_OFF,
EDITOR_GRAPHIC_TAG_SKILL_BUTTON_ON,
EDITOR_GRAPHIC_COUNT,
};
typedef struct KarmaEntry {
int gvar;
int art_num;
int name;
int description;
} KarmaEntry;
typedef struct GenericReputationEntry {
int threshold;
int name;
} GenericReputationEntry;
typedef struct TownReputationEntry {
int gvar;
int city;
} TownReputationEntry;
typedef struct STRUCT_56FCB0 {
int field_0;
char* field_4;
} STRUCT_56FCB0;
// TODO: Field order is probably wrong.
typedef struct KillInfo {
const char* name;
int killTypeId;
int kills;
} KillInfo;
extern int _grph_id[50];
extern const unsigned char _copyflag[EDITOR_GRAPHIC_COUNT];
extern const int word_431D3A[EDITOR_DERIVED_STAT_COUNT];
extern const int _StatYpos[7];
extern const int word_431D6C[EDITOR_DERIVED_STAT_COUNT];
extern char byte_431D93[64];
extern const int dword_431DD4[7];
extern const double dbl_50170B;
extern const double dbl_501713;
extern const double dbl_5018F0;
extern const double dbl_5019BE;
extern bool _bk_enable_0;
extern int _skill_cursor;
extern int _slider_y;
extern int characterEditorRemainingCharacterPoints;
extern KarmaEntry* gKarmaEntries;
extern int gKarmaEntriesLength;
extern GenericReputationEntry* gGenericReputationEntries;
extern int gGenericReputationEntriesLength;
extern const TownReputationEntry gTownReputationEntries[TOWN_REPUTATION_COUNT];
extern const int gAddictionReputationVars[ADDICTION_REPUTATION_COUNT];
extern const int gAddictionReputationFrmIds[ADDICTION_REPUTATION_COUNT];
extern int _folder_up_button;
extern int _folder_down_button;
extern char _folder_card_string[256];
extern int _skillsav[SKILL_COUNT];
extern MessageList editorMessageList;
extern STRUCT_56FCB0 _name_sort_list[DIALOG_PICKER_NUM_OPTIONS];
extern int _trait_bids[TRAIT_COUNT];
extern MessageListItem editorMessageListItem;
extern char _old_str1[48];
extern char _old_str2[48];
extern int _tag_bids[SKILL_COUNT];
extern char _name_save[32];
extern Size _GInfo[EDITOR_GRAPHIC_COUNT];
extern CacheEntry* _grph_key[EDITOR_GRAPHIC_COUNT];
extern unsigned char* _grphcpy[EDITOR_GRAPHIC_COUNT];
extern unsigned char* _grphbmp[EDITOR_GRAPHIC_COUNT];
extern int _folder_max_lines;
extern int _folder_line;
extern int _folder_card_fid;
extern int _folder_top_line;
extern char* _folder_card_title;
extern char* _folder_card_title2;
extern int _folder_yoffset;
extern int _folder_karma_top_line;
extern int _folder_highlight_line;
extern char* _folder_card_desc;
extern int _folder_ypos;
extern int _folder_kills_top_line;
extern int _folder_perk_top_line;
extern unsigned char* gEditorPerkBackgroundBuffer;
extern int gEditorPerkWindow;
extern int _SliderPlusID;
extern int _SliderNegID;
extern int _stat_bids_minus[7];
extern unsigned char* characterEditorWindowBuf;
extern int characterEditorWindowHandle;
extern int _stat_bids_plus[7];
extern unsigned char* gEditorPerkWindowBuffer;
extern CritterProtoData _dude_data;
extern unsigned char* characterEditorWindowBackgroundBuf;
extern int _cline;
extern int _oldsline;
extern int _upsent_points_back;
extern int _last_level;
extern int characterEditorWindowOldFont;
extern int _kills_count;
extern CacheEntry* _bck_key;
extern int _hp_back;
extern int _mouse_ypos;
extern int _mouse_xpos;
extern int characterEditorSelectedItem;
extern int characterEditorWindowSelectedFolder;
extern int _frstc_draw1;
extern int _crow;
extern int _frstc_draw2;
extern int _perk_back[PERK_COUNT];
extern unsigned int _repFtime;
extern unsigned int _frame_time;
extern int _old_tags;
extern int _last_level_back;
extern bool gCharacterEditorIsCreationMode;
extern int _tag_skill_back[NUM_TAGGED_SKILLS];
extern int _card_old_fid2;
extern int _card_old_fid1;
extern int _trait_back[3];
extern int _trait_count;
extern int _optrt_count;
extern int _temp_trait[3];
extern int _tagskill_count;
extern int _temp_tag_skill[NUM_TAGGED_SKILLS];
extern char _free_perk_back;
extern unsigned char _free_perk;
extern unsigned char _first_skill_list;
int _editor_design(bool isCreationMode);
int characterEditorWindowInit();
void characterEditorWindowFree();
void _CharEditInit();
int _get_input_str(int win, int cancelKeyCode, char* text, int maxLength, int x, int y, int textColor, int backgroundColor, int flags);
bool _isdoschar(int ch);
char* _strmfe(char* dest, const char* name, const char* ext);
void editorRenderFolders();
void editorRenderPerks();
int _kills_list_comp(const KillInfo* a, const KillInfo* b);
int editorRenderKills();
void characterEditorRenderBigNumber(int x, int y, int flags, int value, int previousValue, int windowHandle);
void editorRenderPcStats();
void editorRenderPrimaryStat(int stat, bool animate, int previousValue);
void editorRenderGender();
void editorRenderAge();
void editorRenderName();
void editorRenderSecondaryStats();
void editorRenderSkills(int a1);
void editorRenderDetails();
int characterEditorEditName();
void _PrintName(unsigned char* buf, int a2);
int characterEditorRunEditAgeDialog();
void characterEditorEditGender();
void characterEditorHandleIncDecPrimaryStat(int eventCode);
int _OptionWindow();
bool characterFileExists(const char* fname);
int characterPrintToFile(const char* fileName);
char* _AddSpaces(char* string, int length);
char* _AddDots(char* string, int length);
void _ResetScreen();
void _RegInfoAreas();
void _SavePlayer();
void _RestorePlayer();
char* _itostndn(int value, char* dest);
int _DrawCard(int graphicId, const char* name, const char* attributes, char* description);
void _FldrButton();
void _InfoButton(int eventCode);
void editorAdjustSkill(int a1);
void characterEditorToggleTaggedSkill(int skill);
void characterEditorWindowRenderTraits();
void characterEditorToggleOptionalTrait(int trait);
void editorRenderKarma();
int _editor_save(File* stream);
int _editor_load(File* stream);
void _editor_reset();
int _UpdateLevel();
void _RedrwDPrks();
int editorSelectPerk();
int _InputPDLoop(int count, void (*refreshProc)());
int _ListDPerks();
void _RedrwDMPrk();
bool editorHandleMutate();
void _RedrwDMTagSkl();
bool editorHandleTag();
void _ListNewTagSkills();
int _ListMyTraits(int a1);
int _name_sort_comp(const void* a1, const void* a2);
int _DrawCard2(int frmId, const char* name, const char* rank, char* description);
void _pop_perks();
int _is_supper_bonus();
int _folder_init();
void _folder_scroll(int direction);
void _folder_clear();
int _folder_print_seperator(const char* string);
bool _folder_print_line(const char* string);
bool editorDrawKillsEntry(const char* name, int kills);
int karmaInit();
void karmaFree();
int karmaEntryCompare(const void* a1, const void* a2);
int genericReputationInit();
void genericReputationFree();
int genericReputationCompare(const void* a1, const void* a2);
#endif /* CHARACTER_EDITOR_H */

940
src/character_selector.c Normal file
View File

@ -0,0 +1,940 @@
#include "character_selector.h"
#include "character_editor.h"
#include "color.h"
#include "core.h"
#include "critter.h"
#include "db.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_sound.h"
#include "memory.h"
#include "message.h"
#include "object.h"
#include "options.h"
#include "palette.h"
#include "proto.h"
#include "skill.h"
#include "stat.h"
#include "text_font.h"
#include "trait.h"
#include "window_manager.h"
#include <stdio.h>
#include <string.h>
// 0x51C84C
int gCurrentPremadeCharacter = PREMADE_CHARACTER_NARG;
// 0x51C850
PremadeCharacterDescription gPremadeCharacterDescriptions[PREMADE_CHARACTER_COUNT] = {
{ "premade\\combat", 201, "VID 208-197-88-125" },
{ "premade\\stealth", 202, "VID 208-206-49-229" },
{ "premade\\diplomat", 203, "VID 208-206-49-227" },
};
// 0x51C8D4
const int gPremadeCharacterCount = PREMADE_CHARACTER_COUNT;
// 0x51C7F8
int gCharacterSelectorWindow = -1;
// 0x51C7FC
unsigned char* gCharacterSelectorWindowBuffer = NULL;
// 0x51C800
unsigned char* gCharacterSelectorBackground = NULL;
// 0x51C804
int gCharacterSelectorWindowPreviousButton = -1;
// 0x51C808
CacheEntry* gCharacterSelectorWindowPreviousButtonUpFrmHandle = NULL;
// 0x51C80C
CacheEntry* gCharacterSelectorWindowPreviousButtonDownFrmHandle = NULL;
// 0x51C810
int gCharacterSelectorWindowNextButton = -1;
// 0x51C814
CacheEntry* gCharacterSelectorWindowNextButtonUpFrmHandle = NULL;
// 0x51C818
CacheEntry* gCharacterSelectorWindowNextButtonDownFrmHandle = NULL;
// 0x51C81C
int gCharacterSelectorWindowTakeButton = -1;
// 0x51C820
CacheEntry* gCharacterSelectorWindowTakeButtonUpFrmHandle = NULL;
// 0x51C824
CacheEntry* gCharacterSelectorWindowTakeButtonDownFrmHandle = NULL;
// 0x51C828
int gCharacterSelectorWindowModifyButton = -1;
// 0x51C82C
CacheEntry* gCharacterSelectorWindowModifyButtonUpFrmHandle = NULL;
// 0x51C830
CacheEntry* gCharacterSelectorWindowModifyButtonDownFrmHandle = NULL;
// 0x51C834
int gCharacterSelectorWindowCreateButton = -1;
// 0x51C838
CacheEntry* gCharacterSelectorWindowCreateButtonUpFrmHandle = NULL;
// 0x51C83C
CacheEntry* gCharacterSelectorWindowCreateButtonDownFrmHandle = NULL;
// 0x51C840
int gCharacterSelectorWindowBackButton = -1;
// 0x51C844
CacheEntry* gCharacterSelectorWindowBackButtonUpFrmHandle = NULL;
// 0x51C848
CacheEntry* gCharacterSelectorWindowBackButtonDownFrmHandle = NULL;
// 0x667764
unsigned char* gCharacterSelectorWindowTakeButtonUpFrmData;
// 0x667768
unsigned char* gCharacterSelectorWindowModifyButtonDownFrmData;
// 0x66776C
unsigned char* gCharacterSelectorWindowBackButtonUpFrmData;
// 0x667770
unsigned char* gCharacterSelectorWindowCreateButtonUpFrmData;
// 0x667774
unsigned char* gCharacterSelectorWindowModifyButtonUpFrmData;
// 0x667778
unsigned char* gCharacterSelectorWindowBackButtonDownFrmData;
// 0x66777C
unsigned char* gCharacterSelectorWindowCreateButtonDownFrmData;
// 0x667780
unsigned char* gCharacterSelectorWindowTakeButtonDownFrmData;
// 0x667784
unsigned char* gCharacterSelectorWindowNextButtonDownFrmData;
// 0x667788
unsigned char* gCharacterSelectorWindowNextButtonUpFrmData;
// 0x66778C
unsigned char* gCharacterSelectorWindowPreviousButtonUpFrmData;
// 0x667790
unsigned char* gCharacterSelectorWindowPreviousButtonDownFrmData;
// 0x4A71D0
int characterSelectorOpen()
{
if (!characterSelectorWindowInit()) {
return 0;
}
bool cursorWasHidden = cursorIsHidden();
if (cursorWasHidden) {
mouseShowCursor();
}
colorPaletteLoad("color.pal");
paletteFadeTo(_cmap);
int rc = 0;
bool done = false;
while (!done) {
if (_game_user_wants_to_quit != 0) {
break;
}
int keyCode = _get_input();
switch (keyCode) {
case KEY_MINUS:
case KEY_UNDERSCORE:
brightnessDecrease();
break;
case KEY_EQUAL:
case KEY_PLUS:
brightnessIncrease();
break;
case KEY_UPPERCASE_B:
case KEY_LOWERCASE_B:
case KEY_ESCAPE:
rc = 3;
done = true;
break;
case KEY_UPPERCASE_C:
case KEY_LOWERCASE_C:
_ResetPlayer();
if (_editor_design(1) == 0) {
rc = 2;
done = true;
}
break;
case KEY_UPPERCASE_M:
case KEY_LOWERCASE_M:
if (!_editor_design(1)) {
rc = 2;
done = true;
}
break;
case KEY_UPPERCASE_T:
case KEY_LOWERCASE_T:
rc = 2;
done = true;
break;
case KEY_F10:
showQuitConfirmationDialog();
break;
case KEY_ARROW_LEFT:
soundPlayFile("ib2p1xx1");
// FALLTHROUGH
case 500:
gCurrentPremadeCharacter -= 1;
if (gCurrentPremadeCharacter < 0) {
gCurrentPremadeCharacter = gPremadeCharacterCount - 1;
}
characterSelectorWindowRefresh();
break;
case KEY_ARROW_RIGHT:
soundPlayFile("ib2p1xx1");
// FALLTHROUGH
case 501:
gCurrentPremadeCharacter += 1;
if (gCurrentPremadeCharacter >= gPremadeCharacterCount) {
gCurrentPremadeCharacter = 0;
}
characterSelectorWindowRefresh();
break;
}
}
paletteFadeTo(gPaletteBlack);
characterSelectorWindowFree();
if (cursorWasHidden) {
mouseHideCursor();
}
return rc;
}
// 0x4A7468
bool characterSelectorWindowInit()
{
if (gCharacterSelectorWindow != -1) {
return false;
}
gCharacterSelectorWindow = windowCreate(0, 0, CS_WINDOW_WIDTH, CS_WINDOW_HEIGHT, _colorTable[0], 0);
if (gCharacterSelectorWindow == -1) {
goto err;
}
gCharacterSelectorWindowBuffer = windowGetBuffer(gCharacterSelectorWindow);
if (gCharacterSelectorWindowBuffer == NULL) {
goto err;
}
CacheEntry* backgroundFrmHandle;
int backgroundFid = buildFid(6, 174, 0, 0, 0);
unsigned char* backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle);
if (backgroundFrmData == NULL) {
goto err;
}
blitBufferToBuffer(backgroundFrmData,
CS_WINDOW_WIDTH,
CS_WINDOW_HEIGHT,
CS_WINDOW_WIDTH,
gCharacterSelectorWindowBuffer,
CS_WINDOW_WIDTH);
gCharacterSelectorBackground = internal_malloc(CS_WINDOW_BACKGROUND_WIDTH * CS_WINDOW_BACKGROUND_HEIGHT);
if (gCharacterSelectorBackground == NULL)
goto err;
blitBufferToBuffer(backgroundFrmData + CS_WINDOW_WIDTH * CS_WINDOW_BACKGROUND_Y + CS_WINDOW_BACKGROUND_X,
CS_WINDOW_BACKGROUND_WIDTH,
CS_WINDOW_BACKGROUND_HEIGHT,
CS_WINDOW_WIDTH,
gCharacterSelectorBackground,
CS_WINDOW_BACKGROUND_WIDTH);
artUnlock(backgroundFrmHandle);
int fid;
// Setup "Previous" button.
fid = buildFid(6, 122, 0, 0, 0);
gCharacterSelectorWindowPreviousButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowPreviousButtonUpFrmHandle);
if (gCharacterSelectorWindowPreviousButtonUpFrmData == NULL) {
goto err;
}
fid = buildFid(6, 123, 0, 0, 0);
gCharacterSelectorWindowPreviousButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowPreviousButtonDownFrmHandle);
if (gCharacterSelectorWindowPreviousButtonDownFrmData == NULL) {
goto err;
}
gCharacterSelectorWindowPreviousButton = buttonCreate(gCharacterSelectorWindow,
CS_WINDOW_PREVIOUS_BUTTON_X,
CS_WINDOW_PREVIOUS_BUTTON_Y,
20,
18,
-1,
-1,
-1,
500,
gCharacterSelectorWindowPreviousButtonUpFrmData,
gCharacterSelectorWindowPreviousButtonDownFrmData,
NULL,
0);
if (gCharacterSelectorWindowPreviousButton == -1) {
goto err;
}
buttonSetCallbacks(gCharacterSelectorWindowPreviousButton, _gsound_med_butt_press, _gsound_med_butt_release);
// Setup "Next" button.
fid = buildFid(6, 124, 0, 0, 0);
gCharacterSelectorWindowNextButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowNextButtonUpFrmHandle);
if (gCharacterSelectorWindowNextButtonUpFrmData == NULL) {
goto err;
}
fid = buildFid(6, 125, 0, 0, 0);
gCharacterSelectorWindowNextButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowNextButtonDownFrmHandle);
if (gCharacterSelectorWindowNextButtonDownFrmData == NULL) {
goto err;
}
gCharacterSelectorWindowNextButton = buttonCreate(gCharacterSelectorWindow,
CS_WINDOW_NEXT_BUTTON_X,
CS_WINDOW_NEXT_BUTTON_Y,
20,
18,
-1,
-1,
-1,
501,
gCharacterSelectorWindowNextButtonUpFrmData,
gCharacterSelectorWindowNextButtonDownFrmData,
NULL,
0);
if (gCharacterSelectorWindowNextButton == -1) {
goto err;
}
buttonSetCallbacks(gCharacterSelectorWindowNextButton, _gsound_med_butt_press, _gsound_med_butt_release);
// Setup "Take" button.
fid = buildFid(6, 8, 0, 0, 0);
gCharacterSelectorWindowTakeButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowTakeButtonUpFrmHandle);
if (gCharacterSelectorWindowTakeButtonUpFrmData == NULL) {
goto err;
}
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowTakeButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowTakeButtonDownFrmHandle);
if (gCharacterSelectorWindowTakeButtonDownFrmData == NULL) {
goto err;
}
gCharacterSelectorWindowTakeButton = buttonCreate(gCharacterSelectorWindow,
CS_WINDOW_TAKE_BUTTON_X,
CS_WINDOW_TAKE_BUTTON_Y,
15,
16,
-1,
-1,
-1,
KEY_LOWERCASE_T,
gCharacterSelectorWindowTakeButtonUpFrmData,
gCharacterSelectorWindowTakeButtonDownFrmData,
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowTakeButton == -1) {
goto err;
}
buttonSetCallbacks(gCharacterSelectorWindowTakeButton, _gsound_red_butt_press, _gsound_red_butt_release);
// Setup "Modify" button.
fid = buildFid(6, 8, 0, 0, 0);
gCharacterSelectorWindowModifyButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowModifyButtonUpFrmHandle);
if (gCharacterSelectorWindowModifyButtonUpFrmData == NULL)
goto err;
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowModifyButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowModifyButtonDownFrmHandle);
if (gCharacterSelectorWindowModifyButtonDownFrmData == NULL) {
goto err;
}
gCharacterSelectorWindowModifyButton = buttonCreate(gCharacterSelectorWindow,
CS_WINDOW_MODIFY_BUTTON_X,
CS_WINDOW_MODIFY_BUTTON_Y,
15,
16,
-1,
-1,
-1,
KEY_LOWERCASE_M,
gCharacterSelectorWindowModifyButtonUpFrmData,
gCharacterSelectorWindowModifyButtonDownFrmData,
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowModifyButton == -1) {
goto err;
}
buttonSetCallbacks(gCharacterSelectorWindowModifyButton, _gsound_red_butt_press, _gsound_red_butt_release);
// Setup "Create" button.
fid = buildFid(6, 8, 0, 0, 0);
gCharacterSelectorWindowCreateButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowCreateButtonUpFrmHandle);
if (gCharacterSelectorWindowCreateButtonUpFrmData == NULL) {
goto err;
}
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowCreateButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowCreateButtonDownFrmHandle);
if (gCharacterSelectorWindowCreateButtonDownFrmData == NULL) {
goto err;
}
gCharacterSelectorWindowCreateButton = buttonCreate(gCharacterSelectorWindow,
CS_WINDOW_CREATE_BUTTON_X,
CS_WINDOW_CREATE_BUTTON_Y,
15,
16,
-1,
-1,
-1,
KEY_LOWERCASE_C,
gCharacterSelectorWindowCreateButtonUpFrmData,
gCharacterSelectorWindowCreateButtonDownFrmData,
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowCreateButton == -1) {
goto err;
}
buttonSetCallbacks(gCharacterSelectorWindowCreateButton, _gsound_red_butt_press, _gsound_red_butt_release);
// Setup "Back" button.
fid = buildFid(6, 8, 0, 0, 0);
gCharacterSelectorWindowBackButtonUpFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowBackButtonUpFrmHandle);
if (gCharacterSelectorWindowBackButtonUpFrmData == NULL) {
goto err;
}
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowBackButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowBackButtonDownFrmHandle);
if (gCharacterSelectorWindowBackButtonDownFrmData == NULL) {
goto err;
}
gCharacterSelectorWindowBackButton = buttonCreate(gCharacterSelectorWindow,
CS_WINDOW_BACK_BUTTON_X,
CS_WINDOW_BACK_BUTTON_Y,
15,
16,
-1,
-1,
-1,
KEY_ESCAPE,
gCharacterSelectorWindowBackButtonUpFrmData,
gCharacterSelectorWindowBackButtonDownFrmData,
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowBackButton == -1) {
goto err;
}
buttonSetCallbacks(gCharacterSelectorWindowBackButton, _gsound_red_butt_press, _gsound_red_butt_release);
gCurrentPremadeCharacter = PREMADE_CHARACTER_NARG;
windowRefresh(gCharacterSelectorWindow);
if (!characterSelectorWindowRefresh()) {
goto err;
}
return true;
err:
characterSelectorWindowFree();
return false;
}
// 0x4A7AD4
void characterSelectorWindowFree()
{
if (gCharacterSelectorWindow == -1) {
return;
}
if (gCharacterSelectorWindowPreviousButton != -1) {
buttonDestroy(gCharacterSelectorWindowPreviousButton);
gCharacterSelectorWindowPreviousButton = -1;
}
if (gCharacterSelectorWindowPreviousButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowPreviousButtonDownFrmHandle);
gCharacterSelectorWindowPreviousButtonDownFrmHandle = NULL;
gCharacterSelectorWindowPreviousButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowPreviousButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowPreviousButtonUpFrmHandle);
gCharacterSelectorWindowPreviousButtonUpFrmHandle = NULL;
gCharacterSelectorWindowPreviousButtonUpFrmData = NULL;
}
if (gCharacterSelectorWindowNextButton != -1) {
buttonDestroy(gCharacterSelectorWindowNextButton);
gCharacterSelectorWindowNextButton = -1;
}
if (gCharacterSelectorWindowNextButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowNextButtonDownFrmHandle);
gCharacterSelectorWindowNextButtonDownFrmHandle = NULL;
gCharacterSelectorWindowNextButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowNextButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowNextButtonUpFrmHandle);
gCharacterSelectorWindowNextButtonUpFrmHandle = NULL;
gCharacterSelectorWindowNextButtonUpFrmData = NULL;
}
if (gCharacterSelectorWindowTakeButton != -1) {
buttonDestroy(gCharacterSelectorWindowTakeButton);
gCharacterSelectorWindowTakeButton = -1;
}
if (gCharacterSelectorWindowTakeButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowTakeButtonDownFrmHandle);
gCharacterSelectorWindowTakeButtonDownFrmHandle = NULL;
gCharacterSelectorWindowTakeButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowTakeButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowTakeButtonUpFrmHandle);
gCharacterSelectorWindowTakeButtonUpFrmHandle = NULL;
gCharacterSelectorWindowTakeButtonUpFrmData = NULL;
}
if (gCharacterSelectorWindowModifyButton != -1) {
buttonDestroy(gCharacterSelectorWindowModifyButton);
gCharacterSelectorWindowModifyButton = -1;
}
if (gCharacterSelectorWindowModifyButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowModifyButtonDownFrmHandle);
gCharacterSelectorWindowModifyButtonDownFrmHandle = NULL;
gCharacterSelectorWindowModifyButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowModifyButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowModifyButtonUpFrmHandle);
gCharacterSelectorWindowModifyButtonUpFrmHandle = NULL;
gCharacterSelectorWindowModifyButtonUpFrmData = NULL;
}
if (gCharacterSelectorWindowCreateButton != -1) {
buttonDestroy(gCharacterSelectorWindowCreateButton);
gCharacterSelectorWindowCreateButton = -1;
}
if (gCharacterSelectorWindowCreateButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowCreateButtonDownFrmHandle);
gCharacterSelectorWindowCreateButtonDownFrmHandle = NULL;
gCharacterSelectorWindowCreateButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowCreateButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowCreateButtonUpFrmHandle);
gCharacterSelectorWindowCreateButtonUpFrmHandle = NULL;
gCharacterSelectorWindowCreateButtonUpFrmData = NULL;
}
if (gCharacterSelectorWindowBackButton != -1) {
buttonDestroy(gCharacterSelectorWindowBackButton);
gCharacterSelectorWindowBackButton = -1;
}
if (gCharacterSelectorWindowBackButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowBackButtonDownFrmHandle);
gCharacterSelectorWindowBackButtonDownFrmHandle = NULL;
gCharacterSelectorWindowBackButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowBackButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowBackButtonUpFrmHandle);
gCharacterSelectorWindowBackButtonUpFrmHandle = NULL;
gCharacterSelectorWindowBackButtonUpFrmData = NULL;
}
if (gCharacterSelectorBackground != NULL) {
internal_free(gCharacterSelectorBackground);
gCharacterSelectorBackground = NULL;
}
windowDestroy(gCharacterSelectorWindow);
gCharacterSelectorWindow = -1;
}
// 0x4A7D58
bool characterSelectorWindowRefresh()
{
char path[FILENAME_MAX];
sprintf(path, "%s.gcd", gPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
if (_proto_dude_init(path) == -1) {
debugPrint("\n ** Error in dude init! **\n");
return false;
}
blitBufferToBuffer(gCharacterSelectorBackground,
CS_WINDOW_BACKGROUND_WIDTH,
CS_WINDOW_BACKGROUND_HEIGHT,
CS_WINDOW_BACKGROUND_WIDTH,
gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * CS_WINDOW_BACKGROUND_Y + CS_WINDOW_BACKGROUND_X,
CS_WINDOW_WIDTH);
bool success = false;
if (characterSelectorWindowRenderFace()) {
if (characterSelectorWindowRenderStats()) {
success = characterSelectorWindowRenderBio();
}
}
windowRefresh(gCharacterSelectorWindow);
return success;
}
// 0x4A7E08
bool characterSelectorWindowRenderFace()
{
bool success = false;
CacheEntry* faceFrmHandle;
int faceFid = buildFid(6, gPremadeCharacterDescriptions[gCurrentPremadeCharacter].face, 0, 0, 0);
Art* frm = artLock(faceFid, &faceFrmHandle);
if (frm != NULL) {
unsigned char* data = artGetFrameData(frm, 0, 0);
if (data != NULL) {
int width = artGetWidth(frm, 0, 0);
int height = artGetHeight(frm, 0, 0);
blitBufferToBufferTrans(data, width, height, width, (gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * 23 + 27), CS_WINDOW_WIDTH);
success = true;
}
artUnlock(faceFrmHandle);
}
return success;
}
// 0x4A7EA8
bool characterSelectorWindowRenderStats()
{
char* str;
char text[260];
int length;
int value;
MessageListItem messageListItem;
int oldFont = fontGetCurrent();
fontSetCurrent(101);
fontGetCharacterWidth(0x20);
int vh = fontGetLineHeight();
int y = 40;
// NAME
str = objectGetName(gDude);
strcpy(text, str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_NAME_MID_X - (length / 2), text, 160, CS_WINDOW_WIDTH, _colorTable[992]);
// STRENGTH
y += vh + vh + vh;
value = critterGetStat(gDude, STAT_STRENGTH);
str = statGetName(STAT_STRENGTH);
sprintf(text, "%s %02d", str, value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
str = statGetValueDescription(value);
sprintf(text, " %s", str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// PERCEPTION
y += vh;
value = critterGetStat(gDude, STAT_PERCEPTION);
str = statGetName(STAT_PERCEPTION);
sprintf(text, "%s %02d", str, value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
str = statGetValueDescription(value);
sprintf(text, " %s", str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// ENDURANCE
y += vh;
value = critterGetStat(gDude, STAT_ENDURANCE);
str = statGetName(STAT_PERCEPTION);
sprintf(text, "%s %02d", str, value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
str = statGetValueDescription(value);
sprintf(text, " %s", str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// CHARISMA
y += vh;
value = critterGetStat(gDude, STAT_CHARISMA);
str = statGetName(STAT_CHARISMA);
sprintf(text, "%s %02d", str, value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
str = statGetValueDescription(value);
sprintf(text, " %s", str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// INTELLIGENCE
y += vh;
value = critterGetStat(gDude, STAT_INTELLIGENCE);
str = statGetName(STAT_INTELLIGENCE);
sprintf(text, "%s %02d", str, value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
str = statGetValueDescription(value);
sprintf(text, " %s", str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// AGILITY
y += vh;
value = critterGetStat(gDude, STAT_AGILITY);
str = statGetName(STAT_AGILITY);
sprintf(text, "%s %02d", str, value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
str = statGetValueDescription(value);
sprintf(text, " %s", str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// LUCK
y += vh;
value = critterGetStat(gDude, STAT_LUCK);
str = statGetName(STAT_LUCK);
sprintf(text, "%s %02d", str, value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
str = statGetValueDescription(value);
sprintf(text, " %s", str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_PRIMARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
y += vh; // blank line
// HIT POINTS
y += vh;
messageListItem.num = 16;
text[0] = '\0';
if (messageListGetItem(&gMiscMessageList, &messageListItem)) {
strcpy(text, messageListItem.text);
}
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
value = critterGetStat(gDude, STAT_MAXIMUM_HIT_POINTS);
sprintf(text, " %d/%d", critterGetHitPoints(gDude), value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// ARMOR CLASS
y += vh;
str = statGetName(STAT_ARMOR_CLASS);
strcpy(text, str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
value = critterGetStat(gDude, STAT_ARMOR_CLASS);
sprintf(text, " %d", value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// ACTION POINTS
y += vh;
messageListItem.num = 15;
text[0] = '\0';
if (messageListGetItem(&gMiscMessageList, &messageListItem)) {
strcpy(text, messageListItem.text);
}
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
value = critterGetStat(gDude, STAT_MAXIMUM_ACTION_POINTS);
sprintf(text, " %d", value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
// MELEE DAMAGE
y += vh;
str = statGetName(STAT_ARMOR_CLASS);
strcpy(text, str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
value = critterGetStat(gDude, STAT_ARMOR_CLASS);
sprintf(text, " %d", value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
y += vh; // blank line
// SKILLS
int skills[DEFAULT_TAGGED_SKILLS];
skillsGetTagged(skills, DEFAULT_TAGGED_SKILLS);
for (int index = 0; index < DEFAULT_TAGGED_SKILLS; index++) {
y += vh;
str = skillGetName(skills[index]);
strcpy(text, str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
value = skillGetValue(gDude, skills[index]);
sprintf(text, " %d%%", value);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
}
// TRAITS
int traits[TRAITS_MAX_SELECTED_COUNT];
traitsGetSelected(&(traits[0]), &(traits[1]));
for (int index = 0; index < TRAITS_MAX_SELECTED_COUNT; index++) {
y += vh;
str = traitGetName(traits[index]);
strcpy(text, str);
length = fontGetStringWidth(text);
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_SECONDARY_STAT_MID_X - length, text, length, CS_WINDOW_WIDTH, _colorTable[992]);
}
fontSetCurrent(oldFont);
return true;
}
// 0x4A8AE4
bool characterSelectorWindowRenderBio()
{
int oldFont = fontGetCurrent();
fontSetCurrent(101);
char path[FILENAME_MAX];
sprintf(path, "%s.bio", gPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
File* stream = fileOpen(path, "rt");
if (stream != NULL) {
int y = 40;
int lineHeight = fontGetLineHeight();
char string[256];
while (fileReadString(string, 256, stream) && y < 260) {
fontDrawText(gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * y + CS_WINDOW_BIO_X, string, CS_WINDOW_WIDTH - CS_WINDOW_BIO_X, CS_WINDOW_WIDTH, _colorTable[992]);
y += lineHeight;
}
fileClose(stream);
}
fontSetCurrent(oldFont);
return true;
}

99
src/character_selector.h Normal file
View File

@ -0,0 +1,99 @@
#ifndef CHARACTER_SELECTOR_H
#define CHARACTER_SELECTOR_H
#include "art.h"
#include <stdbool.h>
#define CS_WINDOW_WIDTH (640)
#define CS_WINDOW_HEIGHT (480)
#define CS_WINDOW_BACKGROUND_X (40)
#define CS_WINDOW_BACKGROUND_Y (30)
#define CS_WINDOW_BACKGROUND_WIDTH (560)
#define CS_WINDOW_BACKGROUND_HEIGHT (300)
#define CS_WINDOW_PREVIOUS_BUTTON_X (292)
#define CS_WINDOW_PREVIOUS_BUTTON_Y (320)
#define CS_WINDOW_NEXT_BUTTON_X (318)
#define CS_WINDOW_NEXT_BUTTON_Y (320)
#define CS_WINDOW_TAKE_BUTTON_X (81)
#define CS_WINDOW_TAKE_BUTTON_Y (323)
#define CS_WINDOW_MODIFY_BUTTON_X (435)
#define CS_WINDOW_MODIFY_BUTTON_Y (320)
#define CS_WINDOW_CREATE_BUTTON_X (80)
#define CS_WINDOW_CREATE_BUTTON_Y (425)
#define CS_WINDOW_BACK_BUTTON_X (461)
#define CS_WINDOW_BACK_BUTTON_Y (425)
#define CS_WINDOW_NAME_MID_X (318)
#define CS_WINDOW_PRIMARY_STAT_MID_X (362)
#define CS_WINDOW_SECONDARY_STAT_MID_X (379)
#define CS_WINDOW_BIO_X (438)
typedef enum PremadeCharacter {
PREMADE_CHARACTER_NARG,
PREMADE_CHARACTER_CHITSA,
PREMADE_CHARACTER_MINGUN,
PREMADE_CHARACTER_COUNT,
} PremadeCharacter;
typedef struct PremadeCharacterDescription {
char fileName[20];
int face;
char field_18[20];
} PremadeCharacterDescription;
extern int gCurrentPremadeCharacter;
extern PremadeCharacterDescription gPremadeCharacterDescriptions[PREMADE_CHARACTER_COUNT];
extern const int gPremadeCharacterCount;
extern int gCharacterSelectorWindow;
extern unsigned char* gCharacterSelectorWindowBuffer;
extern unsigned char* gCharacterSelectorBackground;
extern int gCharacterSelectorWindowPreviousButton;
extern CacheEntry* gCharacterSelectorWindowPreviousButtonDownFrmHandle;
extern CacheEntry* gCharacterSelectorWindowPreviousButtonUpFrmHandle;
extern int gCharacterSelectorWindowNextButton;
extern CacheEntry* gCharacterSelectorWindowNextButtonUpFrmHandle;
extern CacheEntry* gCharacterSelectorWindowNextButtonDownFrmHandle;
extern int gCharacterSelectorWindowTakeButton;
extern CacheEntry* gCharacterSelectorWindowTakeButtonUpFrmHandle;
extern CacheEntry* gCharacterSelectorWindowTakeButtonDownFrmHandle;
extern int gCharacterSelectorWindowModifyButton;
extern CacheEntry* gCharacterSelectorWindowModifyButtonUpFrmHandle;
extern CacheEntry* gCharacterSelectorWindowModifyButtonDownFrmHandle;
extern int gCharacterSelectorWindowCreateButton;
extern CacheEntry* gCharacterSelectorWindowCreateButtonUpFrmHandle;
extern CacheEntry* gCharacterSelectorWindowCreateButtonDownFrmHandle;
extern int gCharacterSelectorWindowBackButton;
extern CacheEntry* gCharacterSelectorWindowBackButtonUpFrmHandle;
extern CacheEntry* gCharacterSelectorWindowBackButtonDownFrmHandle;
extern unsigned char* gCharacterSelectorWindowTakeButtonUpFrmData;
extern unsigned char* gCharacterSelectorWindowModifyButtonDownFrmData;
extern unsigned char* gCharacterSelectorWindowBackButtonUpFrmData;
extern unsigned char* gCharacterSelectorWindowCreateButtonUpFrmData;
extern unsigned char* gCharacterSelectorWindowModifyButtonUpFrmData;
extern unsigned char* gCharacterSelectorWindowBackButtonDownFrmData;
extern unsigned char* gCharacterSelectorWindowCreateButtonDownFrmData;
extern unsigned char* gCharacterSelectorWindowTakeButtonDownFrmData;
extern unsigned char* gCharacterSelectorWindowNextButtonDownFrmData;
extern unsigned char* gCharacterSelectorWindowNextButtonUpFrmData;
extern unsigned char* gCharacterSelectorWindowPreviousButtonUpFrmData;
extern unsigned char* gCharacterSelectorWindowPreviousButtonDownFrmData;
int characterSelectorOpen();
bool characterSelectorWindowInit();
void characterSelectorWindowFree();
bool characterSelectorWindowRefresh();
bool characterSelectorWindowRenderFace();
bool characterSelectorWindowRenderStats();
bool characterSelectorWindowRenderBio();
#endif /* CHARACTER_SELECTOR_H */

589
src/color.c Normal file
View File

@ -0,0 +1,589 @@
#include "color.h"
#include "core.h"
#include <string.h>
#include <math.h>
// 0x50F930
char _aColor_cNoError[] = "color.c: No errors\n";
// 0x50F95C
char _aColor_cColorTa[] = "color.c: color table not found\n";
// 0x51DF10
char* _errorStr = _aColor_cNoError;
// 0x51DF14
bool _colorsInited = false;
// 0x51DF18
double gBrightness = 1.0;
// 0x51DF20
ColorTransitionCallback* gColorPaletteTransitionCallback = NULL;
// 0x51DF24
MallocProc* gColorPaletteMallocProc = colorPaletteMallocDefaultImpl;
// 0x51DF28
ReallocProc* gColorPaletteReallocProc = colorPaletteReallocDefaultImpl;
// 0x51DF2C
FreeProc* gColorPaletteFreeProc = colorPaletteFreeDefaultImpl;
// 0x51DF30
ColorFileNameManger* gColorFileNameMangler = NULL;
// 0x51DF34
unsigned char _cmap[768] = {
0x3F, 0x3F, 0x3F
};
// 0x673090
unsigned char _systemCmap[256 * 3];
// 0x673390
unsigned char _currentGammaTable[64];
// 0x6733D0
unsigned char* _blendTable[256];
// 0x6737D0
unsigned char _mappedColor[256];
// 0x6738D0
unsigned char _colorMixAddTable[65536];
// 0x6838D0
unsigned char _intensityColorTable[65536];
// 0x6938D0
unsigned char _colorMixMulTable[65536];
// 0x6A38D0
unsigned char _colorTable[32768];
// 0x6AB928
ColorPaletteFileReadProc* gColorPaletteFileReadProc;
// 0x6AB92C
ColorPaletteCloseProc* gColorPaletteFileCloseProc;
// 0x6AB930
ColorPaletteFileOpenProc* gColorPaletteFileOpenProc;
// NOTE: Inlined.
//
// 0x4C7200
int colorPaletteFileOpen(const char* filePath, int flags)
{
if (gColorPaletteFileOpenProc != NULL) {
return gColorPaletteFileOpenProc(filePath, flags);
}
return -1;
}
// NOTE: Inlined.
//
// 0x4C7218
int colorPaletteFileRead(int fd, void* buffer, size_t size)
{
if (gColorPaletteFileReadProc != NULL) {
return gColorPaletteFileReadProc(fd, buffer, size);
}
return -1;
}
// NOTE: Inlined.
//
// 0x4C7230
int colorPaletteFileClose(int fd)
{
if (gColorPaletteFileCloseProc != NULL) {
return gColorPaletteFileCloseProc(fd);
}
return -1;
}
// 0x4C7248
void colorPaletteSetFileIO(ColorPaletteFileOpenProc* openProc, ColorPaletteFileReadProc* readProc, ColorPaletteCloseProc* closeProc)
{
gColorPaletteFileOpenProc = openProc;
gColorPaletteFileReadProc = readProc;
gColorPaletteFileCloseProc = closeProc;
}
// 0x4C725C
void* colorPaletteMallocDefaultImpl(size_t size)
{
return malloc(size);
}
// 0x4C7264
void* colorPaletteReallocDefaultImpl(void* ptr, size_t size)
{
return realloc(ptr, size);
}
// 0x4C726C
void colorPaletteFreeDefaultImpl(void* ptr)
{
free(ptr);
}
// 0x4C72B4
int _calculateColor(int a1, int a2)
{
int v1 = (a1 >> 9) + ((a2 & 0xFF) << 8);
return _intensityColorTable[v1];
}
// 0x4C72E0
int _Color2RGB_(int a1)
{
int v1, v2, v3;
v1 = _cmap[3 * a1] >> 1;
v2 = _cmap[3 * a1 + 1] >> 1;
v3 = _cmap[3 * a1 + 2] >> 1;
return (((v1 << 5) | v2) << 5) | v3;
}
// Performs animated palette transition.
//
// 0x4C7320
void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalette, int steps)
{
for (int step = 0; step < steps; step++) {
unsigned char palette[768];
for (int index = 0; index < 768; index++) {
palette[index] = oldPalette[index] - (oldPalette[index] - newPalette[index]) * step / steps;
}
if (gColorPaletteTransitionCallback != NULL) {
if (step % 128 == 0) {
gColorPaletteTransitionCallback();
}
}
_setSystemPalette(palette);
}
_setSystemPalette(newPalette);
}
// 0x4C73D4
void colorPaletteSetTransitionCallback(ColorTransitionCallback* callback)
{
gColorPaletteTransitionCallback = callback;
}
// 0x4C73E4
void _setSystemPalette(unsigned char* palette)
{
unsigned char newPalette[768];
for (int index = 0; index < 768; index++) {
newPalette[index] = _currentGammaTable[palette[index]];
_systemCmap[index] = palette[index];
}
directDrawSetPalette(newPalette);
}
// 0x4C7420
unsigned char* _getSystemPalette()
{
return _systemCmap;
}
// 0x4C7428
void _setSystemPaletteEntries(unsigned char* palette, int start, int end)
{
unsigned char newPalette[768];
int length = end - start + 1;
for (int index = 0; index < length; index++) {
newPalette[index * 3] = _currentGammaTable[palette[index * 3]];
newPalette[index * 3 + 1] = _currentGammaTable[palette[index * 3 + 1]];
newPalette[index * 3 + 2] = _currentGammaTable[palette[index * 3 + 2]];
_systemCmap[start * 3 + index * 3] = palette[index * 3];
_systemCmap[start * 3 + index * 3 + 1] = palette[index * 3 + 1];
_systemCmap[start * 3 + index * 3 + 2] = palette[index * 3 + 2];
}
directDrawSetPaletteInRange(newPalette, start, end - start + 1);
}
// 0x4C7550
void _setIntensityTableColor(int a1)
{
int v1, v2, v3, v4, v5, v6, v7, v8, v9, v10;
v5 = 0;
v10 = a1 << 8;
for (int index = 0; index < 128; index++) {
v1 = (_Color2RGB_(a1) & 0x7C00) >> 10;
v2 = (_Color2RGB_(a1) & 0x3E0) >> 5;
v3 = (_Color2RGB_(a1) & 0x1F);
v4 = (((v1 * v5) >> 16) << 10) | (((v2 * v5) >> 16) << 5) | ((v3 * v5) >> 16);
_intensityColorTable[index + v10] = _colorTable[v4];
v6 = v1 + (((0x1F - v1) * v5) >> 16);
v7 = v2 + (((0x1F - v2) * v5) >> 16);
v8 = v3 + (((0x1F - v3) * v5) >> 16);
v9 = (v6 << 10) | (v7 << 5) | v8;
_intensityColorTable[0x7F + index + 1 + v10] = _colorTable[v9];
v5 += 0x200;
}
}
// 0x4C7658
void _setIntensityTables()
{
for (int index = 0; index < 256; index++) {
if (_mappedColor[index] != 0) {
_setIntensityTableColor(index);
} else {
memset(_intensityColorTable + index * 256, 0, 256);
}
}
}
// 0x4C769C
void _setMixTableColor(int a1)
{
int i;
int v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19;
int v20, v21, v22, v23, v24, v25, v26, v27, v28, v29;
v1 = a1 << 8;
for (i = 0; i < 256; i++) {
if (_mappedColor[a1] && _mappedColor[i]) {
v2 = (_Color2RGB_(a1) & 0x7C00) >> 10;
v3 = (_Color2RGB_(a1) & 0x3E0) >> 5;
v4 = (_Color2RGB_(a1) & 0x1F);
v5 = (_Color2RGB_(i) & 0x7C00) >> 10;
v6 = (_Color2RGB_(i) & 0x3E0) >> 5;
v7 = (_Color2RGB_(i) & 0x1F);
v8 = v2 + v5;
v9 = v3 + v6;
v10 = v4 + v7;
v11 = v8;
if (v9 > v11) {
v11 = v9;
}
if (v10 > v11) {
v11 = v10;
}
if (v11 <= 0x1F) {
int paletteIndex = (v8 << 10) | (v9 << 5) | v10;
v12 = _colorTable[paletteIndex];
} else {
v13 = v11 - 0x1F;
v14 = v8 - v13;
v15 = v9 - v13;
v16 = v10 - v13;
if (v14 < 0) {
v14 = 0;
}
if (v15 < 0) {
v15 = 0;
}
if (v16 < 0) {
v16 = 0;
}
v17 = (v14 << 10) | (v15 << 5) | v16;
v18 = _colorTable[v17];
v19 = (int)((((double)v11 + (-31.0)) * 0.0078125 + 1.0) * 65536.0);
v12 = _calculateColor(v19, v18);
}
_colorMixAddTable[v1 + i] = v12;
v20 = (_Color2RGB_(a1) & 0x7C00) >> 10;
v21 = (_Color2RGB_(a1) & 0x3E0) >> 5;
v22 = (_Color2RGB_(a1) & 0x1F);
v23 = (_Color2RGB_(i) & 0x7C00) >> 10;
v24 = (_Color2RGB_(i) & 0x3E0) >> 5;
v25 = (_Color2RGB_(i) & 0x1F);
v26 = (v20 * v23) >> 5;
v27 = (v21 * v24) >> 5;
v28 = (v22 * v25) >> 5;
v29 = (v26 << 10) | (v27 << 5) | v28;
_colorMixMulTable[v1 + i] = _colorTable[v29];
} else {
if (_mappedColor[i]) {
_colorMixAddTable[v1 + i] = i;
_colorMixMulTable[v1 + i] = i;
} else {
_colorMixAddTable[v1 + i] = a1;
_colorMixMulTable[v1 + i] = a1;
}
}
}
}
// 0x4C78E4
bool colorPaletteLoad(char* path)
{
if (gColorFileNameMangler != NULL) {
path = gColorFileNameMangler(path);
}
// NOTE: Uninline.
int fd = colorPaletteFileOpen(path, 0x200);
if (fd == -1) {
_errorStr = _aColor_cColorTa;
return false;
}
for (int index = 0; index < 256; index++) {
unsigned char r;
unsigned char g;
unsigned char b;
// NOTE: Uninline.
colorPaletteFileRead(fd, &r, sizeof(r));
// NOTE: Uninline.
colorPaletteFileRead(fd, &g, sizeof(g));
// NOTE: Uninline.
colorPaletteFileRead(fd, &b, sizeof(b));
if (r <= 0x3F && g <= 0x3F && b <= 0x3F) {
_mappedColor[index] = 1;
} else {
r = 0;
g = 0;
b = 0;
_mappedColor[index] = 0;
}
_cmap[index * 3] = r;
_cmap[index * 3 + 1] = g;
_cmap[index * 3 + 2] = b;
}
// NOTE: Uninline.
colorPaletteFileRead(fd, _colorTable, 0x8000);
unsigned int type;
// NOTE: Uninline.
colorPaletteFileRead(fd, &type, sizeof(type));
// NOTE: The value is "NEWC". Original code uses cmp opcode, not stricmp,
// or comparing characters one-by-one.
if (type == 0x4E455743) {
// NOTE: Uninline.
colorPaletteFileRead(fd, _intensityColorTable, 0x10000);
// NOTE: Uninline.
colorPaletteFileRead(fd, _colorMixAddTable, 0x10000);
// NOTE: Uninline.
colorPaletteFileRead(fd, _colorMixMulTable, 0x10000);
} else {
_setIntensityTables();
for (int index = 0; index < 256; index++) {
_setMixTableColor(index);
}
}
_rebuildColorBlendTables();
// NOTE: Uninline.
colorPaletteFileClose(fd);
return true;
}
// 0x4C7AB4
char* _colorError()
{
return _errorStr;
}
// 0x4C7B44
void _buildBlendTable(unsigned char* ptr, unsigned char ch)
{
int r, g, b;
int i, j;
int v12, v14, v16;
unsigned char* beg;
beg = ptr;
r = (_Color2RGB_(ch) & 0x7C00) >> 10;
g = (_Color2RGB_(ch) & 0x3E0) >> 5;
b = (_Color2RGB_(ch) & 0x1F);
for (i = 0; i < 256; i++) {
ptr[i] = i;
}
ptr += 256;
int b_1 = b;
int v31 = 6;
int g_1 = g;
int r_1 = r;
int b_2 = b_1;
int g_2 = g_1;
int r_2 = r_1;
for (j = 0; j < 7; j++) {
for (i = 0; i < 256; i++) {
v12 = (_Color2RGB_(i) & 0x7C00) >> 10;
v14 = (_Color2RGB_(i) & 0x3E0) >> 5;
v16 = (_Color2RGB_(i) & 0x1F);
int index = 0;
index |= (r_2 + v12 * v31) / 7 << 10;
index |= (g_2 + v14 * v31) / 7 << 5;
index |= (b_2 + v16 * v31) / 7;
ptr[i] = _colorTable[index];
}
v31--;
ptr += 256;
r_2 += r_1;
g_2 += g_1;
b_2 += b_1;
}
int v18 = 0;
for (j = 0; j < 6; j++) {
int v20 = v18 / 7 + 0xFFFF;
for (i = 0; i < 256; i++) {
ptr[i] = _calculateColor(v20, ch);
}
v18 += 0x10000;
ptr += 256;
}
}
// 0x4C7D90
void _rebuildColorBlendTables()
{
int i;
for (i = 0; i < 256; i++) {
if (_blendTable[i]) {
_buildBlendTable(_blendTable[i], i);
}
}
}
// 0x4C7DC0
unsigned char* _getColorBlendTable(int ch)
{
unsigned char* ptr;
if (_blendTable[ch] == NULL) {
ptr = (unsigned char*)gColorPaletteMallocProc(4100);
*(int*)ptr = 1;
_blendTable[ch] = ptr + 4;
_buildBlendTable(_blendTable[ch], ch);
}
ptr = _blendTable[ch];
*(int*)((unsigned char*)ptr - 4) = *(int*)((unsigned char*)ptr - 4) + 1;
return ptr;
}
// 0x4C7E20
void _freeColorBlendTable(int a1)
{
unsigned char* v2 = _blendTable[a1];
if (v2 != NULL) {
int* count = (int*)(v2 - sizeof(int));
*count -= 1;
if (*count == 0) {
gColorPaletteFreeProc(count);
_blendTable[a1] = NULL;
}
}
}
// 0x4C7E58
void colorPaletteSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc)
{
gColorPaletteMallocProc = mallocProc;
gColorPaletteReallocProc = reallocProc;
gColorPaletteFreeProc = freeProc;
}
// 0x4C7E6C
void colorSetBrightness(double value)
{
gBrightness = value;
for (int i = 0; i < 64; i++) {
double value = pow(i, gBrightness);
_currentGammaTable[i] = (unsigned char)min(max(value, 0.0), 63.0);
}
_setSystemPalette(_systemCmap);
}
// 0x4C89CC
bool _initColors()
{
if (_colorsInited) {
return true;
}
_colorsInited = true;
colorSetBrightness(1.0);
if (!colorPaletteLoad("color.pal")) {
return false;
}
_setSystemPalette(_cmap);
return true;
}
// 0x4C8A18
void _colorsClose()
{
for (int index = 0; index < 256; index++) {
_freeColorBlendTable(index);
}
// TODO: Incomplete.
}

69
src/color.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef COLOR_H
#define COLOR_H
#include "memory_defs.h"
#include <stdbool.h>
#include <stdlib.h>
typedef char*(ColorFileNameManger)(char*);
typedef void(ColorTransitionCallback)();
typedef int(ColorPaletteFileOpenProc)(const char* path, int mode);
typedef int(ColorPaletteFileReadProc)(int fd, void* buffer, size_t size);
typedef int(ColorPaletteCloseProc)(int fd);
extern char _aColor_cNoError[];
extern char _aColor_cColorTa[];
extern char* _errorStr;
extern bool _colorsInited;
extern double gBrightness;
extern ColorTransitionCallback* gColorPaletteTransitionCallback;
extern MallocProc* gColorPaletteMallocProc;
extern ReallocProc* gColorPaletteReallocProc;
extern FreeProc* gColorPaletteFreeProc;
extern ColorFileNameManger* gColorFileNameMangler;
extern unsigned char _cmap[768];
extern unsigned char _systemCmap[256 * 3];
extern unsigned char _currentGammaTable[64];
extern unsigned char* _blendTable[256];
extern unsigned char _mappedColor[256];
extern unsigned char _colorMixAddTable[65536];
extern unsigned char _intensityColorTable[65536];
extern unsigned char _colorMixMulTable[65536];
extern unsigned char _colorTable[32768];
extern ColorPaletteFileReadProc* gColorPaletteFileReadProc;
extern ColorPaletteCloseProc* gColorPaletteFileCloseProc;
extern ColorPaletteFileOpenProc* gColorPaletteFileOpenProc;
int colorPaletteFileOpen(const char* filePath, int flags);
int colorPaletteFileRead(int fd, void* buffer, size_t size);
int colorPaletteFileClose(int fd);
void colorPaletteSetFileIO(ColorPaletteFileOpenProc* openProc, ColorPaletteFileReadProc* readProc, ColorPaletteCloseProc* closeProc);
void* colorPaletteMallocDefaultImpl(size_t size);
void* colorPaletteReallocDefaultImpl(void* ptr, size_t size);
void colorPaletteFreeDefaultImpl(void* ptr);
int _calculateColor(int a1, int a2);
int _Color2RGB_(int a1);
void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalette, int steps);
void colorPaletteSetTransitionCallback(ColorTransitionCallback* callback);
void _setSystemPalette(unsigned char* palette);
unsigned char* _getSystemPalette();
void _setSystemPaletteEntries(unsigned char* a1, int a2, int a3);
void _setIntensityTableColor(int a1);
void _setIntensityTables();
void _setMixTableColor(int a1);
bool colorPaletteLoad(char* path);
char* _colorError();
void _buildBlendTable(unsigned char* ptr, unsigned char ch);
void _rebuildColorBlendTables();
unsigned char* _getColorBlendTable(int ch);
void _freeColorBlendTable(int a1);
void colorPaletteSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc);
void colorSetBrightness(double value);
bool _initColors();
void _colorsClose();
#endif /* COLOR_H */

5762
src/combat.c Normal file

File diff suppressed because it is too large Load Diff

138
src/combat.h Normal file
View File

@ -0,0 +1,138 @@
#ifndef COMBAT_H
#define COMBAT_H
#include "animation.h"
#include "db.h"
#include "combat_defs.h"
#include "message.h"
#include "obj_types.h"
#include "party_member.h"
#include "proto_types.h"
#define CALLED_SHOW_WINDOW_X (68)
#define CALLED_SHOW_WINDOW_Y (20)
#define CALLED_SHOW_WINDOW_WIDTH (504)
#define CALLED_SHOW_WINDOW_HEIGHT (309)
extern char _a_1[];
extern int _combat_turn_running;
extern int _combatNumTurns;
extern unsigned int gCombatState;
extern STRUCT_510948* _aiInfoList;
extern STRUCT_664980* _gcsd;
extern bool _combat_call_display;
extern const int _hit_location_penalty[HIT_LOCATION_COUNT];
extern CriticalHitDescription gCriticalHitTables[KILL_TYPE_COUNT][HIT_LOCATION_COUNT][CRTICIAL_EFFECT_COUNT];
extern CriticalHitDescription gPlayerCriticalHitTable[HIT_LOCATION_COUNT][CRTICIAL_EFFECT_COUNT];
extern int _combat_end_due_to_load;
extern bool _combat_cleanup_enabled;
extern const int _cf_table[WEAPON_CRITICAL_FAILURE_TYPE_COUNT][WEAPON_CRITICAL_FAILURE_EFFECT_COUNT];
extern const int _call_ty[4];
extern const int _hit_loc_left[4];
extern const int _hit_loc_right[4];
extern Attack _main_ctd;
extern MessageList gCombatMessageList;
extern Object* gCalledShotCritter;
extern int gCalledShotWindow;
extern int _combat_elev;
extern int _list_total;
extern Object* _combat_ending_guy;
extern int _list_noncom;
extern Object* _combat_turn_obj;
extern int _combat_highlight;
extern Object** _combat_list;
extern int _list_com;
extern int _combat_exps;
extern int _combat_free_move;
extern Attack _shoot_ctd;
extern Attack _explosion_ctd;
int combatInit();
void combatReset();
void combatExit();
int _find_cid(int a1, int a2, Object** a3, int a4);
int combatLoad(File* stream);
int combatSave(File* stream);
bool _combat_safety_invalidate_weapon(Object* a1, Object* a2, int hitMode, Object* a4, int* a5);
bool _combat_safety_invalidate_weapon_func(Object* critter, Object* weapon, int hitMode, Object* a4, int* a5, Object* a6);
bool _combatTestIncidentalHit(Object* a1, Object* a2, Object* a3, Object* a4);
Object* _combat_whose_turn();
void _combat_data_init(Object* obj);
int _combatCopyAIInfo(int a1, int a2);
Object* _combatAIInfoGetFriendlyDead(Object* obj);
int _combatAIInfoSetFriendlyDead(Object* a1, Object* a2);
Object* _combatAIInfoGetLastTarget(Object* obj);
int _combatAIInfoSetLastTarget(Object* a1, Object* a2);
Object* _combatAIInfoGetLastItem(Object* obj);
int _combatAIInfoSetLastItem(Object* obj, Object* a2);
void _combat_begin(Object* a1);
void _combat_begin_extra(Object* a1);
void _combat_update_critter_outline_for_los(Object* critter, bool a2);
void _combat_over();
void _combat_over_from_load();
void _combat_give_exps(int exp_points);
void _combat_add_noncoms();
int _compare_faster(const void* a1, const void* a2);
void _combat_sequence_init(Object* a1, Object* a2);
void _combat_sequence();
void combatAttemptEnd();
void _combat_turn_run();
int _combat_input();
void _combat_set_move_all();
int _combat_turn(Object* a1, bool a2);
bool _combat_should_end();
void _combat(STRUCT_664980* attack);
void attackInit(Attack* attack, Object* a2, Object* a3, int a4, int a5);
int _combat_attack(Object* a1, Object* a2, int a3, int a4);
int _combat_bullet_start(const Object* a1, const Object* a2);
bool _check_ranged_miss(Attack* attack);
int _shoot_along_path(Attack* attack, int a2, int a3, int anim);
int _compute_spray(Attack* attack, int accuracy, int* a3, int* a4, int anim);
int attackComputeEnhancedKnockout(Attack* attack);
int attackCompute(Attack* attack);
void _compute_explosion_on_extras(Attack* attack, int a2, int a3, int a4);
int attackComputeCriticalHit(Attack* a1);
int _attackFindInvalidFlags(Object* a1, Object* a2);
int attackComputeCriticalFailure(Attack* attack);
int _determine_to_hit(Object* a1, Object* a2, int hitLocation, int hitMode);
int _determine_to_hit_no_range(Object* a1, Object* a2, int a3, int a4, unsigned char* a5);
int _determine_to_hit_from_tile(Object* a1, int a2, Object* a3, int a4, int a5);
int attackDetermineToHit(Object* attacker, int tile, Object* defender, int hitLocation, int hitMode, int a6);
void attackComputeDamage(Attack* attack, int ammoQuantity, int a3);
void attackComputeDeathFlags(Attack* attack);
void _apply_damage(Attack* attack, bool animated);
void _check_for_death(Object* a1, int a2, int* a3);
void _set_new_results(Object* a1, int a2);
void _damage_object(Object* a1, int damage, bool animated, int a4, Object* a5);
void _combat_display(Attack* attack);
void combatCopyDamageAmountDescription(char* dest, Object* critter_obj, int damage);
void combatAddDamageFlagsDescription(char* a1, int flags, Object* a3);
void _combat_anim_begin();
void _combat_anim_finished();
void _combat_standup(Object* a1);
void _print_tohit(unsigned char* dest, int dest_pitch, int a3);
char* hitLocationGetName(Object* critter, int hitLocation);
void _draw_loc_off(int a1, int a2);
void _draw_loc_on_(int a1, int a2);
void _draw_loc_(int eventCode, int color);
int calledShotSelectHitLocation(Object* critter, int* hitLocation, int hitMode);
int _combat_check_bad_shot(Object* attacker, Object* defender, int hitMode, bool aiming);
bool _combat_to_hit(Object* target, int* accuracy);
void _combat_attack_this(Object* a1);
void _combat_outline_on();
void _combat_outline_off();
void _combat_highlight_change();
bool _combat_is_shot_blocked(Object* a1, int from, int to, Object* a4, int* a5);
int _combat_player_knocked_out_by();
int _combat_explode_scenery(Object* a1, Object* a2);
void _combat_delete_critter(Object* obj);
void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
static inline bool isInCombat()
{
return (gCombatState & COMBAT_STATE_0x01) != 0;
}
#endif /* COMBAT_H */

3349
src/combat_ai.c Normal file

File diff suppressed because it is too large Load Diff

182
src/combat_ai.h Normal file
View File

@ -0,0 +1,182 @@
#ifndef COMBAT_AI_H
#define COMBAT_AI_H
#include "combat_ai_defs.h"
#include "combat_defs.h"
#include "db.h"
#include "message.h"
#include "obj_types.h"
#include "party_member.h"
#include <stdbool.h>
#define AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT (3)
typedef enum AiMessageType {
AI_MESSAGE_TYPE_RUN,
AI_MESSAGE_TYPE_MOVE,
AI_MESSAGE_TYPE_ATTACK,
AI_MESSAGE_TYPE_MISS,
AI_MESSAGE_TYPE_HIT,
} AiMessageType;
typedef struct AiMessageRange {
int start;
int end;
} AiMessageRange;
typedef struct AiPacket {
char* name;
int packet_num;
int max_dist;
int min_to_hit;
int min_hp;
int aggression;
int hurt_too_much;
int secondary_freq;
int called_freq;
int font;
int color;
int outline_color;
int chance;
AiMessageRange run;
AiMessageRange move;
AiMessageRange attack;
AiMessageRange miss;
AiMessageRange hit[HIT_LOCATION_SPECIFIC_COUNT];
int area_attack_mode;
int run_away_mode;
int best_weapon;
int distance;
int attack_who;
int chem_use;
int chem_primary_desire[AI_PACKET_CHEM_PRIMARY_DESIRE_COUNT];
int disposition;
char* body_type;
char* general_type;
} AiPacket;
typedef struct STRUCT_832 {
Object* field_0;
Object* field_4;
Object* field_8[100];
int field_198[100];
int field_328;
int field_32C;
int field_330;
int field_334;
int* field_338;
int field_33C;
int field_340;
} STRUCT_832;
extern Object* _combat_obj;
extern int gAiPacketsLength;
extern AiPacket* gAiPackets;
extern bool gAiInitialized;
extern const char* gAreaAttackModeKeys[AREA_ATTACK_MODE_COUNT];
extern const char* gAttackWhoKeys[ATTACK_WHO_COUNT];
extern const char* gBestWeaponKeys[BEST_WEAPON_COUNT];
extern const char* gChemUseKeys[CHEM_USE_COUNT];
extern const char* gDistanceModeKeys[DISTANCE_COUNT];
extern const char* gRunAwayModeKeys[RUN_AWAY_MODE_COUNT];
extern const char* gDispositionKeys[DISPOSITION_COUNT];
extern const char* gHurtTooMuchKeys[HURT_COUNT];
extern const int _rmatchHurtVals[5];
extern const int _hp_run_away_value[6];
extern Object* _attackerTeamObj;
extern Object* _targetTeamObj;
extern const int _weapPrefOrderings[BEST_WEAPON_COUNT + 1][5];
extern const int _aiPartyMemberDistances[DISTANCE_COUNT];
extern int gLanguageFilter;
extern MessageList gCombatAiMessageList;
extern char _target_str[260];
extern int _curr_crit_num;
extern Object** _curr_crit_list;
extern char _attack_str[268];
void _parse_hurt_str(char* str, int* out_value);
int _cai_match_str_to_list(const char* str, const char** list, int count, int* out_value);
void aiPacketInit(AiPacket* ai);
int aiInit();
void aiReset();
int aiExit();
int aiLoad(File* stream);
int aiSave(File* stream);
int aiPacketRead(File* stream, AiPacket* ai);
int aiPacketWrite(File* stream, AiPacket* ai);
AiPacket* aiGetPacket(Object* obj);
AiPacket* aiGetPacketByNum(int aiPacketNum);
int aiGetAreaAttackMode(Object* obj);
int aiGetRunAwayMode(Object* obj);
int aiGetBestWeapon(Object* obj);
int aiGetDistance(Object* obj);
int aiGetAttackWho(Object* obj);
int aiGetChemUse(Object* obj);
int aiSetAreaAttackMode(Object* critter, int areaAttackMode);
int aiSetRunAwayMode(Object* obj, int run_away_mode);
int aiSetBestWeapon(Object* critter, int bestWeapon);
int aiSetDistance(Object* critter, int distance);
int aiSetAttackWho(Object* critter, int attackWho);
int aiSetChemUse(Object* critter, int chemUse);
int aiGetDisposition(Object* obj);
int aiSetDisposition(Object* obj, int a2);
int _ai_magic_hands(Object* a1, Object* a2, int num);
int _ai_check_drugs(Object* critter);
void _ai_run_away(Object* a1, Object* a2);
int _ai_move_away(Object* a1, Object* a2, int a3);
bool _ai_find_friend(Object* a1, int a2, int a3);
int _compare_nearer(const void* a1, const void* a2);
int _compare_strength(const void* p1, const void* p2);
int _compare_weakness(const void* p1, const void* p2);
Object* _ai_find_nearest_team(Object* a1, Object* a2, int a3);
Object* _ai_find_nearest_team_in_combat(Object* a1, Object* a2, int a3);
int _ai_find_attackers(Object* a1, Object** a2, Object** a3, Object** a4);
Object* _ai_danger_source(Object* a1);
int _caiSetupTeamCombat(Object* a1, Object* a2);
int _caiTeamCombatInit(Object** a1, int a2);
void _caiTeamCombatExit();
int _ai_have_ammo(Object* critter_obj, Object* weapon_obj, Object** out_ammo_obj);
bool _caiHasWeapPrefType(AiPacket* ai, int attackType);
Object* _ai_best_weapon(Object* a1, Object* a2, Object* a3, Object* a4);
bool _ai_can_use_weapon(Object* critter, Object* weapon, int hitMode);
Object* _ai_search_inven_weap(Object* critter, int a2, Object* a3);
Object* _ai_search_inven_armor(Object* critter);
bool aiCanUseItem(Object* obj, Object* a2);
Object* _ai_search_environ(Object* critter, int itemType);
Object* _ai_retrieve_object(Object* a1, Object* a2);
int _ai_pick_hit_mode(Object* a1, Object* a2, Object* a3);
int _ai_move_steps_closer(Object* a1, Object* a2, int actionPoints, int a4);
int _cai_retargetTileFromFriendlyFire(Object* a1, Object* a2, int* a3);
int _cai_retargetTileFromFriendlyFireSubFunc(STRUCT_832* a1, int a2);
bool _cai_attackWouldIntersect(Object* a1, Object* a2, Object* a3, int tile, int* distance);
int _ai_switch_weapons(Object* a1, int* hitMode, Object** weapon, Object* a4);
int _ai_called_shot(Object* a1, Object* a2, int a3);
int _ai_attack(Object* a1, Object* a2, int a3);
int _ai_try_attack(Object* a1, Object* a2);
int _cAIPrepWeaponItem(Object* critter, Object* item);
void _cai_attempt_w_reload(Object* critter_obj, int a2);
void _combat_ai_begin(int a1, void* a2);
void _combat_ai_over();
int _cai_perform_distance_prefs(Object* a1, Object* a2);
int _cai_get_min_hp(AiPacket* ai);
void _combat_ai(Object* a1, Object* a2);
bool _combatai_want_to_join(Object* a1);
bool _combatai_want_to_stop(Object* a1);
int critterSetTeam(Object* obj, int team);
int critterSetAiPacket(Object* object, int aiPacket);
int _combatai_msg(Object* a1, Attack* attack, int a3, int a4);
int _ai_print_msg(Object* critter, int type);
Object* _combat_ai_random_target(Attack* attack);
int _combatai_rating(Object* obj);
int _combatai_check_retaliation(Object* a1, Object* a2);
bool objectCanHearObject(Object* a1, Object* a2);
int aiMessageListInit();
int aiMessageListFree();
void aiMessageListReloadIfNeeded();
void _combatai_notify_onlookers(Object* a1);
void _combatai_notify_friends(Object* a1);
void _combatai_delete_critter(Object* obj);
#endif /* COMBAT_AI_H */

82
src/combat_ai_defs.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef COMBAT_AI_DEFS_H
#define COMBAT_AI_DEFS_H
typedef enum AreaAttackMode {
AREA_ATTACK_MODE_ALWAYS,
AREA_ATTACK_MODE_SOMETIMES,
AREA_ATTACK_MODE_BE_SURE,
AREA_ATTACK_MODE_BE_CAREFUL,
AREA_ATTACK_MODE_BE_ABSOLUTELY_SURE,
AREA_ATTACK_MODE_COUNT,
} AreaAttackMode;
typedef enum RunAwayMode {
RUN_AWAY_MODE_NONE,
RUN_AWAY_MODE_COWARD,
RUN_AWAY_MODE_FINGER_HURTS,
RUN_AWAY_MODE_BLEEDING,
RUN_AWAY_MODE_NOT_FEELING_GOOD,
RUN_AWAY_MODE_TOURNIQUET,
RUN_AWAY_MODE_NEVER,
RUN_AWAY_MODE_COUNT,
} RunAwayMode;
typedef enum BestWeapon {
BEST_WEAPON_NO_PREF,
BEST_WEAPON_MELEE,
BEST_WEAPON_MELEE_OVER_RANGED,
BEST_WEAPON_RANGED_OVER_MELEE,
BEST_WEAPON_RANGED,
BEST_WEAPON_UNARMED,
BEST_WEAPON_UNARMED_OVER_THROW,
BEST_WEAPON_RANDOM,
BEST_WEAPON_COUNT,
} BestWeapon;
typedef enum DistanceMode {
DISTANCE_STAY_CLOSE,
DISTANCE_CHARGE,
DISTANCE_SNIPE,
DISTANCE_ON_YOUR_OWN,
DISTANCE_STAY,
DISTANCE_COUNT,
} DistanceMode;
typedef enum AttackWho {
ATTACK_WHO_WHOMEVER_ATTACKING_ME,
ATTACK_WHO_STRONGEST,
ATTACK_WHO_WEAKEST,
ATTACK_WHO_WHOMEVER,
ATTACK_WHO_CLOSEST,
ATTACK_WHO_COUNT,
} AttackWho;
typedef enum ChemUse {
CHEM_USE_CLEAN,
CHEM_USE_STIMS_WHEN_HURT_LITTLE,
CHEM_USE_STIMS_WHEN_HURT_LOTS,
CHEM_USE_SOMETIMES,
CHEM_USE_ANYTIME,
CHEM_USE_ALWAYS,
CHEM_USE_COUNT,
} ChemUse;
typedef enum Disposition {
DISPOSITION_NONE,
DISPOSITION_CUSTOM,
DISPOSITION_COWARD,
DISPOSITION_DEFENSIVE,
DISPOSITION_AGGRESSIVE,
DISPOSITION_BERKSERK,
DISPOSITION_COUNT,
} Disposition;
typedef enum HurtTooMuch {
HURT_BLIND,
HURT_CRIPPLED,
HURT_CRIPPLED_LEGS,
HURT_CRIPPLED_ARMS,
HURT_COUNT,
} HurtTooMuch;
#endif /* COMBAT_AI_DEFS_H */

156
src/combat_defs.h Normal file
View File

@ -0,0 +1,156 @@
#ifndef COMBAT_DEFS_H
#define COMBAT_DEFS_H
#include "obj_types.h"
#define EXPLOSION_TARGET_COUNT (6)
#define CRTICIAL_EFFECT_COUNT (6)
#define WEAPON_CRITICAL_FAILURE_TYPE_COUNT (7)
#define WEAPON_CRITICAL_FAILURE_EFFECT_COUNT (5)
typedef enum CombatState {
COMBAT_STATE_0x01 = 0x01,
COMBAT_STATE_0x02 = 0x02,
COMBAT_STATE_0x08 = 0x08,
} CombatState;
typedef enum HitMode {
HIT_MODE_LEFT_WEAPON_PRIMARY = 0,
HIT_MODE_LEFT_WEAPON_SECONDARY = 1,
HIT_MODE_RIGHT_WEAPON_PRIMARY = 2,
HIT_MODE_RIGHT_WEAPON_SECONDARY = 3,
HIT_MODE_PUNCH = 4,
HIT_MODE_KICK = 5,
HIT_MODE_LEFT_WEAPON_RELOAD = 6,
HIT_MODE_RIGHT_WEAPON_RELOAD = 7,
// Punch Level 2
HIT_MODE_STRONG_PUNCH = 8,
// Punch Level 3
HIT_MODE_HAMMER_PUNCH = 9,
// Punch Level 4 aka 'Lightning Punch'
HIT_MODE_HAYMAKER = 10,
// Punch Level 5 aka 'Chop Punch'
HIT_MODE_JAB = 11,
// Punch Level 6 aka 'Dragon Punch'
HIT_MODE_PALM_STRIKE = 12,
// Punch Level 7 aka 'Force Punch'
HIT_MODE_PIERCING_STRIKE = 13,
// Kick Level 2
HIT_MODE_STRONG_KICK = 14,
// Kick Level 3
HIT_MODE_SNAP_KICK = 15,
// Kick Level 4 aka 'Roundhouse Kick'
HIT_MODE_POWER_KICK = 16,
// Kick Level 5
HIT_MODE_HIP_KICK = 17,
// Kick Level 6 aka 'Jump Kick'
HIT_MODE_HOOK_KICK = 18,
// Kick Level 7 aka 'Death Blossom Kick'
HIT_MODE_PIERCING_KICK = 19,
HIT_MODE_COUNT,
FIRST_ADVANCED_PUNCH_HIT_MODE = HIT_MODE_STRONG_PUNCH,
LAST_ADVANCED_PUNCH_HIT_MODE = HIT_MODE_PIERCING_STRIKE,
FIRST_ADVANCED_KICK_HIT_MODE = HIT_MODE_STRONG_KICK,
LAST_ADVANCED_KICK_HIT_MODE = HIT_MODE_PIERCING_KICK,
FIRST_ADVANCED_UNARMED_HIT_MODE = FIRST_ADVANCED_PUNCH_HIT_MODE,
LAST_ADVANCED_UNARMED_HIT_MODE = LAST_ADVANCED_KICK_HIT_MODE,
} HitMode;
typedef enum HitLocation {
HIT_LOCATION_HEAD,
HIT_LOCATION_LEFT_ARM,
HIT_LOCATION_RIGHT_ARM,
HIT_LOCATION_TORSO,
HIT_LOCATION_RIGHT_LEG,
HIT_LOCATION_LEFT_LEG,
HIT_LOCATION_EYES,
HIT_LOCATION_GROIN,
HIT_LOCATION_UNCALLED,
HIT_LOCATION_COUNT,
HIT_LOCATION_SPECIFIC_COUNT = HIT_LOCATION_COUNT - 1,
} HitLocation;
typedef struct STRUCT_510948 {
Object* field_0;
Object* field_4;
Object* field_8;
int field_C;
} STRUCT_510948;
typedef struct STRUCT_664980 {
Object* attacker;
Object* defender;
int actionPointsBonus;
int accuracyBonus;
int damageBonus;
int minDamage;
int maxDamage;
int field_1C; // probably bool, indicating field_20 and field_24 used
int field_20; // flags on attacker
int field_24; // flags on defender
} STRUCT_664980;
static_assert(sizeof(STRUCT_664980) == 40, "wrong size");
typedef struct Attack {
Object* attacker;
int hitMode;
Object* weapon;
int attackHitLocation;
int attackerDamage;
int attackerFlags;
int ammoQuantity;
int criticalMessageId;
Object* defender;
int tile;
int defenderHitLocation;
int defenderDamage;
int defenderFlags;
int defenderKnockback;
Object* oops;
int extrasLength;
Object* extras[EXPLOSION_TARGET_COUNT];
int extrasHitLocation[EXPLOSION_TARGET_COUNT];
int extrasDamage[EXPLOSION_TARGET_COUNT];
int extrasFlags[EXPLOSION_TARGET_COUNT];
int extrasKnockback[EXPLOSION_TARGET_COUNT];
} Attack;
static_assert(sizeof(Attack) == 184, "wrong size");
// Provides metadata about critical hit effect.
typedef struct CriticalHitDescription {
int damageMultiplier;
// Damage flags that will be applied to defender.
int flags;
// Stat to check to upgrade this critical hit to massive critical hit or
// -1 if there is no massive critical hit.
int massiveCriticalStat;
// Bonus/penalty to massive critical stat.
int massiveCriticalStatModifier;
// Additional damage flags if this critical hit become massive critical.
int massiveCriticalFlags;
int messageId;
int massiveCriticalMessageId;
} CriticalHitDescription;
#endif /* COMBAT_DEFS_H */

549
src/config.c Normal file
View File

@ -0,0 +1,549 @@
#include "config.h"
#include "db.h"
#include "memory.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Last section key read from .INI file.
//
// 0x518224
char gConfigLastSectionKey[CONFIG_FILE_MAX_LINE_LENGTH] = "unknown";
// 0x42BD90
bool configInit(Config* config)
{
if (config == NULL) {
return false;
}
if (dictionaryInit(config, CONFIG_INITIAL_CAPACITY, sizeof(ConfigSection), NULL) != 0) {
return false;
}
return true;
}
// 0x42BDBC
void configFree(Config* config)
{
if (config == NULL) {
return;
}
for (int sectionIndex = 0; sectionIndex < config->entriesLength; sectionIndex++) {
DictionaryEntry* sectionEntry = &(config->entries[sectionIndex]);
ConfigSection* section = sectionEntry->value;
for (int keyValueIndex = 0; keyValueIndex < section->entriesLength; keyValueIndex++) {
DictionaryEntry* keyValueEntry = &(section->entries[keyValueIndex]);
char** value = keyValueEntry->value;
internal_free(*value);
*value = NULL;
}
dictionaryFree(section);
}
dictionaryFree(config);
}
// Parses command line argments and adds them into the config.
//
// The expected format of [argv] elements are "[section]key=value", otherwise
// the element is silently ignored.
//
// NOTE: This function trims whitespace in key-value pair, but not in section.
// I don't know if this is intentional or it's bug.
//
// 0x42BE38
bool configParseCommandLineArguments(Config* config, int argc, char** argv)
{
if (config == NULL) {
return false;
}
for (int arg = 0; arg < argc; arg++) {
char* pch = argv[arg];
// Find opening bracket.
while (*pch != '\0' && *pch != '[') {
pch++;
}
if (*pch == '\0') {
continue;
}
char* sectionKey = pch + 1;
// Find closing bracket.
while (*pch != '\0' && *pch != ']') {
pch++;
}
if (*pch == '\0') {
continue;
}
*pch = '\0';
char key[260];
char value[260];
if (configParseKeyValue(pch + 1, key, value)) {
if (!configSetString(config, sectionKey, key, value)) {
*pch = ']';
return false;
}
}
*pch = ']';
}
return true;
}
// 0x42BF48
bool configGetString(Config* config, const char* sectionKey, const char* key, char** valuePtr)
{
if (config == NULL || sectionKey == NULL || key == NULL || valuePtr == NULL) {
return false;
}
int sectionIndex = dictionaryGetIndexByKey(config, sectionKey);
if (sectionIndex == -1) {
return false;
}
DictionaryEntry* sectionEntry = &(config->entries[sectionIndex]);
ConfigSection* section = sectionEntry->value;
int index = dictionaryGetIndexByKey(section, key);
if (index == -1) {
return false;
}
DictionaryEntry* keyValueEntry = &(section->entries[index]);
*valuePtr = *(char**)keyValueEntry->value;
return true;
}
// 0x42BF90
bool configSetString(Config* config, const char* sectionKey, const char* key, const char* value)
{
if (config == NULL || sectionKey == NULL || key == NULL || value == NULL) {
return false;
}
int sectionIndex = dictionaryGetIndexByKey(config, sectionKey);
if (sectionIndex == -1) {
// FIXME: Looks like a bug, this function never returns -1, which will
// eventually lead to crash.
if (configEnsureSectionExists(config, sectionKey) == -1) {
return false;
}
sectionIndex = dictionaryGetIndexByKey(config, sectionKey);
}
DictionaryEntry* sectionEntry = &(config->entries[sectionIndex]);
ConfigSection* section = sectionEntry->value;
int index = dictionaryGetIndexByKey(section, key);
if (index != -1) {
DictionaryEntry* keyValueEntry = &(section->entries[index]);
char** existingValue = keyValueEntry->value;
internal_free(*existingValue);
*existingValue = NULL;
dictionaryRemoveValue(section, key);
}
char* valueCopy = internal_strdup(value);
if (valueCopy == NULL) {
return false;
}
if (dictionaryAddValue(section, key, &valueCopy) == -1) {
internal_free(valueCopy);
return false;
}
return true;
}
// 0x42C05C
bool configGetInt(Config* config, const char* sectionKey, const char* key, int* valuePtr)
{
if (valuePtr == NULL) {
return false;
}
char* stringValue;
if (!configGetString(config, sectionKey, key, &stringValue)) {
return false;
}
*valuePtr = atoi(stringValue);
return true;
}
// 0x42C090
bool configGetIntList(Config* config, const char* sectionKey, const char* key, int* arr, int count)
{
if (arr == NULL || count < 2) {
return false;
}
char* string;
if (!configGetString(config, sectionKey, key, &string)) {
return false;
}
char temp[CONFIG_FILE_MAX_LINE_LENGTH];
strncpy(temp, string, CONFIG_FILE_MAX_LINE_LENGTH - 1);
char* beginning = temp;
char* pch = beginning;
while (*pch != '\0') {
if (*pch == ',') {
*pch = '\0';
*arr++ = atoi(beginning);
*pch = ',';
pch++;
beginning = pch;
count--;
if (count < 0) {
break;
}
}
pch++;
}
if (count <= 1) {
*arr = atoi(beginning);
}
return true;
}
// 0x42C160
bool configSetInt(Config* config, const char* sectionKey, const char* key, int value)
{
char stringValue[20];
itoa(value, stringValue, 10);
return configSetString(config, sectionKey, key, stringValue);
}
// Reads .INI file into config.
//
// 0x42C280
bool configRead(Config* config, const char* filePath, bool isDb)
{
if (config == NULL || filePath == NULL) {
return false;
}
char string[CONFIG_FILE_MAX_LINE_LENGTH];
if (isDb) {
File* stream = fileOpen(filePath, "rb");
if (stream != NULL) {
while (fileReadString(string, sizeof(string), stream) != NULL) {
configParseLine(config, string);
}
fileClose(stream);
}
} else {
FILE* stream = fopen(filePath, "rt");
if (stream != NULL) {
while (fgets(string, sizeof(string), stream) != NULL) {
configParseLine(config, string);
}
fclose(stream);
}
// FIXME: This function returns `true` even if the file was not actually
// read. I'm pretty sure it's bug.
}
return true;
}
// Writes config into .INI file.
//
// 0x42C324
bool configWrite(Config* config, const char* filePath, bool isDb)
{
if (config == NULL || filePath == NULL) {
return false;
}
if (isDb) {
File* stream = fileOpen(filePath, "wt");
if (stream == NULL) {
return false;
}
for (int sectionIndex = 0; sectionIndex < config->entriesLength; sectionIndex++) {
DictionaryEntry* sectionEntry = &(config->entries[sectionIndex]);
filePrintFormatted(stream, "[%s]\n", sectionEntry->key);
ConfigSection* section = sectionEntry->value;
for (int index = 0; index < section->entriesLength; index++) {
DictionaryEntry* keyValueEntry = &(section->entries[index]);
filePrintFormatted(stream, "%s=%s\n", keyValueEntry->key, *(char**)keyValueEntry->value);
}
filePrintFormatted(stream, "\n");
}
fileClose(stream);
} else {
FILE* stream = fopen(filePath, "wt");
if (stream == NULL) {
return false;
}
for (int sectionIndex = 0; sectionIndex < config->entriesLength; sectionIndex++) {
DictionaryEntry* sectionEntry = &(config->entries[sectionIndex]);
fprintf(stream, "[%s]\n", sectionEntry->key);
ConfigSection* section = sectionEntry->value;
for (int index = 0; index < section->entriesLength; index++) {
DictionaryEntry* keyValueEntry = &(section->entries[index]);
fprintf(stream, "%s=%s\n", keyValueEntry->key, *(char**)keyValueEntry->value);
}
fprintf(stream, "\n");
}
fclose(stream);
}
return true;
}
// Parses a line from .INI file into config.
//
// A line either contains a "[section]" section key or "key=value" pair. In the
// first case section key is not added to config immediately, instead it is
// stored in [gConfigLastSectionKey] for later usage. This prevents empty
// sections in the config.
//
// In case of key-value pair it pretty straight forward - it adds key-value
// pair into previously read section key stored in [gConfigLastSectionKey].
//
// Returns `true` when a section was parsed or key-value pair was parsed and
// added to the config, or `false` otherwise.
//
// 0x42C4BC
bool configParseLine(Config* config, char* string)
{
char* pch;
// Find comment marker and truncate the string.
pch = string;
while (*pch != '\0' && *pch != ';') {
pch++;
}
if (*pch != '\0') {
*pch = '\0';
}
// Find opening bracket.
pch = string;
while (*pch != '\0' && *pch != '[') {
pch++;
}
if (*pch == '[') {
char* sectionKey = pch + 1;
// Find closing bracket.
while (*pch != '\0' && *pch != ']') {
pch++;
}
if (*pch == ']') {
*pch = '\0';
strcpy(gConfigLastSectionKey, sectionKey);
return configTrimString(gConfigLastSectionKey);
}
}
char key[260];
char value[260];
if (!configParseKeyValue(string, key, value)) {
return false;
}
return configSetString(config, gConfigLastSectionKey, key, value);
}
// Splits "key=value" pair from [string] and copy appropriate parts into [key]
// and [value] respectively.
//
// Both key and value are trimmed.
//
// 0x42C594
bool configParseKeyValue(char* string, char* key, char* value)
{
if (string == NULL || key == NULL || value == NULL) {
return false;
}
// Find equals character.
char* pch = string;
while (*pch != '\0' && *pch != '=') {
pch++;
}
if (*pch == '\0') {
return false;
}
*pch = '\0';
strcpy(key, string);
strcpy(value, pch + 1);
*pch = '=';
configTrimString(key);
configTrimString(value);
return true;
}
// Ensures the config has a section with specified key.
//
// Return `true` if section exists or it was successfully added, or `false`
// otherwise.
//
// 0x42C638
bool configEnsureSectionExists(Config* config, const char* sectionKey)
{
if (config == NULL || sectionKey == NULL) {
return false;
}
if (dictionaryGetIndexByKey(config, sectionKey) != -1) {
// Section already exists, no need to do anything.
return true;
}
ConfigSection section;
if (dictionaryInit(&section, CONFIG_INITIAL_CAPACITY, sizeof(char**), NULL) == -1) {
return false;
}
if (dictionaryAddValue(config, sectionKey, &section) == -1) {
return false;
}
return true;
}
// Removes leading and trailing whitespace from the specified string.
//
// 0x42C698
bool configTrimString(char* string)
{
if (string == NULL) {
return false;
}
int length = strlen(string);
if (length == 0) {
return true;
}
// Starting from the end of the string, loop while it's a whitespace and
// decrement string length.
char* pch = string + length - 1;
while (length != 0 && isspace(*pch)) {
length--;
pch--;
}
// pch now points to the last non-whitespace character.
pch[1] = '\0';
// Starting from the beginning of the string loop while it's a whitespace
// and decrement string length.
pch = string;
while (isspace(*pch)) {
pch++;
length--;
}
// pch now points for to the first non-whitespace character.
memmove(string, pch, length + 1);
return true;
}
// 0x42C718
bool configGetDouble(Config* config, const char* sectionKey, const char* key, double* valuePtr)
{
if (valuePtr == NULL) {
return false;
}
char* stringValue;
if (!configGetString(config, sectionKey, key, &stringValue)) {
return false;
}
*valuePtr = strtod(stringValue, NULL);
return true;
}
// 0x42C74C
bool configSetDouble(Config* config, const char* sectionKey, const char* key, double value)
{
char stringValue[32];
sprintf(stringValue, "%.6f", value);
return configSetString(config, sectionKey, key, stringValue);
}
// NOTE: Boolean-typed variant of [configGetInt].
bool configGetBool(Config* config, const char* sectionKey, const char* key, bool* valuePtr)
{
if (valuePtr == NULL) {
return false;
}
int integerValue;
if (!configGetInt(config, sectionKey, key, &integerValue)) {
return false;
}
*valuePtr = integerValue != 0;
return true;
}
// NOTE: Boolean-typed variant of [configGetInt].
bool configSetBool(Config* config, const char* sectionKey, const char* key, bool value)
{
return configSetInt(config, sectionKey, key, value ? 1 : 0);
}

47
src/config.h Normal file
View File

@ -0,0 +1,47 @@
#ifndef CONFIG_H
#define CONFIG_H
#include "dictionary.h"
#include <stdbool.h>
#define CONFIG_FILE_MAX_LINE_LENGTH (256)
// The initial number of sections (or key-value) pairs in the config.
#define CONFIG_INITIAL_CAPACITY (10)
// A representation of .INI file.
//
// It's implemented as a [Dictionary] whos keys are section names of .INI file,
// and it's values are [ConfigSection] structs.
typedef Dictionary Config;
// Representation of .INI section.
//
// It's implemented as a [Dictionary] whos keys are names of .INI file
// key-pair values, and it's values are pointers to strings (char**).
typedef Dictionary ConfigSection;
extern char gConfigLastSectionKey[CONFIG_FILE_MAX_LINE_LENGTH];
bool configInit(Config* config);
void configFree(Config* config);
bool configParseCommandLineArguments(Config* config, int argc, char** argv);
bool configGetString(Config* config, const char* sectionKey, const char* key, char** valuePtr);
bool configSetString(Config* config, const char* sectionKey, const char* key, const char* value);
bool configGetInt(Config* config, const char* sectionKey, const char* key, int* valuePtr);
bool configGetIntList(Config* config, const char* section, const char* key, int* arr, int count);
bool configSetInt(Config* config, const char* sectionKey, const char* key, int value);
bool configRead(Config* config, const char* filePath, bool isDb);
bool configWrite(Config* config, const char* filePath, bool isDb);
bool configParseLine(Config* config, char* string);
bool configParseKeyValue(char* string, char* key, char* value);
bool configEnsureSectionExists(Config* config, const char* sectionKey);
bool configTrimString(char* string);
bool configGetDouble(Config* config, const char* sectionKey, const char* key, double* valuePtr);
bool configSetDouble(Config* config, const char* sectionKey, const char* key, double value);
bool configGetBool(Config* config, const char* sectionKey, const char* key, bool* valuePtr);
bool configSetBool(Config* config, const char* sectionKey, const char* key, bool value);
#endif /* CONFIG_H */

4608
src/core.c Normal file

File diff suppressed because it is too large Load Diff

637
src/core.h Normal file
View File

@ -0,0 +1,637 @@
#ifndef CORE_H
#define CORE_H
#include "db.h"
#include "dinput.h"
#include "geometry.h"
#include "window.h"
#include <stdbool.h>
#define MOUSE_DEFAULT_CURSOR_WIDTH 8
#define MOUSE_DEFAULT_CURSOR_HEIGHT 8
#define MOUSE_DEFAULT_CURSOR_SIZE (MOUSE_DEFAULT_CURSOR_WIDTH * MOUSE_DEFAULT_CURSOR_HEIGHT)
#define MOUSE_STATE_LEFT_BUTTON_DOWN 0x01
#define MOUSE_STATE_RIGHT_BUTTON_DOWN 0x02
#define MOUSE_EVENT_LEFT_BUTTON_DOWN 0x01
#define MOUSE_EVENT_RIGHT_BUTTON_DOWN 0x02
#define MOUSE_EVENT_LEFT_BUTTON_REPEAT 0x04
#define MOUSE_EVENT_RIGHT_BUTTON_REPEAT 0x08
#define MOUSE_EVENT_LEFT_BUTTON_UP 0x10
#define MOUSE_EVENT_RIGHT_BUTTON_UP 0x20
#define MOUSE_EVENT_ANY_BUTTON_DOWN (MOUSE_EVENT_LEFT_BUTTON_DOWN | MOUSE_EVENT_RIGHT_BUTTON_DOWN)
#define MOUSE_EVENT_ANY_BUTTON_REPEAT (MOUSE_EVENT_LEFT_BUTTON_REPEAT | MOUSE_EVENT_RIGHT_BUTTON_REPEAT)
#define MOUSE_EVENT_ANY_BUTTON_UP (MOUSE_EVENT_LEFT_BUTTON_UP | MOUSE_EVENT_RIGHT_BUTTON_UP)
#define MOUSE_EVENT_LEFT_BUTTON_DOWN_REPEAT (MOUSE_EVENT_LEFT_BUTTON_DOWN | MOUSE_EVENT_LEFT_BUTTON_REPEAT)
#define MOUSE_EVENT_RIGHT_BUTTON_DOWN_REPEAT (MOUSE_EVENT_RIGHT_BUTTON_DOWN | MOUSE_EVENT_RIGHT_BUTTON_REPEAT)
#define BUTTON_REPEAT_TIME 250
#define KEY_STATE_UP 0
#define KEY_STATE_DOWN 1
#define KEY_STATE_REPEAT 2
#define MODIFIER_KEY_STATE_NUM_LOCK 0x01
#define MODIFIER_KEY_STATE_CAPS_LOCK 0x02
#define MODIFIER_KEY_STATE_SCROLL_LOCK 0x04
#define KEYBOARD_EVENT_MODIFIER_CAPS_LOCK 0x0001
#define KEYBOARD_EVENT_MODIFIER_NUM_LOCK 0x0002
#define KEYBOARD_EVENT_MODIFIER_SCROLL_LOCK 0x0004
#define KEYBOARD_EVENT_MODIFIER_LEFT_SHIFT 0x0008
#define KEYBOARD_EVENT_MODIFIER_RIGHT_SHIFT 0x0010
#define KEYBOARD_EVENT_MODIFIER_LEFT_ALT 0x0020
#define KEYBOARD_EVENT_MODIFIER_RIGHT_ALT 0x0040
#define KEYBOARD_EVENT_MODIFIER_LEFT_CONTROL 0x0080
#define KEYBOARD_EVENT_MODIFIER_RIGHT_CONTROL 0x0100
#define KEYBOARD_EVENT_MODIFIER_ANY_SHIFT (KEYBOARD_EVENT_MODIFIER_LEFT_SHIFT | KEYBOARD_EVENT_MODIFIER_RIGHT_SHIFT)
#define KEYBOARD_EVENT_MODIFIER_ANY_ALT (KEYBOARD_EVENT_MODIFIER_LEFT_ALT | KEYBOARD_EVENT_MODIFIER_RIGHT_ALT)
#define KEYBOARD_EVENT_MODIFIER_ANY_CONTROL (KEYBOARD_EVENT_MODIFIER_LEFT_CONTROL | KEYBOARD_EVENT_MODIFIER_RIGHT_CONTROL)
#define KEY_QUEUE_SIZE 64
typedef enum Key {
KEY_ESCAPE = '\x1b',
KEY_TAB = '\x09',
KEY_BACKSPACE = '\x08',
KEY_RETURN = '\r',
KEY_SPACE = ' ',
KEY_EXCLAMATION = '!',
KEY_QUOTE = '"',
KEY_NUMBER_SIGN = '#',
KEY_DOLLAR = '$',
KEY_PERCENT = '%',
KEY_AMPERSAND = '&',
KEY_SINGLE_QUOTE = '\'',
KEY_PAREN_LEFT = '(',
KEY_PAREN_RIGHT = ')',
KEY_ASTERISK = '*',
KEY_PLUS = '+',
KEY_COMMA = ',',
KEY_MINUS = '-',
KEY_DOT = '.',
KEY_SLASH = '/',
KEY_0 = '0',
KEY_1 = '1',
KEY_2 = '2',
KEY_3 = '3',
KEY_4 = '4',
KEY_5 = '5',
KEY_6 = '6',
KEY_7 = '7',
KEY_8 = '8',
KEY_9 = '9',
KEY_COLON = ':',
KEY_SEMICOLON = ';',
KEY_LESS = '<',
KEY_EQUAL = '=',
KEY_GREATER = '>',
KEY_QUESTION = '?',
KEY_AT = '@',
KEY_UPPERCASE_A = 'A',
KEY_UPPERCASE_B = 'B',
KEY_UPPERCASE_C = 'C',
KEY_UPPERCASE_D = 'D',
KEY_UPPERCASE_E = 'E',
KEY_UPPERCASE_F = 'F',
KEY_UPPERCASE_G = 'G',
KEY_UPPERCASE_H = 'H',
KEY_UPPERCASE_I = 'I',
KEY_UPPERCASE_J = 'J',
KEY_UPPERCASE_K = 'K',
KEY_UPPERCASE_L = 'L',
KEY_UPPERCASE_M = 'M',
KEY_UPPERCASE_N = 'N',
KEY_UPPERCASE_O = 'O',
KEY_UPPERCASE_P = 'P',
KEY_UPPERCASE_Q = 'Q',
KEY_UPPERCASE_R = 'R',
KEY_UPPERCASE_S = 'S',
KEY_UPPERCASE_T = 'T',
KEY_UPPERCASE_U = 'U',
KEY_UPPERCASE_V = 'V',
KEY_UPPERCASE_W = 'W',
KEY_UPPERCASE_X = 'X',
KEY_UPPERCASE_Y = 'Y',
KEY_UPPERCASE_Z = 'Z',
KEY_BRACKET_LEFT = '[',
KEY_BACKSLASH = '\\',
KEY_BRACKET_RIGHT = ']',
KEY_CARET = '^',
KEY_UNDERSCORE = '_',
KEY_GRAVE = '`',
KEY_LOWERCASE_A = 'a',
KEY_LOWERCASE_B = 'b',
KEY_LOWERCASE_C = 'c',
KEY_LOWERCASE_D = 'd',
KEY_LOWERCASE_E = 'e',
KEY_LOWERCASE_F = 'f',
KEY_LOWERCASE_G = 'g',
KEY_LOWERCASE_H = 'h',
KEY_LOWERCASE_I = 'i',
KEY_LOWERCASE_J = 'j',
KEY_LOWERCASE_K = 'k',
KEY_LOWERCASE_L = 'l',
KEY_LOWERCASE_M = 'm',
KEY_LOWERCASE_N = 'n',
KEY_LOWERCASE_O = 'o',
KEY_LOWERCASE_P = 'p',
KEY_LOWERCASE_Q = 'q',
KEY_LOWERCASE_R = 'r',
KEY_LOWERCASE_S = 's',
KEY_LOWERCASE_T = 't',
KEY_LOWERCASE_U = 'u',
KEY_LOWERCASE_V = 'v',
KEY_LOWERCASE_W = 'w',
KEY_LOWERCASE_X = 'x',
KEY_LOWERCASE_Y = 'y',
KEY_LOWERCASE_Z = 'z',
KEY_BRACE_LEFT = '{',
KEY_BAR = '|',
KEY_BRACE_RIGHT = '}',
KEY_TILDE = '~',
KEY_DEL = 127,
KEY_136 = 136,
KEY_146 = 146,
KEY_149 = 149,
KEY_150 = 150,
KEY_151 = 151,
KEY_152 = 152,
KEY_161 = 161,
KEY_163 = 163,
KEY_164 = 164,
KEY_166 = 166,
KEY_168 = 168,
KEY_167 = 167,
KEY_170 = 170,
KEY_172 = 172,
KEY_176 = 176,
KEY_178 = 178,
KEY_179 = 179,
KEY_180 = 180,
KEY_181 = 181,
KEY_186 = 186,
KEY_191 = 191,
KEY_196 = 196,
KEY_199 = 199,
KEY_209 = 209,
KEY_214 = 214,
KEY_215 = 215,
KEY_220 = 220,
KEY_223 = 223,
KEY_224 = 224,
KEY_228 = 228,
KEY_231 = 231,
KEY_232 = 232,
KEY_233 = 233,
KEY_241 = 241,
KEY_246 = 246,
KEY_247 = 247,
KEY_249 = 249,
KEY_252 = 252,
KEY_ALT_Q = 272,
KEY_ALT_W = 273,
KEY_ALT_E = 274,
KEY_ALT_R = 275,
KEY_ALT_T = 276,
KEY_ALT_Y = 277,
KEY_ALT_U = 278,
KEY_ALT_I = 279,
KEY_ALT_O = 280,
KEY_ALT_P = 281,
KEY_ALT_A = 286,
KEY_ALT_S = 287,
KEY_ALT_D = 288,
KEY_ALT_F = 289,
KEY_ALT_G = 290,
KEY_ALT_H = 291,
KEY_ALT_J = 292,
KEY_ALT_K = 293,
KEY_ALT_L = 294,
KEY_ALT_Z = 300,
KEY_ALT_X = 301,
KEY_ALT_C = 302,
KEY_ALT_V = 303,
KEY_ALT_B = 304,
KEY_ALT_N = 305,
KEY_ALT_M = 306,
KEY_CTRL_Q = 17,
KEY_CTRL_W = 23,
KEY_CTRL_E = 5,
KEY_CTRL_R = 18,
KEY_CTRL_T = 20,
KEY_CTRL_Y = 25,
KEY_CTRL_U = 21,
KEY_CTRL_I = 9,
KEY_CTRL_O = 15,
KEY_CTRL_P = 16,
KEY_CTRL_A = 1,
KEY_CTRL_S = 19,
KEY_CTRL_D = 4,
KEY_CTRL_F = 6,
KEY_CTRL_G = 7,
KEY_CTRL_H = 8,
KEY_CTRL_J = 10,
KEY_CTRL_K = 11,
KEY_CTRL_L = 12,
KEY_CTRL_Z = 26,
KEY_CTRL_X = 24,
KEY_CTRL_C = 3,
KEY_CTRL_V = 22,
KEY_CTRL_B = 2,
KEY_CTRL_N = 14,
KEY_CTRL_M = 13,
KEY_F1 = 315,
KEY_F2 = 316,
KEY_F3 = 317,
KEY_F4 = 318,
KEY_F5 = 319,
KEY_F6 = 320,
KEY_F7 = 321,
KEY_F8 = 322,
KEY_F9 = 323,
KEY_F10 = 324,
KEY_F11 = 389,
KEY_F12 = 390,
KEY_SHIFT_F1 = 340,
KEY_SHIFT_F2 = 341,
KEY_SHIFT_F3 = 342,
KEY_SHIFT_F4 = 343,
KEY_SHIFT_F5 = 344,
KEY_SHIFT_F6 = 345,
KEY_SHIFT_F7 = 346,
KEY_SHIFT_F8 = 347,
KEY_SHIFT_F9 = 348,
KEY_SHIFT_F10 = 349,
KEY_SHIFT_F11 = 391,
KEY_SHIFT_F12 = 392,
KEY_CTRL_F1 = 350,
KEY_CTRL_F2 = 351,
KEY_CTRL_F3 = 352,
KEY_CTRL_F4 = 353,
KEY_CTRL_F5 = 354,
KEY_CTRL_F6 = 355,
KEY_CTRL_F7 = 356,
KEY_CTRL_F8 = 357,
KEY_CTRL_F9 = 358,
KEY_CTRL_F10 = 359,
KEY_CTRL_F11 = 393,
KEY_CTRL_F12 = 394,
KEY_ALT_F1 = 360,
KEY_ALT_F2 = 361,
KEY_ALT_F3 = 362,
KEY_ALT_F4 = 363,
KEY_ALT_F5 = 364,
KEY_ALT_F6 = 365,
KEY_ALT_F7 = 366,
KEY_ALT_F8 = 367,
KEY_ALT_F9 = 368,
KEY_ALT_F10 = 369,
KEY_ALT_F11 = 395,
KEY_ALT_F12 = 396,
KEY_HOME = 327,
KEY_CTRL_HOME = 375,
KEY_ALT_HOME = 407,
KEY_PAGE_UP = 329,
KEY_CTRL_PAGE_UP = 388,
KEY_ALT_PAGE_UP = 409,
KEY_INSERT = 338,
KEY_CTRL_INSERT = 402,
KEY_ALT_INSERT = 418,
KEY_DELETE = 339,
KEY_CTRL_DELETE = 403,
KEY_ALT_DELETE = 419,
KEY_END = 335,
KEY_CTRL_END = 373,
KEY_ALT_END = 415,
KEY_PAGE_DOWN = 337,
KEY_ALT_PAGE_DOWN = 417,
KEY_CTRL_PAGE_DOWN = 374,
KEY_ARROW_UP = 328,
KEY_CTRL_ARROW_UP = 397,
KEY_ALT_ARROW_UP = 408,
KEY_ARROW_DOWN = 336,
KEY_CTRL_ARROW_DOWN = 401,
KEY_ALT_ARROW_DOWN = 416,
KEY_ARROW_LEFT = 331,
KEY_CTRL_ARROW_LEFT = 371,
KEY_ALT_ARROW_LEFT = 411,
KEY_ARROW_RIGHT = 333,
KEY_CTRL_ARROW_RIGHT = 372,
KEY_ALT_ARROW_RIGHT = 413,
KEY_CTRL_BACKSLASH = 192,
KEY_NUMBERPAD_5 = 332,
KEY_CTRL_NUMBERPAD_5 = 399,
KEY_ALT_NUMBERPAD_5 = 9999,
KEY_FIRST_INPUT_CHARACTER = KEY_SPACE,
KEY_LAST_INPUT_CHARACTER = KEY_LOWERCASE_Z,
} Key;
typedef enum KeyboardLayout {
KEYBOARD_LAYOUT_QWERTY,
KEYBOARD_LAYOUT_FRENCH,
KEYBOARD_LAYOUT_GERMAN,
KEYBOARD_LAYOUT_ITALIAN,
KEYBOARD_LAYOUT_SPANISH,
} KeyboardLayout;
typedef struct STRUCT_6ABF50 {
// Time when appropriate key was pressed down or -1 if it's up.
int tick;
int repeatCount;
} STRUCT_6ABF50;
typedef struct InputEvent {
// This is either logical key or input event id, which can be either
// character code pressed or some other numbers used throughout the
// game interface.
int logicalKey;
int mouseX;
int mouseY;
} InputEvent;
typedef void TickerProc();
typedef struct TickerListNode {
int flags;
TickerProc* proc;
struct TickerListNode* next;
} TickerListNode;
typedef struct STRUCT_51E2F0 {
int type;
int field_4;
int field_8;
union {
struct {
int type_1_field_C; // mouse x
int type_1_field_10; // mouse y
int type_1_field_14; // keyboard layout
};
struct {
short type_2_field_C;
};
struct {
int dx;
int dy;
int buttons;
};
};
} STRUCT_51E2F0;
static_assert(sizeof(STRUCT_51E2F0) == 24, "wrong size");
typedef struct LogicalKeyEntry {
short field_0;
short unmodified;
short shift;
short lmenu;
short rmenu;
short ctrl;
} LogicalKeyEntry;
typedef struct KeyboardEvent {
unsigned char scanCode;
unsigned short modifiers;
} KeyboardEvent;
typedef int(PauseHandler)();
typedef int(ScreenshotHandler)(int width, int height, unsigned char* buffer, unsigned char* palette);
extern void (*_idle_func)();
extern void (*_focus_func)(int);
extern int gKeyboardKeyRepeatRate;
extern int gKeyboardKeyRepeatDelay;
extern bool _keyboard_hooked;
extern unsigned char gMouseDefaultCursor[MOUSE_DEFAULT_CURSOR_SIZE];
extern int _mouse_idling;
extern unsigned char* gMouseCursorData;
extern unsigned char* _mouse_shape;
extern unsigned char* _mouse_fptr;
extern double gMouseSensitivity;
extern unsigned int _ticker_;
extern int gMouseButtonsState;
extern LPDIRECTDRAW gDirectDraw;
extern LPDIRECTDRAWSURFACE gDirectDrawSurface1;
extern LPDIRECTDRAWSURFACE gDirectDrawSurface2;
extern LPDIRECTDRAWPALETTE gDirectDrawPalette;
extern void (*_update_palette_func)();
extern bool gMmxEnabled;
extern bool gMmxProbed;
extern unsigned char _kb_installed;
extern bool gKeyboardDisabled;
extern bool gKeyboardNumpadDisabled;
extern bool gKeyboardNumlockDisabled;
extern int gKeyboardEventQueueWriteIndex;
extern int gKeyboardEventQueueReadIndex;
extern short word_51E2E8;
extern int gModifierKeysState;
extern int (*_kb_scan_to_ascii)();
extern STRUCT_51E2F0* _vcr_buffer;
extern int _vcr_buffer_index;
extern int _vcr_state;
extern int _vcr_time;
extern int _vcr_counter;
extern int _vcr_terminate_flags;
extern int _vcr_terminated_condition;
extern int _vcr_start_time;
extern int _vcr_registered_atexit;
extern File* _vcr_file;
extern int _vcr_buffer_end;
extern unsigned char gNormalizedQwertyKeys[256];
extern InputEvent gInputEventQueue[40];
extern STRUCT_6ABF50 _GNW95_key_time_stamps[256];
extern int _input_mx;
extern int _input_my;
extern HHOOK _GNW95_keyboardHandle;
extern bool gPaused;
extern int gScreenshotKeyCode;
extern int _using_msec_timer;
extern int gPauseKeyCode;
extern ScreenshotHandler* gScreenshotHandler;
extern int gInputEventQueueReadIndex;
extern unsigned char* gScreenshotBuffer;
extern PauseHandler* gPauseHandler;
extern int gInputEventQueueWriteIndex;
extern bool gRunLoopDisabled;
extern TickerListNode* gTickerListHead;
extern unsigned int gTickerLastTimestamp;
extern bool gCursorIsHidden;
extern int _raw_x;
extern int gMouseCursorHeight;
extern int _raw_y;
extern int _raw_buttons;
extern int gMouseCursorY;
extern int gMouseCursorX;
extern int _mouse_disabled;
extern int gMouseEvent;
extern unsigned int _mouse_speed;
extern int _mouse_curr_frame;
extern bool gMouseInitialized;
extern int gMouseCursorPitch;
extern int gMouseCursorWidth;
extern int _mouse_num_frames;
extern int _mouse_hoty;
extern int _mouse_hotx;
extern unsigned int _mouse_idle_start_time;
extern WindowDrawingProc2* _mouse_blit_trans;
extern WINDOWDRAWINGPROC _mouse_blit;
extern unsigned char _mouse_trans;
extern int gMouseRightButtonDownTimestamp;
extern int gMouseLeftButtonDownTimestamp;
extern int gMousePreviousEvent;
extern unsigned short gSixteenBppPalette[256];
extern Rect _scr_size;
extern int gRedMask;
extern int gGreenMask;
extern int gBlueMask;
extern int gBlueShift;
extern int gRedShift;
extern int gGreenShift;
extern void (*_scr_blit)(unsigned char* src, int src_pitch, int a3, int src_x, int src_y, int src_width, int src_height, int dest_x, int dest_y);
extern void (*_zero_mem)();
extern bool gMmxSupported;
extern unsigned char gLastVideoModePalette[268];
extern KeyboardEvent gKeyboardEventsQueue[KEY_QUEUE_SIZE];
extern LogicalKeyEntry gLogicalKeyEntries[256];
extern unsigned char gPressedPhysicalKeys[256];
extern unsigned int _kb_idle_start_time;
extern KeyboardEvent gLastKeyboardEvent;
extern int gKeyboardLayout;
extern unsigned char gPressedPhysicalKeysCount;
int coreInit(int a1);
void coreExit();
int _get_input();
void _process_bk();
void enqueueInputEvent(int a1);
int dequeueInputEvent();
void inputEventQueueReset();
void tickersExecute();
void tickersAdd(TickerProc* fn);
void tickersRemove(TickerProc* fn);
void tickersEnable();
void tickersDisable();
void pauseGame();
int pauseHandlerDefaultImpl();
void pauseHandlerConfigure(int keyCode, PauseHandler* fn);
void takeScreenshot();
void screenshotBlitter(unsigned char* src, int src_pitch, int a3, int x, int y, int width, int height, int dest_x, int dest_y);
int screenshotHandlerDefaultImpl(int width, int height, unsigned char* data, unsigned char* palette);
void screenshotHandlerConfigure(int keyCode, ScreenshotHandler* handler);
unsigned int _get_time();
void coreDelayProcessingEvents(unsigned int ms);
void coreDelay(unsigned int ms);
unsigned int getTicksSince(unsigned int a1);
unsigned int getTicksBetween(unsigned int a1, unsigned int a2);
unsigned int _get_bk_time();
void buildNormalizedQwertyKeys();
void _GNW95_hook_input(int a1);
int _GNW95_input_init();
int _GNW95_hook_keyboard(int a1);
LRESULT CALLBACK _GNW95_keyboard_hook(int nCode, WPARAM wParam, LPARAM lParam);
void _GNW95_process_message();
void _GNW95_clear_time_stamps();
void _GNW95_process_key(KeyboardData* data);
void _GNW95_lost_focus();
int mouseInit();
void mouseFree();
void mousePrepareDefaultCursor();
int mouseSetFrame(unsigned char* a1, int width, int height, int pitch, int a5, int a6, int a7);
void _mouse_anim();
void mouseShowCursor();
void mouseHideCursor();
void _mouse_info();
void _mouse_simulate_input(int delta_x, int delta_y, int buttons);
bool _mouse_in(int left, int top, int right, int bottom);
bool _mouse_click_in(int left, int top, int right, int bottom);
void mouseGetRect(Rect* rect);
void mouseGetPosition(int* out_x, int* out_y);
void _mouse_set_position(int a1, int a2);
void _mouse_clip();
int mouseGetEvent();
bool cursorIsHidden();
void _mouse_get_raw_state(int* out_x, int* out_y, int* out_buttons);
void mouseSetSensitivity(double value);
void mmxSetEnabled(bool a1);
int _init_mode_320_200();
int _init_mode_320_400();
int _init_mode_640_480_16();
int _init_mode_640_480();
int _init_mode_640_400();
int _init_mode_800_600();
int _init_mode_1024_768();
int _init_mode_1280_1024();
void _get_start_mode_();
void _zero_vid_mem();
int _GNW95_init_mode_ex(int width, int height, int bpp);
int _init_vesa_mode(int width, int height);
int _GNW95_init_window();
int getShiftForBitMask(int mask);
int directDrawInit(int width, int height, int bpp);
void directDrawFree();
void directDrawSetPaletteInRange(unsigned char* a1, int a2, int a3);
void directDrawSetPalette(unsigned char* palette);
unsigned char* directDrawGetPalette();
void _GNW95_ShowRect(unsigned char* src, int src_pitch, int a3, int src_x, int src_y, int src_width, int src_height, int dest_x, int dest_y);
void _GNW95_MouseShowRect16(unsigned char* src, int srcPitch, int a3, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY);
void _GNW95_ShowRect16(unsigned char* src, int srcPitch, int a3, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY);
void _GNW95_MouseShowTransRect16(unsigned char* src, int srcPitch, int a3, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, unsigned char keyColor);
void _GNW95_zero_vid_mem();
int keyboardInit();
void keyboardFree();
void keyboardReset();
int _kb_getch();
void keyboardDisable();
void keyboardEnable();
int keyboardIsDisabled();
void keyboardSetLayout(int new_language);
int keyboardGetLayout();
void _kb_simulate_key(int a1);
int _kb_next_ascii_English_US();
int keyboardDequeueLogicalKeyCode();
void keyboardBuildQwertyConfiguration();
void keyboardBuildFrenchConfiguration();
void keyboardBuildGermanConfiguration();
void keyboardBuildItalianConfiguration();
void keyboardBuildSpanishConfiguration();
void _kb_init_lock_status();
int keyboardPeekEvent(int index, KeyboardEvent** keyboardEventPtr);
bool _vcr_record(const char* fileName);
int _vcr_stop(void);
int _vcr_status();
int _vcr_update();
bool _vcr_clear_buffer();
int _vcr_dump_buffer();
bool _vcr_save_record(STRUCT_51E2F0* ptr, File* stream);
bool _vcr_load_record(STRUCT_51E2F0* ptr, File* stream);
#endif /* CORE_H */

266
src/credits.c Normal file
View File

@ -0,0 +1,266 @@
#include "credits.h"
#include "art.h"
#include "color.h"
#include "core.h"
#include "cycle.h"
#include "debug.h"
#include "draw.h"
#include "game_mouse.h"
#include "memory.h"
#include "message.h"
#include "palette.h"
#include "sound.h"
#include "text_font.h"
#include "window_manager.h"
#include <string.h>
// 0x56D740
File* gCreditsFile;
// 0x56D744
int gCreditsWindowNameColor;
// 0x56D748
int gCreditsWindowTitleFont;
// 0x56D74C
int gCreditsWindowNameFont;
// 0x56D750
int gCreditsWindowTitleColor;
// 0x42C860
void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
{
int oldFont = fontGetCurrent();
colorPaletteLoad("color.pal");
if (useReversedStyle) {
gCreditsWindowTitleColor = _colorTable[18917];
gCreditsWindowNameFont = 103;
gCreditsWindowTitleFont = 104;
gCreditsWindowNameColor = _colorTable[13673];
} else {
gCreditsWindowTitleColor = _colorTable[13673];
gCreditsWindowNameFont = 104;
gCreditsWindowTitleFont = 103;
gCreditsWindowNameColor = _colorTable[18917];
}
soundContinueAll();
char localizedPath[MAX_PATH];
if (_message_make_path(localizedPath, filePath)) {
gCreditsFile = fileOpen(localizedPath, "rt");
if (gCreditsFile != NULL) {
soundContinueAll();
colorCycleDisable();
gameMouseSetCursor(MOUSE_CURSOR_NONE);
bool cursorWasHidden = cursorIsHidden();
if (cursorWasHidden) {
mouseShowCursor();
}
int window = windowCreate(0, 0, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_HEIGHT, _colorTable[0], 20);
soundContinueAll();
if (window != -1) {
unsigned char* windowBuffer = windowGetBuffer(window);
if (windowBuffer != NULL) {
unsigned char* backgroundBuffer = internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
if (backgroundBuffer) {
soundContinueAll();
memset(backgroundBuffer, _colorTable[0], CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
if (backgroundFid != -1) {
CacheEntry* backgroundFrmHandle;
Art* frm = artLock(backgroundFid, &backgroundFrmHandle);
if (frm != NULL) {
int width = artGetWidth(frm, 0, 0);
int height = artGetHeight(frm, 0, 0);
unsigned char* backgroundFrmData = artGetFrameData(frm, 0, 0);
blitBufferToBuffer(backgroundFrmData,
width,
height,
width,
backgroundBuffer + CREDITS_WINDOW_WIDTH * ((CREDITS_WINDOW_HEIGHT - height) / 2) + (CREDITS_WINDOW_WIDTH - width) / 2,
CREDITS_WINDOW_WIDTH);
artUnlock(backgroundFrmHandle);
}
}
unsigned char* intermediateBuffer = internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
if (intermediateBuffer != NULL) {
memset(intermediateBuffer, 0, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
fontSetCurrent(gCreditsWindowTitleFont);
int titleFontLineHeight = fontGetLineHeight();
fontSetCurrent(gCreditsWindowNameFont);
int nameFontLineHeight = fontGetLineHeight();
int lineHeight = nameFontLineHeight + (titleFontLineHeight >= nameFontLineHeight ? titleFontLineHeight - nameFontLineHeight : 0);
int stringBufferSize = CREDITS_WINDOW_WIDTH * lineHeight;
unsigned char* stringBuffer = internal_malloc(stringBufferSize);
if (stringBuffer != NULL) {
blitBufferToBuffer(backgroundBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowBuffer,
CREDITS_WINDOW_WIDTH);
windowRefresh(window);
paletteFadeTo(_cmap);
unsigned char* v40 = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH;
char str[260];
int font;
int color;
unsigned int tick = 0;
bool stop = false;
while (creditsFileParseNextLine(str, &font, &color)) {
fontSetCurrent(font);
int v19 = fontGetStringWidth(str);
if (v19 >= CREDITS_WINDOW_WIDTH) {
continue;
}
memset(stringBuffer, 0, stringBufferSize);
fontDrawText(stringBuffer, str, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH, color);
unsigned char* dest = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH + (CREDITS_WINDOW_WIDTH - v19) / 2;
unsigned char* src = stringBuffer;
for (int index = 0; index < lineHeight; index++) {
if (_get_input() != -1) {
stop = true;
break;
}
memmove(intermediateBuffer, intermediateBuffer + CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH);
memcpy(dest, src, v19);
blitBufferToBuffer(backgroundBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowBuffer,
CREDITS_WINDOW_WIDTH);
blitBufferToBufferTrans(intermediateBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowBuffer,
CREDITS_WINDOW_WIDTH);
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
}
tick = _get_time();
windowRefresh(window);
src += CREDITS_WINDOW_WIDTH;
}
if (stop) {
break;
}
}
if (!stop) {
for (int index = 0; index < CREDITS_WINDOW_HEIGHT; index++) {
if (_get_input() != -1) {
break;
}
memmove(intermediateBuffer, intermediateBuffer + CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH);
memset(intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH, 0, CREDITS_WINDOW_WIDTH);
blitBufferToBuffer(backgroundBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowBuffer,
CREDITS_WINDOW_WIDTH);
blitBufferToBufferTrans(intermediateBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowBuffer,
CREDITS_WINDOW_WIDTH);
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
}
tick = _get_time();
windowRefresh(window);
}
}
internal_free(stringBuffer);
}
internal_free(intermediateBuffer);
}
internal_free(backgroundBuffer);
}
}
soundContinueAll();
paletteFadeTo(gPaletteBlack);
soundContinueAll();
windowDestroy(window);
}
if (cursorWasHidden) {
mouseHideCursor();
}
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
colorCycleEnable();
fileClose(gCreditsFile);
}
}
fontSetCurrent(oldFont);
}
// 0x42CE6C
bool creditsFileParseNextLine(char* dest, int* font, int* color)
{
char string[256];
while (fileReadString(string, 256, gCreditsFile)) {
char* pch;
if (string[0] == ';') {
continue;
} else if (string[0] == '@') {
*font = gCreditsWindowTitleFont;
*color = gCreditsWindowTitleColor;
pch = string + 1;
} else if (string[0] == '#') {
*font = gCreditsWindowNameFont;
*color = _colorTable[17969];
pch = string + 1;
} else {
*font = gCreditsWindowNameFont;
*color = gCreditsWindowNameColor;
pch = string;
}
strcpy(dest, pch);
return true;
}
return false;
}

21
src/credits.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef CREDITS_H
#define CREDITS_H
#include "db.h"
#include <stdbool.h>
#define CREDITS_WINDOW_WIDTH (640)
#define CREDITS_WINDOW_HEIGHT (480)
#define CREDITS_WINDOW_SCROLLING_DELAY (38)
extern File* gCreditsFile;
extern int gCreditsWindowNameColor;
extern int gCreditsWindowTitleFont;
extern int gCreditsWindowNameFont;
extern int gCreditsWindowTitleColor;
void creditsOpen(const char* path, int fid, bool useReversedStyle);
bool creditsFileParseNextLine(char* dest, int* font, int* color);
#endif /* CREDITS_H */

1326
src/critter.c Normal file

File diff suppressed because it is too large Load Diff

128
src/critter.h Normal file
View File

@ -0,0 +1,128 @@
#ifndef CRITTER_H
#define CRITTER_H
#include "db.h"
#include "message.h"
#include "obj_types.h"
#include "proto_types.h"
#include <stdbool.h>
// Maximum length of dude's name length.
#define DUDE_NAME_MAX_LENGTH (32)
// The number of effects caused by radiation.
//
// A radiation effect is an identifier and does not have it's own name. It's
// stat is specified in [gRadiationEffectStats], and it's amount is specified
// in [gRadiationEffectPenalties] for every [RadiationLevel].
#define RADIATION_EFFECT_COUNT 8
// Radiation levels.
//
// The names of levels are taken from Fallout 3, comments from Fallout 2.
typedef enum RadiationLevel {
// Very nauseous.
RADIATION_LEVEL_NONE,
// Slightly fatigued.
RADIATION_LEVEL_MINOR,
// Vomiting does not stop.
RADIATION_LEVEL_ADVANCED,
// Hair is falling out.
RADIATION_LEVEL_CRITICAL,
// Skin is falling off.
RADIATION_LEVEL_DEADLY,
// Intense agony.
RADIATION_LEVEL_FATAL,
// The number of radiation levels.
RADIATION_LEVEL_COUNT,
} RadiationLevel;
typedef enum DudeState {
DUDE_STATE_SNEAKING = 0,
DUDE_STATE_LEVEL_UP_AVAILABLE = 3,
DUDE_STATE_ADDICTED = 4,
} DudeState;
extern char _aCorpse[];
extern char byte_501494[];
extern char* _name_critter;
extern const int gRadiationEnduranceModifiers[RADIATION_LEVEL_COUNT];
extern const int gRadiationEffectStats[RADIATION_EFFECT_COUNT];
extern const int gRadiationEffectPenalties[RADIATION_LEVEL_COUNT][RADIATION_EFFECT_COUNT];
extern Object* _critterClearObj;
extern MessageList gCritterMessageList;
extern char gDudeName[DUDE_NAME_MAX_LENGTH];
extern int _sneak_working;
extern int gKillsByType[KILL_TYPE_COUNT];
extern int _old_rad_level;
int critterInit();
void critterReset();
void critterExit();
int critterLoad(File* stream);
int critterSave(File* stream);
char* critterGetName(Object* obj);
void critterProtoDataCopy(CritterProtoData* dest, CritterProtoData* src);
int dudeSetName(const char* name);
void dudeResetName();
int critterGetHitPoints(Object* critter);
int critterAdjustHitPoints(Object* critter, int amount);
int critterGetPoison(Object* critter);
int critterAdjustPoison(Object* obj, int amount);
int poisonEventProcess(Object* obj, void* data);
int critterGetRadiation(Object* critter);
int critterAdjustRadiation(Object* obj, int amount);
int _critter_check_rads(Object* critter);
int _get_rad_damage_level(Object* obj, void* data);
int _clear_rad_damage(Object* obj, void* data);
void _process_rads(Object* obj, int radiationLevel, bool direction);
int radiationEventProcess(Object* obj, void* data);
int radiationEventRead(File* stream, void** dataPtr);
int radiationEventWrite(File* stream, void* data);
int critterGetDamageType(Object* critter);
int killsIncByType(int killType);
int killsGetByType(int killType);
int killsLoad(File* stream);
int killsSave(File* stream);
int critterGetKillType(Object* critter);
char* killTypeGetName(int killType);
char* killTypeGetDescription(int killType);
int _critter_heal_hours(Object* obj, int a2);
int _critterClearObjDrugs(Object* obj, void* data);
void critterKill(Object* critter, int anim, bool a3);
int critterGetExp(Object* critter);
bool critterIsActive(Object* critter);
bool critterIsDead(Object* critter);
bool critterIsCrippled(Object* critter);
bool _critter_is_prone(Object* critter);
int critterGetBodyType(Object* critter);
int gcdLoad(const char* path);
int protoCritterDataRead(File* stream, CritterProtoData* critterData);
int gcdSave(const char* path);
int protoCritterDataWrite(File* stream, CritterProtoData* critterData);
void dudeDisableState(int state);
void dudeEnableState(int state);
void dudeToggleState(int state);
bool dudeHasState(int state);
int sneakEventProcess(Object* obj, void* data);
int _critter_sneak_clear(Object* obj, void* data);
bool dudeIsSneaking();
int knockoutEventProcess(Object* obj, void* data);
int _critter_wake_clear(Object* obj, void* data);
int _critter_set_who_hit_me(Object* a1, Object* a2);
bool _critter_can_obj_dude_rest();
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int a2);
bool critterIsEncumbered(Object* critter);
bool critterIsFleeing(Object* a1);
bool _critter_flag_check(int pid, int flag);
#endif /* CRITTER_H */

329
src/cycle.c Normal file
View File

@ -0,0 +1,329 @@
#include "cycle.h"
#include "color.h"
#include "core.h"
#include "game_config.h"
#include "palette.h"
// 0x51843C
int gColorCycleSpeedFactor = 1;
// TODO: Convert colors to RGB.
// clang-format off
// Green.
//
// 0x518440
unsigned char _slime[12] = {
0, 108, 0,
11, 115, 7,
27, 123, 15,
43, 131, 27,
};
// Light gray?
//
// 0x51844C
unsigned char _shoreline[18] = {
83, 63, 43,
75, 59, 43,
67, 55, 39,
63, 51, 39,
55, 47, 35,
51, 43, 35,
};
// Orange.
//
// 0x51845E
unsigned char _fire_slow[15] = {
255, 0, 0,
215, 0, 0,
147, 43, 11,
255, 119, 0,
255, 59, 0,
};
// Red.
//
// 0x51846D
unsigned char _fire_fast[15] = {
71, 0, 0,
123, 0, 0,
179, 0, 0,
123, 0, 0,
71, 0, 0,
};
// Light blue.
//
// 0x51847C
unsigned char _monitors[15] = {
107, 107, 111,
99, 103, 127,
87, 107, 143,
0, 147, 163,
107, 187, 255,
};
// clang-format on
// 0x51848C
bool gColorCycleInitialized = false;
// 0x518490
bool gColorCycleEnabled = false;
// 0x518494
int _slime_start = 0;
// 0x518498
int _shoreline_start = 0;
// 0x51849C
int _fire_slow_start = 0;
// 0x5184A0
int _fire_fast_start = 0;
// 0x5184A4
int _monitors_start = 0;
// 0x5184A8
unsigned char _bobber_red = 0;
// 0x5184A9
signed char _bobber_diff = -4;
// 0x56D7D0
unsigned int gColorCycleTimestamp3;
// 0x56D7D4
unsigned int gColorCycleTimestamp1;
// 0x56D7D8
unsigned int gColorCycleTimestamp2;
// 0x56D7DC
unsigned int gColorCycleTimestamp4;
// 0x42E780
void colorCycleInit()
{
if (gColorCycleInitialized) {
return;
}
bool colorCycling;
if (!configGetBool(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_COLOR_CYCLING_KEY, &colorCycling)) {
colorCycling = true;
}
if (!colorCycling) {
return;
}
for (int index = 0; index < 12; index++) {
_slime[index] >>= 2;
}
for (int index = 0; index < 18; index++) {
_shoreline[index] >>= 2;
}
for (int index = 0; index < 15; index++) {
_fire_slow[index] >>= 2;
}
for (int index = 0; index < 15; index++) {
_fire_fast[index] >>= 2;
}
for (int index = 0; index < 15; index++) {
_monitors[index] >>= 2;
}
tickersAdd(colorCycleTicker);
gColorCycleInitialized = true;
gColorCycleEnabled = true;
int cycleSpeedFactor;
if (!configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, &cycleSpeedFactor)) {
cycleSpeedFactor = 1;
}
cycleSetSpeedFactor(cycleSpeedFactor);
}
// 0x42E8CC
void colorCycleReset()
{
if (gColorCycleInitialized) {
gColorCycleTimestamp1 = 0;
gColorCycleTimestamp2 = 0;
gColorCycleTimestamp3 = 0;
gColorCycleTimestamp4 = 0;
tickersAdd(colorCycleTicker);
gColorCycleEnabled = true;
}
}
// 0x42E90C
void colorCycleFree()
{
if (gColorCycleInitialized) {
tickersRemove(colorCycleTicker);
gColorCycleInitialized = false;
gColorCycleEnabled = false;
}
}
// 0x42E930
void colorCycleDisable()
{
gColorCycleEnabled = false;
}
// 0x42E93C
void colorCycleEnable()
{
gColorCycleEnabled = true;
}
// 0x42E948
bool colorCycleEnabled()
{
return gColorCycleEnabled;
}
// 0x42E950
void cycleSetSpeedFactor(int value)
{
gColorCycleSpeedFactor = value;
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, value);
}
// 0x42E97C
void colorCycleTicker()
{
if (!gColorCycleEnabled) {
return;
}
bool changed = false;
unsigned char* palette = _getSystemPalette();
unsigned int time = _get_time();
if (getTicksBetween(time, gColorCycleTimestamp1) >= COLOR_CYCLE_PERIOD_1 * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp1 = time;
int paletteIndex = 229 * 3;
for (int index = _slime_start; index < 12; index++) {
palette[paletteIndex++] = _slime[index];
}
for (int index = 0; index < _slime_start; index++) {
palette[paletteIndex++] = _slime[index];
}
_slime_start -= 3;
if (_slime_start < 0) {
_slime_start = 9;
}
paletteIndex = 248 * 3;
for (int index = _shoreline_start; index < 18; index++) {
palette[paletteIndex++] = _shoreline[index];
}
for (int index = 0; index < _shoreline_start; index++) {
palette[paletteIndex++] = _shoreline[index];
}
_shoreline_start -= 3;
if (_shoreline_start < 0) {
_shoreline_start = 15;
}
paletteIndex = 238 * 3;
for (int index = _fire_slow_start; index < 15; index++) {
palette[paletteIndex++] = _fire_slow[index];
}
for (int index = 0; index < _fire_slow_start; index++) {
palette[paletteIndex++] = _fire_slow[index];
}
_fire_slow_start -= 3;
if (_fire_slow_start < 0) {
_fire_slow_start = 12;
}
}
if (getTicksBetween(time, gColorCycleTimestamp2) >= COLOR_CYCLE_PERIOD_2 * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp2 = time;
int paletteIndex = 243 * 3;
for (int index = _fire_fast_start; index < 15; index++) {
palette[paletteIndex++] = _fire_fast[index];
}
for (int index = 0; index < _fire_fast_start; index++) {
palette[paletteIndex++] = _fire_fast[index];
}
_fire_fast_start -= 3;
if (_fire_fast_start < 0) {
_fire_fast_start = 12;
}
}
if (getTicksBetween(time, gColorCycleTimestamp3) >= COLOR_CYCLE_PERIOD_3 * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp3 = time;
int paletteIndex = 233 * 3;
for (int index = _monitors_start; index < 15; index++) {
palette[paletteIndex++] = _monitors[index];
}
for (int index = 0; index < _monitors_start; index++) {
palette[paletteIndex++] = _monitors[index];
}
_monitors_start -= 3;
if (_monitors_start < 0) {
_monitors_start = 12;
}
}
if (getTicksBetween(time, gColorCycleTimestamp4) >= COLOR_CYCLE_PERIOD_4 * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp4 = time;
if (_bobber_red == 0 || _bobber_red == 60) {
_bobber_diff = -_bobber_diff;
}
_bobber_red += _bobber_diff;
int paletteIndex = 254 * 3;
palette[paletteIndex++] = _bobber_red;
palette[paletteIndex++] = 0;
palette[paletteIndex++] = 0;
}
if (changed) {
paletteSetEntriesInRange(palette + 229 * 3, 229, 255);
}
}

34
src/cycle.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef CYCLE_H
#define CYCLE_H
#include <stdbool.h>
#define COLOR_CYCLE_PERIOD_1 (200U)
#define COLOR_CYCLE_PERIOD_2 (142U)
#define COLOR_CYCLE_PERIOD_3 (100U)
#define COLOR_CYCLE_PERIOD_4 (33U)
extern int gColorCycleSpeedFactor;
extern unsigned char _slime[12];
extern unsigned char _shoreline[18];
extern unsigned char _fire_slow[15];
extern unsigned char _fire_fast[15];
extern unsigned char _monitors[15];
extern bool gColorCycleEnabled;
extern bool gColorCycleInitialized;
extern unsigned int gColorCycleTimestamp3;
extern unsigned int gColorCycleTimestamp1;
extern unsigned int gColorCycleTimestamp2;
extern unsigned int gColorCycleTimestamp4;
void colorCycleInit();
void colorCycleReset();
void colorCycleFree();
void colorCycleDisable();
void colorCycleEnable();
bool colorCycleEnabled();
void cycleSetSpeedFactor(int value);
void colorCycleTicker();
#endif /* CYCLE_H */

10
src/datafile.c Normal file
View File

@ -0,0 +1,10 @@
#include "datafile.h"
// 0x56D7E0
unsigned char _pal[768];
// 0x42F0E4
unsigned char* _datafileGetPalette()
{
return _pal;
}

8
src/datafile.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef DATAFILE_H
#define DATAFILE_H
extern unsigned char _pal[768];
unsigned char* _datafileGetPalette();
#endif /* DATAFILE_H */

734
src/db.c Normal file
View File

@ -0,0 +1,734 @@
#include "db.h"
#include "xfile.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
// Generic file progress report handler.
//
// 0x51DEEC
FileReadProgressHandler* gFileReadProgressHandler = NULL;
// Bytes read so far while tracking progress.
//
// Once this value reaches [gFileReadProgressChunkSize] the handler is called
// and this value resets to zero.
//
// 0x51DEF0
int gFileReadProgressBytesRead = 0;
// The number of bytes to read between calls to progress handler.
//
// 0x673040
int gFileReadProgressChunkSize;
// 0x673044
FileList* gFileListHead;
// Opens file database.
//
// Returns -1 if [filePath1] was specified, but could not be opened by the
// underlying xbase implementation. Result of opening [filePath2] is ignored.
// Returns 0 on success.
//
// NOTE: There are two unknown parameters passed via edx and ecx. The [a2] is
// always 0 at the calling sites, and [a4] is always 1. Both parameters are not
// used, so it's impossible to figure out their meaning.
//
// 0x4C5D30
int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4)
{
if (filePath1 != NULL) {
if (!xbaseOpen(filePath1)) {
return -1;
}
}
if (filePath2 != NULL) {
xbaseOpen(filePath2);
}
return 0;
}
// NOTE: This function simply returns 0, but it definitely accept one parameter
// via eax, as seen at every call site. This value is ignored. It's impossible
// to guess it's name.
//
// 0x4C5D54
int _db_current(int a1)
{
return 0;
}
// 0x4C5D58
bool _db_total()
{
return true;
}
// 0x4C5D60
void dbExit()
{
xbaseReopenAll(NULL);
}
// TODO: sizePtr should be long*.
//
// 0x4C5D68
int dbGetFileSize(const char* filePath, int* sizePtr)
{
assert(filePath); // "filename", "db.c", 108
assert(sizePtr); // "de", "db.c", 109
File* stream = xfileOpen(filePath, "rb");
if (stream == NULL) {
return -1;
}
*sizePtr = xfileGetSize(stream);
xfileClose(stream);
return 0;
}
// 0x4C5DD4
int dbGetFileContents(const char* filePath, void* ptr)
{
assert(filePath); // "filename", "db.c", 141
assert(ptr); // "buf", "db.c", 142
File* stream = xfileOpen(filePath, "rb");
if (stream == NULL) {
return -1;
}
long size = xfileGetSize(stream);
if (gFileReadProgressHandler != NULL) {
unsigned char* byteBuffer = (unsigned char*)ptr;
long remainingSize = size;
long chunkSize = gFileReadProgressChunkSize - gFileReadProgressBytesRead;
while (remainingSize >= chunkSize) {
size_t bytesRead = xfileRead(byteBuffer, sizeof(*byteBuffer), chunkSize, stream);
byteBuffer += bytesRead;
remainingSize -= bytesRead;
gFileReadProgressBytesRead = 0;
gFileReadProgressHandler();
chunkSize = gFileReadProgressChunkSize;
}
if (remainingSize != 0) {
gFileReadProgressBytesRead += xfileRead(byteBuffer, sizeof(*byteBuffer), remainingSize, stream);
}
} else {
xfileRead(ptr, 1, size, stream);
}
xfileClose(stream);
return 0;
}
// 0x4C5EB4
int fileClose(File* stream)
{
return xfileClose(stream);
}
// 0x4C5EC8
File* fileOpen(const char* filename, const char* mode)
{
return xfileOpen(filename, mode);
}
// 0x4C5ED0
int filePrintFormatted(File* stream, const char* format, ...)
{
assert(format); // "format", "db.c", 224
va_list args;
va_start(args, format);
int rc = xfilePrintFormattedArgs(stream, format, args);
va_end(args);
return rc;
}
// 0x4C5F24
int fileReadChar(File* stream)
{
if (gFileReadProgressHandler != NULL) {
int ch = xfileReadChar(stream);
gFileReadProgressBytesRead++;
if (gFileReadProgressBytesRead >= gFileReadProgressChunkSize) {
gFileReadProgressHandler();
gFileReadProgressBytesRead = 0;
}
return ch;
}
return xfileReadChar(stream);
}
// 0x4C5F70
char* fileReadString(char* string, size_t size, File* stream)
{
if (gFileReadProgressHandler != NULL) {
if (xfileReadString(string, size, stream) == NULL) {
return NULL;
}
gFileReadProgressBytesRead += strlen(string);
while (gFileReadProgressBytesRead >= gFileReadProgressChunkSize) {
gFileReadProgressHandler();
gFileReadProgressBytesRead -= gFileReadProgressChunkSize;
}
return string;
}
return xfileReadString(string, size, stream);
}
// 0x4C5FEC
int fileWriteString(const char* string, File* stream)
{
return xfileWriteString(string, stream);
}
// 0x4C5FFC
size_t fileRead(void* ptr, size_t size, size_t count, File* stream)
{
if (gFileReadProgressHandler != NULL) {
unsigned char* byteBuffer = (unsigned char*)ptr;
size_t totalBytesRead = 0;
long remainingSize = size * count;
long chunkSize = gFileReadProgressChunkSize - gFileReadProgressBytesRead;
while (remainingSize >= chunkSize) {
size_t bytesRead = xfileRead(byteBuffer, sizeof(*byteBuffer), chunkSize, stream);
byteBuffer += bytesRead;
totalBytesRead += bytesRead;
remainingSize -= bytesRead;
gFileReadProgressBytesRead = 0;
gFileReadProgressHandler();
chunkSize = gFileReadProgressChunkSize;
}
if (remainingSize != 0) {
size_t bytesRead = xfileRead(byteBuffer, sizeof(*byteBuffer), remainingSize, stream);
gFileReadProgressBytesRead += bytesRead;
totalBytesRead += bytesRead;
}
return totalBytesRead / size;
}
return xfileRead(ptr, size, count, stream);
}
// 0x4C60B8
size_t fileWrite(const void* buf, size_t size, size_t count, File* stream)
{
return xfileWrite(buf, size, count, stream);
}
// 0x4C60C0
int fileSeek(File* stream, long offset, int origin)
{
return xfileSeek(stream, offset, origin);
}
// 0x4C60C8
long fileTell(File* stream)
{
return xfileTell(stream);
}
// 0x4C60D0
void fileRewind(File* stream)
{
xfileRewind(stream);
}
// 0x4C60D8
int fileEof(File* stream)
{
return xfileEof(stream);
}
// NOTE: Not sure about signness.
//
// 0x4C60E0
int fileReadUInt8(File* stream, unsigned char* valuePtr)
{
int value = fileReadChar(stream);
if (value == -1) {
return -1;
}
*valuePtr = value & 0xFF;
return 0;
}
// NOTE: Not sure about signness.
//
// 0x4C60F4
int fileReadInt16(File* stream, short* valuePtr)
{
unsigned char high;
// NOTE: Uninline.
if (fileReadUInt8(stream, &high) == -1) {
return -1;
}
unsigned char low;
// NOTE: Uninline.
if (fileReadUInt8(stream, &low) == -1) {
return -1;
}
*valuePtr = (high << 8) | low;
return 0;
}
// NOTE: Probably uncollapsed 0x4C60F4. There are only couple of places where
// the game reads/writes 16-bit integers. I'm not sure there are unsigned
// shorts used, but there are definitely signed (art offsets can be both
// positive and negative). Provided just in case.
int fileReadUInt16(File* stream, unsigned short* valuePtr)
{
return fileReadInt16(stream, (short*)valuePtr);
}
// 0x4C614C
int fileReadInt32(File* stream, int* valuePtr)
{
int value;
if (xfileRead(&value, 4, 1, stream) == -1) {
return -1;
}
*valuePtr = ((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000);
return 0;
}
// NOTE: Uncollapsed 0x4C614C. The opposite of [_db_fwriteLong]. It can be either
// signed vs. unsigned variant, as well as int vs. long. It's provided here to
// identify places where data was written with [_db_fwriteLong].
int _db_freadInt(File* stream, int* valuePtr)
{
return fileReadInt32(stream, valuePtr);
}
// NOTE: Probably uncollapsed 0x4C614C.
int fileReadUInt32(File* stream, unsigned int* valuePtr)
{
return _db_freadInt(stream, (int*)valuePtr);
}
// NOTE: Uncollapsed 0x4C614C. The opposite of [fileWriteFloat].
int fileReadFloat(File* stream, float* valuePtr)
{
return fileReadInt32(stream, (int*)valuePtr);
}
int fileReadBool(File* stream, bool* valuePtr)
{
int value;
if (fileReadInt32(stream, &value) == -1) {
return -1;
}
*valuePtr = (value != 0);
return 0;
}
// NOTE: Not sure about signness.
//
// 0x4C61AC
int fileWriteUInt8(File* stream, unsigned char value)
{
return xfileWriteChar(value, stream);
};
// 0x4C61C8
int fileWriteInt16(File* stream, short value)
{
// NOTE: Uninline.
if (fileWriteUInt8(stream, (value >> 8) & 0xFF) == -1) {
return -1;
}
// NOTE: Uninline.
if (fileWriteUInt8(stream, value & 0xFF) == -1) {
return -1;
}
return 0;
}
// NOTE: Probably uncollapsed 0x4C61C8.
int fileWriteUInt16(File* stream, unsigned short value)
{
return fileWriteInt16(stream, (short)value);
}
// NOTE: Not sure about signness and int vs. long.
//
// 0x4C6214
int fileWriteInt32(File* stream, int value)
{
// NOTE: Uninline.
return _db_fwriteLong(stream, value);
}
// NOTE: Can either be signed vs. unsigned variant of [fileWriteInt32],
// or int vs. long.
//
// 0x4C6244
int _db_fwriteLong(File* stream, int value)
{
if (fileWriteInt16(stream, (value >> 16) & 0xFFFF) == -1) {
return -1;
}
if (fileWriteInt16(stream, value & 0xFFFF) == -1) {
return -1;
}
return 0;
}
// NOTE: Probably uncollapsed 0x4C6214 or 0x4C6244.
int fileWriteUInt32(File* stream, unsigned int value)
{
return _db_fwriteLong(stream, (int)value);
}
// 0x4C62C4
int fileWriteFloat(File* stream, float value)
{
// NOTE: Uninline.
return _db_fwriteLong(stream, *(int*)&value);
}
int fileWriteBool(File* stream, bool value)
{
return _db_fwriteLong(stream, value ? 1 : 0);
}
// 0x4C62FC
int fileReadUInt8List(File* stream, unsigned char* arr, int count)
{
for (int index = 0; index < count; index++) {
unsigned char ch;
// NOTE: Uninline.
if (fileReadUInt8(stream, &ch) == -1) {
return -1;
}
arr[index] = ch;
}
return 0;
}
// NOTE: Probably uncollapsed 0x4C62FC. There are couple of places where
// [fileReadUInt8List] is used to read strings of fixed length. I'm not
// pretty sure this function existed in the original code, but at least
// it increases visibility of these places.
int fileReadFixedLengthString(File* stream, char* string, int length)
{
return fileReadUInt8List(stream, (unsigned char*)string, length);
}
// 0x4C6330
int fileReadInt16List(File* stream, short* arr, int count)
{
for (int index = 0; index < count; index++) {
short value;
// NOTE: Uninline.
if (fileReadInt16(stream, &value) == -1) {
return -1;
}
arr[index] = value;
}
return 0;
}
// NOTE: Probably uncollapsed 0x4C6330.
int fileReadUInt16List(File* stream, unsigned short* arr, int count)
{
return fileReadInt16List(stream, (short*)arr, count);
}
// NOTE: Not sure about signed/unsigned int/long.
//
// 0x4C63BC
int fileReadInt32List(File* stream, int* arr, int count)
{
if (count == 0) {
return 0;
}
if (fileRead(arr, sizeof(*arr) * count, 1, stream) < 1) {
return -1;
}
for (int index = 0; index < count; index++) {
int value = arr[index];
arr[index] = ((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000);
}
return 0;
}
// NOTE: Uncollapsed 0x4C63BC. The opposite of [_db_fwriteLongCount].
int _db_freadIntCount(File* stream, int* arr, int count)
{
return fileReadInt32List(stream, arr, count);
}
// NOTE: Probably uncollapsed 0x4C63BC.
int fileReadUInt32List(File* stream, unsigned int* arr, int count)
{
return fileReadInt32List(stream, (int*)arr, count);
}
// 0x4C6464
int fileWriteUInt8List(File* stream, unsigned char* arr, int count)
{
for (int index = 0; index < count; index++) {
// NOTE: Uninline.
if (fileWriteUInt8(stream, arr[index]) == -1) {
return -1;
}
}
return 0;
}
// NOTE: Probably uncollapsed 0x4C6464. See [fileReadFixedLengthString].
int fileWriteFixedLengthString(File* stream, char* string, int length)
{
return fileWriteUInt8List(stream, (unsigned char*)string, length);
}
// 0x4C6490
int fileWriteInt16List(File* stream, short* arr, int count)
{
for (int index = 0; index < count; index++) {
// NOTE: Uninline.
if (fileWriteInt16(stream, arr[index]) == -1) {
return -1;
}
}
return 0;
}
// NOTE: Probably uncollapsed 0x4C6490.
int fileWriteUInt16List(File* stream, unsigned short* arr, int count)
{
return fileWriteInt16List(stream, (short*)arr, count);
}
// NOTE: Can be either signed/unsigned + int/long variant.
//
// 0x4C64F8
int fileWriteInt32List(File* stream, int* arr, int count)
{
for (int index = 0; index < count; index++) {
// NOTE: Uninline.
if (_db_fwriteLong(stream, arr[index]) == -1) {
return -1;
}
}
return 0;
}
// NOTE: Not sure about signed/unsigned int/long.
//
// 0x4C6550
int _db_fwriteLongCount(File* stream, int* arr, int count)
{
for (int index = 0; index < count; index++) {
int value = arr[index];
// NOTE: Uninline.
if (fileWriteInt16(stream, (value >> 16) & 0xFFFF) == -1) {
return -1;
}
// NOTE: Uninline.
if (fileWriteInt16(stream, value & 0xFFFF) == -1) {
return -1;
}
}
return 0;
}
// NOTE: Probably uncollapsed 0x4C64F8 or 0x4C6550.
int fileWriteUInt32List(File* stream, unsigned int* arr, int count)
{
return fileWriteInt32List(stream, (int*)arr, count);
}
// 0x4C6628
int fileNameListInit(const char* pattern, char*** fileNameListPtr, int a3, int a4)
{
FileList* fileList = malloc(sizeof(*fileList));
if (fileList == NULL) {
return 0;
}
memset(fileList, 0, sizeof(*fileList));
XList* xlist = &(fileList->xlist);
if (!xlistInit(pattern, xlist)) {
free(fileList);
return 0;
}
int length = 0;
if (xlist->fileNamesLength != 0) {
qsort(xlist->fileNames, xlist->fileNamesLength, sizeof(*xlist->fileNames), _db_list_compare);
int fileNamesLength = xlist->fileNamesLength;
for (int index = 0; index < fileNamesLength - 1; index++) {
if (stricmp(xlist->fileNames[index], xlist->fileNames[index + 1]) == 0) {
char* temp = xlist->fileNames[index + 1];
memmove(&(xlist->fileNames[index + 1]), &(xlist->fileNames[index + 2]), sizeof(*xlist->fileNames) * (xlist->fileNamesLength - index - 1));
xlist->fileNames[xlist->fileNamesLength - 1] = temp;
fileNamesLength--;
index--;
}
}
bool v1 = *pattern == '*';
for (int index = 0; index < fileNamesLength; index += 1) {
const char* name = xlist->fileNames[index];
char dir[_MAX_DIR];
char fileName[_MAX_FNAME];
char extension[_MAX_EXT];
_splitpath(name, NULL, dir, fileName, extension);
bool v2 = false;
if (v1) {
char* pch = dir;
while (*pch != '\0' && *pch != '\\') {
pch++;
}
v2 = *pch != '\0';
}
if (!v2) {
sprintf(xlist->fileNames[index], "%s%s", fileName, extension);
length++;
}
}
}
fileList->next = gFileListHead;
gFileListHead = fileList;
*fileNameListPtr = xlist->fileNames;
return length;
}
// 0x4C6868
void fileNameListFree(char*** fileNameListPtr, int a2)
{
if (gFileListHead == NULL) {
return;
}
FileList* currentFileList = gFileListHead;
FileList* previousFileList = gFileListHead;
while (*fileNameListPtr != currentFileList->xlist.fileNames) {
previousFileList = currentFileList;
currentFileList = currentFileList->next;
if (currentFileList == NULL) {
return;
}
}
if (previousFileList == gFileListHead) {
gFileListHead = currentFileList->next;
} else {
previousFileList->next = currentFileList->next;
}
xlistFree(&(currentFileList->xlist));
free(currentFileList);
}
// NOTE: This function does nothing. It was probably used to set memory procs
// for building file name list.
//
// 0x4C68B8
void _db_register_mem(MallocProc* mallocProc, StrdupProc* strdupProc, FreeProc* freeProc)
{
}
// TODO: Return type should be long.
//
// 0x4C68BC
int fileGetSize(File* stream)
{
return xfileGetSize(stream);
}
// 0x4C68C4
void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size)
{
if (handler != NULL && size != 0) {
gFileReadProgressHandler = handler;
gFileReadProgressChunkSize = size;
} else {
gFileReadProgressHandler = NULL;
gFileReadProgressChunkSize = 0;
}
}
// NOTE: This function is called when fallout2.cfg has "hashing" enabled, but
// it does nothing. It's impossible to guess it's name.
//
// 0x4C68E4
void _db_enable_hash_table_()
{
}
// 0x4C68E8
int _db_list_compare(const void* p1, const void* p2)
{
return stricmp(*(const char**)p1, *(const char**)p2);
}

81
src/db.h Normal file
View File

@ -0,0 +1,81 @@
#ifndef DB_H
#define DB_H
#include "memory_defs.h"
#include "xfile.h"
#include <stdbool.h>
#include <stddef.h>
typedef XFile File;
typedef void FileReadProgressHandler();
typedef char* StrdupProc(const char* string);
typedef struct FileList {
XList xlist;
struct FileList* next;
} FileList;
extern FileReadProgressHandler* gFileReadProgressHandler;
extern int gFileReadProgressBytesRead;
extern int gFileReadProgressChunkSize;
extern FileList* gFileListHead;
int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4);
int _db_current(int a1);
bool _db_total();
void dbExit();
int dbGetFileSize(const char* filePath, int* sizePtr);
int dbGetFileContents(const char* filePath, void* ptr);
int fileClose(File* stream);
File* fileOpen(const char* filename, const char* mode);
int filePrintFormatted(File* stream, const char* format, ...);
int fileReadChar(File* stream);
char* fileReadString(char* str, size_t size, File* stream);
int fileWriteString(const char* s, File* stream);
size_t fileRead(void* buf, size_t size, size_t count, File* stream);
size_t fileWrite(const void* buf, size_t size, size_t count, File* stream);
int fileSeek(File* stream, long offset, int origin);
long fileTell(File* stream);
void fileRewind(File* stream);
int fileEof(File* stream);
int fileReadUInt8(File* stream, unsigned char* valuePtr);
int fileReadInt16(File* stream, short* valuePtr);
int fileReadUInt16(File* stream, unsigned short* valuePtr);
int fileReadInt32(File* stream, int* valuePtr);
int fileReadUInt32(File* stream, unsigned int* valuePtr);
int _db_freadInt(File* stream, int* valuePtr);
int fileReadFloat(File* stream, float* valuePtr);
int fileReadBool(File* stream, bool* valuePtr);
int fileWriteUInt8(File* stream, unsigned char value);
int fileWriteInt16(File* stream, short value);
int fileWriteUInt16(File* stream, unsigned short value);
int fileWriteInt32(File* stream, int value);
int _db_fwriteLong(File* stream, int value);
int fileWriteUInt32(File* stream, unsigned int value);
int fileWriteFloat(File* stream, float value);
int fileWriteBool(File* stream, bool value);
int fileReadUInt8List(File* stream, unsigned char* arr, int count);
int fileReadFixedLengthString(File* stream, char* string, int length);
int fileReadInt16List(File* stream, short* arr, int count);
int fileReadUInt16List(File* stream, unsigned short* arr, int count);
int fileReadInt32List(File* stream, int* arr, int count);
int _db_freadIntCount(File* stream, int* arr, int count);
int fileReadUInt32List(File* stream, unsigned int* arr, int count);
int fileWriteUInt8List(File* stream, unsigned char* arr, int count);
int fileWriteFixedLengthString(File* stream, char* string, int length);
int fileWriteInt16List(File* stream, short* arr, int count);
int fileWriteUInt16List(File* stream, unsigned short* arr, int count);
int fileWriteInt32List(File* stream, int* arr, int count);
int _db_fwriteLongCount(File* stream, int* arr, int count);
int fileWriteUInt32List(File* stream, unsigned int* arr, int count);
int fileNameListInit(const char* pattern, char*** fileNames, int a3, int a4);
void fileNameListFree(char*** fileNames, int a2);
void _db_register_mem(MallocProc* mallocProc, StrdupProc* strdupProc, FreeProc* freeProc);
int fileGetSize(File* stream);
void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size);
void _db_enable_hash_table_();
int _db_list_compare(const void* p1, const void* p2);
#endif /* DB_H */

620
src/dbox.c Normal file
View File

@ -0,0 +1,620 @@
#include "dbox.h"
#include "art.h"
#include "color.h"
#include "core.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_sound.h"
#include "message.h"
#include "text_font.h"
#include "window_manager.h"
#include "word_wrap.h"
#include <stdio.h>
#include <string.h>
// 0x5108C8
const int gDialogBoxBackgroundFrmIds[DIALOG_TYPE_COUNT] = {
218, // MEDIALOG.FRM - Medium generic dialog box
217, // LGDIALOG.FRM - Large generic dialog box
};
// 0x5108D0
const int _ytable[DIALOG_TYPE_COUNT] = {
23,
27,
};
// 0x5108D8
const int _xtable[DIALOG_TYPE_COUNT] = {
29,
29,
};
// 0x5108E0
const int _doneY[DIALOG_TYPE_COUNT] = {
81,
98,
};
// 0x5108E8
const int _doneX[DIALOG_TYPE_COUNT] = {
51,
37,
};
// 0x5108F0
const int _dblines[DIALOG_TYPE_COUNT] = {
5,
6,
};
// 0x510900
int _flgids[7] = {
224, // loadbox.frm - character editor
8, // lilredup.frm - little red button up
9, // lilreddn.frm - little red button down
181, // dnarwoff.frm - character editor
182, // dnarwon.frm - character editor
199, // uparwoff.frm - character editor
200, // uparwon.frm - character editor
};
// 0x51091C
int _flgids2[7] = {
225, // savebox.frm - character editor
8, // lilredup.frm - little red button up
9, // lilreddn.frm - little red button down
181, // dnarwoff.frm - character editor
182, // dnarwon.frm - character editor
199, // uparwoff.frm - character editor
200, // uparwon.frm - character editor
};
// 0x41CF20
int showDialogBox(const char* title, const char** body, int bodyLength, int x, int y, int titleColor, const char* a8, int bodyColor, int flags)
{
MessageList messageList;
MessageListItem messageListItem;
int savedFont = fontGetCurrent();
bool v86 = false;
bool hasTwoButtons = false;
if (a8 != NULL) {
hasTwoButtons = true;
}
bool hasTitle = false;
if (title != NULL) {
hasTitle = true;
}
if ((flags & DIALOG_BOX_YES_NO) != 0) {
hasTwoButtons = true;
flags |= DIALOG_BOX_LARGE;
flags &= ~DIALOG_BOX_0x20;
}
int maximumLineWidth = 0;
if (hasTitle) {
maximumLineWidth = fontGetStringWidth(title);
}
int linesCount = 0;
for (int index = 0; index < bodyLength; index++) {
// NOTE: Calls [fontGetStringWidth] twice because of [max] macro.
maximumLineWidth = max(fontGetStringWidth(body[index]), maximumLineWidth);
linesCount++;
}
int dialogType;
if ((flags & DIALOG_BOX_LARGE) != 0 || hasTwoButtons) {
dialogType = DIALOG_TYPE_LARGE;
} else if ((flags & DIALOG_BOX_MEDIUM) != 0) {
dialogType = DIALOG_TYPE_MEDIUM;
} else {
if (hasTitle) {
linesCount++;
}
dialogType = maximumLineWidth > 168 || linesCount > 5
? DIALOG_TYPE_LARGE
: DIALOG_TYPE_MEDIUM;
}
CacheEntry* backgroundHandle;
int backgroundWidth;
int backgroundHeight;
int fid = buildFid(6, gDialogBoxBackgroundFrmIds[dialogType], 0, 0, 0);
unsigned char* background = artLockFrameDataReturningSize(fid, &backgroundHandle, &backgroundWidth, &backgroundHeight);
if (background == NULL) {
fontSetCurrent(savedFont);
return -1;
}
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
if (win == -1) {
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
return -1;
}
unsigned char* windowBuf = windowGetBuffer(win);
memcpy(windowBuf, background, backgroundWidth * backgroundHeight);
CacheEntry* doneBoxHandle = NULL;
unsigned char* doneBox = NULL;
int doneBoxWidth;
int doneBoxHeight;
CacheEntry* downButtonHandle = NULL;
unsigned char* downButton = NULL;
int downButtonWidth;
int downButtonHeight;
CacheEntry* upButtonHandle = NULL;
unsigned char* upButton = NULL;
if ((flags & DIALOG_BOX_0x20) == 0) {
int doneBoxFid = buildFid(6, 209, 0, 0, 0);
doneBox = artLockFrameDataReturningSize(doneBoxFid, &doneBoxHandle, &doneBoxWidth, &doneBoxHeight);
if (doneBox == NULL) {
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int downButtonFid = buildFid(6, 9, 0, 0, 0);
downButton = artLockFrameDataReturningSize(downButtonFid, &downButtonHandle, &downButtonWidth, &downButtonHeight);
if (downButton == NULL) {
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int upButtonFid = buildFid(6, 8, 0, 0, 0);
upButton = artLockFrameData(upButtonFid, 0, 0, &upButtonHandle);
if (upButton == NULL) {
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int v27 = hasTwoButtons ? _doneX[dialogType] : (backgroundWidth - doneBoxWidth) / 2;
blitBufferToBuffer(doneBox, doneBoxWidth, doneBoxHeight, doneBoxWidth, windowBuf + backgroundWidth * _doneY[dialogType] + v27, backgroundWidth);
if (!messageListInit(&messageList)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
char path[MAX_PATH];
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
if (!messageListLoad(&messageList, path)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
// FIXME: Window is not removed.
return -1;
}
fontSetCurrent(103);
// 100 - DONE
// 101 - YES
messageListItem.num = (flags & DIALOG_BOX_YES_NO) == 0 ? 100 : 101;
if (messageListGetItem(&messageList, &messageListItem)) {
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + v27 + 35, messageListItem.text, backgroundWidth, backgroundWidth, _colorTable[18979]);
}
int btn = buttonCreate(win, v27 + 13, _doneY[dialogType] + 4, downButtonWidth, downButtonHeight, -1, -1, -1, 500, upButton, downButton, NULL, BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
v86 = true;
}
if (hasTwoButtons && dialogType == DIALOG_TYPE_LARGE) {
if (v86) {
if ((flags & DIALOG_BOX_YES_NO) != 0) {
a8 = getmsg(&messageList, &messageListItem, 102);
}
fontSetCurrent(103);
blitBufferToBufferTrans(doneBox,
doneBoxWidth,
doneBoxHeight,
doneBoxWidth,
windowBuf + backgroundWidth * _doneY[dialogType] + _doneX[dialogType] + doneBoxWidth + 24,
backgroundWidth);
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + _doneX[dialogType] + doneBoxWidth + 59,
a8, backgroundWidth, backgroundWidth, _colorTable[18979]);
int btn = buttonCreate(win,
doneBoxWidth + _doneX[dialogType] + 37,
_doneY[dialogType] + 4,
downButtonWidth,
downButtonHeight,
-1, -1, -1, 501, upButton, downButton, 0, BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
} else {
int doneBoxFid = buildFid(6, 209, 0, 0, 0);
unsigned char* doneBox = artLockFrameDataReturningSize(doneBoxFid, &doneBoxHandle, &doneBoxWidth, &doneBoxHeight);
if (doneBox == NULL) {
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int downButtonFid = buildFid(6, 9, 0, 0, 0);
unsigned char* downButton = artLockFrameDataReturningSize(downButtonFid, &downButtonHandle, &downButtonWidth, &downButtonHeight);
if (downButton == NULL) {
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
int upButtonFid = buildFid(6, 8, 0, 0, 0);
unsigned char* upButton = artLockFrameData(upButtonFid, 0, 0, &upButtonHandle);
if (upButton == NULL) {
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
if (!messageListInit(&messageList)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
char path[MAX_PATH];
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
if (!messageListLoad(&messageList, path)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
blitBufferToBufferTrans(doneBox,
doneBoxWidth,
doneBoxHeight,
doneBoxWidth,
windowBuf + backgroundWidth * _doneY[dialogType] + _doneX[dialogType],
backgroundWidth);
fontSetCurrent(103);
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + _doneX[dialogType] + 35,
a8, backgroundWidth, backgroundWidth, _colorTable[18979]);
int btn = buttonCreate(win,
_doneX[dialogType] + 13,
_doneY[dialogType] + 4,
downButtonWidth,
downButtonHeight,
-1,
-1,
-1,
501,
upButton,
downButton,
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
v86 = true;
}
}
fontSetCurrent(101);
int v23 = _ytable[dialogType];
if ((flags & DIALOG_BOX_NO_VERTICAL_CENTERING) == 0) {
int v41 = _dblines[dialogType] * fontGetLineHeight() / 2 + v23;
v23 = v41 - ((bodyLength + 1) * fontGetLineHeight() / 2);
}
if (hasTitle) {
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], title, backgroundWidth, backgroundWidth, titleColor);
} else {
int length = fontGetStringWidth(title);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, title, backgroundWidth, backgroundWidth, titleColor);
}
v23 += fontGetLineHeight();
}
for (int v94 = 0; v94 < bodyLength; v94++) {
int len = fontGetStringWidth(body[v94]);
if (len <= backgroundWidth - 26) {
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], body[v94], backgroundWidth, backgroundWidth, bodyColor);
} else {
int length = fontGetStringWidth(body[v94]);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, body[v94], backgroundWidth, backgroundWidth, bodyColor);
}
v23 += fontGetLineHeight();
} else {
short beginnings[WORD_WRAP_MAX_COUNT];
short count;
if (wordWrap(body[v94], backgroundWidth - 26, beginnings, &count) != 0) {
debugPrint("\nError: dialog_out");
}
for (int v48 = 1; v48 < count; v48++) {
int v51 = beginnings[v48] - beginnings[v48 - 1];
if (v51 >= 260) {
v51 = 259;
}
char string[260];
strncpy(string, body[v94] + beginnings[v48 - 1], v51);
string[v51] = '\0';
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], string, backgroundWidth, backgroundWidth, bodyColor);
} else {
int length = fontGetStringWidth(string);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, string, backgroundWidth, backgroundWidth, bodyColor);
}
v23 += fontGetLineHeight();
}
}
}
windowRefresh(win);
int rc = -1;
while (rc == -1) {
int keyCode = _get_input();
if (keyCode == 500) {
rc = 1;
} else if (keyCode == KEY_RETURN) {
soundPlayFile("ib1p1xx1");
rc = 1;
} else if (keyCode == KEY_ESCAPE || keyCode == 501) {
rc = 0;
} else {
if ((flags & 0x10) != 0) {
if (keyCode == KEY_UPPERCASE_Y || keyCode == KEY_LOWERCASE_Y) {
rc = 1;
} else if (keyCode == KEY_UPPERCASE_N || keyCode == KEY_LOWERCASE_N) {
rc = 0;
}
}
}
if (_game_user_wants_to_quit != 0) {
rc = 1;
}
}
windowDestroy(win);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
if (v86) {
artUnlock(doneBoxHandle);
artUnlock(downButtonHandle);
artUnlock(upButtonHandle);
messageListFree(&messageList);
}
return rc;
}
// 0x41EA78
int _save_file_dialog(char* a1, char** fileList, char* fileName, int fileListLength, int x, int y, int flags)
{
int oldFont = fontGetCurrent();
unsigned char* frmBuffers[7];
CacheEntry* frmHandles[7];
Size frmSizes[7];
for (int index = 0; index < 7; index++) {
int fid = buildFid(6, _flgids2[index], 0, 0, 0);
frmBuffers[index] = artLockFrameDataReturningSize(fid, &(frmHandles[index]), &(frmSizes[index].width), &(frmSizes[index].height));
if (frmBuffers[index] == NULL) {
while (--index >= 0) {
artUnlock(frmHandles[index]);
}
return -1;
}
}
int win = windowCreate(x, y, frmSizes[0].width, frmSizes[0].height, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
if (win == -1) {
for (int index = 0; index < 7; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
unsigned char* windowBuffer = windowGetBuffer(win);
memcpy(windowBuffer, frmBuffers[0], frmSizes[0].width * frmSizes[0].height);
MessageList messageList;
MessageListItem messageListItem;
if (!messageListInit(&messageList)) {
windowDestroy(win);
for (int index = 0; index < 7; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
char path[MAX_PATH];
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
if (!messageListLoad(&messageList, path)) {
windowDestroy(win);
for (int index = 0; index < 7; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
fontSetCurrent(103);
// DONE
const char* done = getmsg(&messageList, &messageListItem, 100);
fontDrawText(windowBuffer + frmSizes[0].width * 213 + 79, done, frmSizes[0].width, frmSizes[0].width, _colorTable[18979]);
// CANCEL
const char* cancel = getmsg(&messageList, &messageListItem, 103);
fontDrawText(windowBuffer + frmSizes[0].width * 213 + 182, cancel, frmSizes[0].width, frmSizes[0].width, _colorTable[18979]);
int doneBtn = buttonCreate(win,
58,
214,
frmSizes[2].width,
frmSizes[2].height,
-1,
-1,
-1,
500,
frmBuffers[1],
frmBuffers[2],
NULL,
BUTTON_FLAG_TRANSPARENT);
if (doneBtn != -1) {
buttonSetCallbacks(doneBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
int cancelBtn = buttonCreate(win,
163,
214,
frmSizes[2].width,
frmSizes[2].height,
-1,
-1,
-1,
501,
frmBuffers[1],
frmBuffers[2],
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
buttonSetCallbacks(cancelBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
int scrollUpBtn = buttonCreate(win,
36,
44,
frmSizes[6].width,
frmSizes[6].height,
-1,
505,
506,
505,
frmBuffers[5],
frmBuffers[6],
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
buttonSetCallbacks(cancelBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
int scrollDownButton = buttonCreate(win,
36,
44 + frmSizes[6].height,
frmSizes[4].width,
frmSizes[4].height,
-1,
503,
504,
503,
frmBuffers[3],
frmBuffers[4],
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
buttonSetCallbacks(cancelBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
buttonCreate(
win,
55,
49,
190,
124,
-1,
-1,
-1,
502,
NULL,
NULL,
NULL,
0);
if (a1 != NULL) {
fontDrawText(windowBuffer + frmSizes[0].width * 16 + 49, a1, frmSizes[0].width, frmSizes[0].width, _colorTable[18979]);
}
}
// 0x41FBDC
void _PrntFlist(unsigned char* buffer, char** fileList, int pageOffset, int fileListLength, int selectedIndex, int pitch)
{
int lineHeight = fontGetLineHeight();
int y = 49;
bufferFill(buffer + y * pitch + 55, 190, 124, pitch, 100);
if (fileListLength != 0) {
if (fileListLength - pageOffset > 12) {
fileListLength = 12;
}
for (int index = 0; index < fileListLength; index++) {
int color = index == selectedIndex ? _colorTable[32747] : _colorTable[992];
fontDrawText(buffer + y * index + 55, fileList[index], pitch, pitch, color);
y += lineHeight;
}
}
}

32
src/dbox.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef DBOX_H
#define DBOX_H
typedef enum DialogBoxOptions {
DIALOG_BOX_LARGE = 0x01,
DIALOG_BOX_MEDIUM = 0x02,
DIALOG_BOX_NO_HORIZONTAL_CENTERING = 0x04,
DIALOG_BOX_NO_VERTICAL_CENTERING = 0x08,
DIALOG_BOX_YES_NO = 0x10,
DIALOG_BOX_0x20 = 0x20,
} DialogBoxOptions;
typedef enum DialogType {
DIALOG_TYPE_MEDIUM,
DIALOG_TYPE_LARGE,
DIALOG_TYPE_COUNT,
} DialogType;
extern const int gDialogBoxBackgroundFrmIds[DIALOG_TYPE_COUNT];
extern const int _ytable[DIALOG_TYPE_COUNT];
extern const int _xtable[DIALOG_TYPE_COUNT];
extern const int _doneY[DIALOG_TYPE_COUNT];
extern const int _doneX[DIALOG_TYPE_COUNT];
extern const int _dblines[DIALOG_TYPE_COUNT];
extern int _flgids[7];
extern int _flgids2[7];
int showDialogBox(const char* title, const char** body, int bodyLength, int x, int y, int titleColor, const char* a8, int bodyColor, int flags);
int _save_file_dialog(char* a1, char** fileList, char* fileName, int fileListLength, int x, int y, int flags);
void _PrntFlist(unsigned char* buffer, char** fileList, int pageOffset, int fileListLength, int selectedIndex, int pitch);
#endif /* DBOX_H */

225
src/debug.c Normal file
View File

@ -0,0 +1,225 @@
#include "debug.h"
#include "memory.h"
#include "window_manager_private.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// 0x51DEF8
FILE* _fd = NULL;
// 0x51DEFC
int _curx = 0;
// 0x51DF00
int _cury = 0;
// 0x51DF04
DebugPrintProc* gDebugPrintProc = NULL;
// 0x4C6CD0
void _GNW_debug_init()
{
atexit(_debug_exit);
}
// 0x4C6CDC
void _debug_register_mono()
{
if (gDebugPrintProc != _debug_mono) {
if (_fd != NULL) {
fclose(_fd);
_fd = NULL;
}
gDebugPrintProc = _debug_mono;
_debug_clear();
}
}
// 0x4C6D18
void _debug_register_log(const char* fileName, const char* mode)
{
if ((mode[0] == 'w' && mode[1] == 'a') && mode[1] == 't') {
if (_fd != NULL) {
fclose(_fd);
}
_fd = fopen(fileName, mode);
gDebugPrintProc = _debug_log;
}
}
// 0x4C6D5C
void _debug_register_screen()
{
if (gDebugPrintProc != _debug_screen) {
if (_fd != NULL) {
fclose(_fd);
_fd = NULL;
}
gDebugPrintProc = _debug_screen;
}
}
// 0x4C6D90
void _debug_register_env()
{
const char* type = getenv("DEBUGACTIVE");
if (type == NULL) {
return;
}
char* copy = internal_malloc(strlen(type) + 1);
if (copy == NULL) {
return;
}
strcpy(copy, type);
strlwr(copy);
if (strcmp(copy, "mono") == 0) {
// NOTE: Uninline.
_debug_register_mono();
} else if (strcmp(copy, "log") == 0) {
_debug_register_log("debug.log", "wt");
} else if (strcmp(copy, "screen") == 0) {
// NOTE: Uninline.
_debug_register_screen();
} else if (strcmp(copy, "gnw") == 0) {
if (gDebugPrintProc != _win_debug) {
if (_fd != NULL) {
fclose(_fd);
_fd = NULL;
}
gDebugPrintProc = _win_debug;
}
}
internal_free(copy);
}
// 0x4C6F18
void _debug_register_func(DebugPrintProc* proc)
{
if (gDebugPrintProc != proc) {
if (_fd != NULL) {
fclose(_fd);
_fd = NULL;
}
gDebugPrintProc = proc;
}
}
// 0x4C6F48
int debugPrint(const char* format, ...)
{
va_list args;
va_start(args, format);
int rc;
if (gDebugPrintProc != NULL) {
char string[260];
vsprintf(string, format, args);
rc = gDebugPrintProc(string);
} else {
#ifdef _DEBUG
char string[260];
vsprintf(string, format, args);
OutputDebugStringA(string);
#endif
rc = -1;
}
va_end(args);
return rc;
}
// 0x4C6F94
int _debug_puts(char* string)
{
if (gDebugPrintProc != NULL) {
return gDebugPrintProc(string);
}
return -1;
}
// 0x4C6FAC
void _debug_clear()
{
// TODO: Something with segments.
}
// 0x4C7004
int _debug_mono(char* string)
{
if (gDebugPrintProc == _debug_mono) {
while (*string != '\0') {
char ch = *string++;
_debug_putc(ch);
}
}
return 0;
}
// 0x4C7028
int _debug_log(char* string)
{
if (gDebugPrintProc == _debug_log) {
if (_fd == NULL) {
return -1;
}
if (fprintf(_fd, string) < 0) {
return -1;
}
if (fflush(_fd) == EOF) {
return -1;
}
}
return 0;
}
// 0x4C7068
int _debug_screen(char* string)
{
if (gDebugPrintProc == _debug_screen) {
printf(string);
}
return 0;
}
// 0x4C709C
void _debug_putc()
{
// TODO: Something with segments.
}
// 0x4C71AC
void _debug_scroll()
{
// TODO: Something with segments.
}
// 0x4C71E8
void _debug_exit(void)
{
if (_fd != NULL) {
fclose(_fd);
}
}

28
src/debug.h Normal file
View File

@ -0,0 +1,28 @@
#ifndef DEBUG_H
#define DEBUG_H
#include <stdio.h>
typedef int(DebugPrintProc)(char* string);
extern FILE* _fd;
extern int _curx;
extern int _cury;
extern DebugPrintProc* gDebugPrintProc;
void _GNW_debug_init();
void _debug_register_mono();
void _debug_register_log(const char* fileName, const char* mode);
void _debug_register_screen();
void _debug_register_env();
void _debug_register_func(DebugPrintProc* proc);
int debugPrint(const char* format, ...);
int _debug_puts(char* string);
void _debug_clear();
int _debug_mono(char* string);
int _debug_log(char* string);
int _debug_screen(char* string);
void _debug_putc();
void _debug_exit(void);
#endif /* DEBUG_H */

828
src/dfile.c Normal file
View File

@ -0,0 +1,828 @@
#include "dfile.h"
#include "fpattern.h"
#include <assert.h>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static_assert(sizeof(DBase) == 20, "wrong size");
static_assert(sizeof(DBaseEntry) == 20, "wrong size");
static_assert(sizeof(DFile) == 44, "wrong size");
static_assert(sizeof(DFileFindData) == 524, "wrong size");
// Reads .DAT file contents.
//
// 0x4E4F58
DBase* dbaseOpen(const char* filePath)
{
assert(filePath); // "filename", "dfile.c", 74
FILE* stream = fopen(filePath, "rb");
if (stream == NULL) {
return NULL;
}
DBase* dbase = malloc(sizeof(*dbase));
if (dbase == NULL) {
fclose(stream);
return NULL;
}
memset(dbase, 0, sizeof(*dbase));
// Get file size, and reposition stream to read footer, which contains two
// 32-bits ints.
int fileSize = filelength(fileno(stream));
if (fseek(stream, fileSize - sizeof(int) * 2, SEEK_SET) != 0) {
goto err;
}
// Read the size of entries table.
int entriesDataSize;
if (fread(&entriesDataSize, sizeof(entriesDataSize), 1, stream) != 1) {
goto err;
}
// Read the size of entire dbase content.
//
// NOTE: It appears that this approach allows existence of arbitrary data in
// the beginning of the .DAT file.
int dbaseDataSize;
if (fread(&dbaseDataSize, sizeof(dbaseDataSize), 1, stream) != 1) {
goto err;
}
// Reposition stream to the beginning of the entries table.
if (fseek(stream, fileSize - entriesDataSize - sizeof(int) * 2, SEEK_SET) != 0) {
goto err;
}
if (fread(&(dbase->entriesLength), sizeof(dbase->entriesLength), 1, stream) != 1) {
goto err;
}
dbase->entries = malloc(sizeof(*dbase->entries) * dbase->entriesLength);
if (dbase->entries == NULL) {
goto err;
}
memset(dbase->entries, 0, sizeof(*dbase->entries) * dbase->entriesLength);
// Read entries one by one, stopping on any error.
int entryIndex;
for (entryIndex = 0; entryIndex < dbase->entriesLength; entryIndex++) {
DBaseEntry* entry = &(dbase->entries[entryIndex]);
int pathLength;
if (fread(&pathLength, sizeof(pathLength), 1, stream) != 1) {
break;
}
entry->path = malloc(pathLength + 1);
if (entry->path == NULL) {
break;
}
if (fread(entry->path, pathLength, 1, stream) != 1) {
break;
}
entry->path[pathLength] = '\0';
if (fread(&(entry->compressed), sizeof(entry->compressed), 1, stream) != 1) {
break;
}
if (fread(&(entry->uncompressedSize), sizeof(entry->uncompressedSize), 1, stream) != 1) {
break;
}
if (fread(&(entry->dataSize), sizeof(entry->dataSize), 1, stream) != 1) {
break;
}
if (fread(&(entry->dataOffset), sizeof(entry->dataOffset), 1, stream) != 1) {
break;
}
}
if (entryIndex < dbase->entriesLength) {
// We haven't reached the end, which means there was an error while
// reading entries.
goto err;
}
dbase->path = strdup(filePath);
dbase->dataOffset = fileSize - dbaseDataSize;
fclose(stream);
return dbase;
err:
dbaseClose(dbase);
fclose(stream);
return NULL;
}
// Closes [dbase], all open file handles, frees all associated resources,
// including the [dbase] itself.
//
// 0x4E5270
bool dbaseClose(DBase* dbase)
{
assert(dbase); // "dbase", "dfile.c", 173
DFile* curr = dbase->dfileHead;
while (curr != NULL) {
DFile* next = curr->next;
dfileClose(curr);
curr = next;
}
if (dbase->entries != NULL) {
for (int index = 0; index < dbase->entriesLength; index++) {
DBaseEntry* entry = &(dbase->entries[index]);
char* entryName = entry->path;
if (entryName != NULL) {
free(entryName);
}
}
free(dbase->entries);
}
if (dbase->path != NULL) {
free(dbase->path);
}
memset(dbase, 0, sizeof(*dbase));
free(dbase);
return true;
}
// 0x4E5308
bool dbaseFindFirstEntry(DBase* dbase, DFileFindData* findFileData, const char* pattern)
{
for (int index = 0; index < dbase->entriesLength; index++) {
DBaseEntry* entry = &(dbase->entries[index]);
if (fpattern_match(pattern, entry->path)) {
strcpy(findFileData->fileName, entry->path);
strcpy(findFileData->pattern, pattern);
findFileData->index = index;
return true;
}
}
return false;
}
// 0x4E53A0
bool dbaseFindNextEntry(DBase* dbase, DFileFindData* findFileData)
{
for (int index = findFileData->index + 1; index < dbase->entriesLength; index++) {
DBaseEntry* entry = &(dbase->entries[index]);
if (fpattern_match(findFileData->pattern, entry->path)) {
strcpy(findFileData->fileName, entry->path);
findFileData->index = index;
return true;
}
}
return false;
}
// 0x4E541C
bool dbaseFindClose(DBase* dbase, DFileFindData* findFileData)
{
return true;
}
// [filelength].
//
// 0x4E5424
long dfileGetSize(DFile* stream)
{
return stream->entry->uncompressedSize;
}
// [fclose].
//
// 0x4E542C
int dfileClose(DFile* stream)
{
assert(stream); // "stream", "dfile.c", 253
int rc = 0;
if (stream->entry->compressed == 1) {
if (inflateEnd(stream->decompressionStream) != Z_OK) {
rc = -1;
}
}
if (stream->decompressionStream != NULL) {
free(stream->decompressionStream);
}
if (stream->decompressionBuffer != NULL) {
free(stream->decompressionBuffer);
}
if (stream->stream != NULL) {
fclose(stream->stream);
}
// Loop thru open file handles and find previous to remove current handle
// from linked list.
//
// NOTE: Compiled code is slightly different.
DFile* curr = stream->dbase->dfileHead;
DFile* prev = NULL;
while (curr != NULL) {
if (curr == stream) {
break;
}
prev = curr;
curr = curr->next;
}
if (curr != NULL) {
if (prev == NULL) {
stream->dbase->dfileHead = stream->next;
} else {
prev->next = stream->next;
}
}
memset(stream, 0, sizeof(*stream));
free(stream);
return rc;
}
// [fopen].
//
// 0x4E5504
DFile* dfileOpen(DBase* dbase, const char* filePath, const char* mode)
{
assert(dbase); // dfile.c, 295
assert(filePath); // dfile.c, 296
assert(mode); // dfile.c, 297
return dfileOpenInternal(dbase, filePath, mode, 0);
}
// [vfprintf].
//
// 0x4E56C0
int dfilePrintFormattedArgs(DFile* stream, const char* format, va_list args)
{
assert(stream); // "stream", "dfile.c", 368
assert(format); // "format", "dfile.c", 369
return -1;
}
// [fgetc].
//
// This function reports \r\n sequence as one character \n, even though it
// consumes two characters from the underlying stream.
//
// 0x4E5700
int dfileReadChar(DFile* stream)
{
assert(stream); // "stream", "dfile.c", 384
if ((stream->flags & DFILE_EOF) != 0 || (stream->flags & DFILE_ERROR) != 0) {
return -1;
}
if ((stream->flags & DFILE_HAS_UNGETC) != 0) {
stream->flags &= ~DFILE_HAS_UNGETC;
return stream->ungotten;
}
int ch = dfileReadCharInternal(stream);
if (ch == -1) {
stream->flags |= DFILE_EOF;
}
return ch;
}
// [fgets].
//
// Both Windows (\r\n) and Unix (\n) line endings are recognized. Windows
// line ending is reported as \n.
//
// 0x4E5764
char* dfileReadString(char* string, int size, DFile* stream)
{
assert(string); // "s", "dfile.c", 407
assert(size); // "n", "dfile.c", 408
assert(stream); // "stream", "dfile.c", 409
if ((stream->flags & DFILE_EOF) != 0 || (stream->flags & DFILE_ERROR) != 0) {
return NULL;
}
char* pch = string;
if ((stream->flags & DFILE_HAS_UNGETC) != 0) {
*pch++ = stream->ungotten & 0xFF;
size--;
stream->flags &= ~DFILE_HAS_UNGETC;
}
// Read up to size - 1 characters one by one saving space for the null
// terminator.
for (int index = 0; index < size - 1; index++) {
int ch = dfileReadCharInternal(stream);
if (ch == -1) {
break;
}
*pch++ = ch & 0xFF;
if (ch == '\n') {
break;
}
}
if (pch == string) {
// No character was set into the buffer.
return NULL;
}
*pch = '\0';
return string;
}
// [fputc].
//
// 0x4E5830
int dfileWriteChar(int ch, DFile* stream)
{
assert(stream); // "stream", "dfile.c", 437
return -1;
}
// [fputs].
//
// 0x4E5854
int dfileWriteString(const char* string, DFile* stream)
{
assert(string); // "s", "dfile.c", 448
assert(stream); // "stream", "dfile.c", 449
return -1;
}
// [fread].
//
// 0x4E58FC
size_t dfileRead(void* ptr, size_t size, size_t count, DFile* stream)
{
assert(ptr); // "ptr", "dfile.c", 499
assert(stream); // "stream", dfile.c, 500
if ((stream->flags & DFILE_EOF) != 0 || (stream->flags & DFILE_ERROR) != 0) {
return 0;
}
size_t remainingSize = stream->entry->uncompressedSize - stream->position;
if ((stream->flags & DFILE_HAS_UNGETC) != 0) {
remainingSize++;
}
size_t bytesToRead = size * count;
if (remainingSize < bytesToRead) {
bytesToRead = remainingSize;
stream->flags |= DFILE_EOF;
}
size_t extraBytesRead = 0;
if ((stream->flags & DFILE_HAS_UNGETC) != 0) {
unsigned char* byteBuffer = ptr;
*byteBuffer++ = stream->ungotten & 0xFF;
ptr = byteBuffer;
bytesToRead--;
stream->flags &= ~DFILE_HAS_UNGETC;
extraBytesRead = 1;
}
size_t bytesRead;
if (stream->entry->compressed == 1) {
if (!dfileReadCompressed(stream, ptr, bytesToRead)) {
stream->flags |= DFILE_ERROR;
return false;
}
bytesRead = bytesToRead;
} else {
bytesRead = fread(ptr, 1, bytesToRead, stream->stream) + extraBytesRead;
stream->position += bytesRead;
}
return bytesRead / size;
}
// [fwrite].
//
// 0x4E59F8
size_t dfileWrite(const void* ptr, size_t size, size_t count, DFile* stream)
{
assert(ptr); // "ptr", "dfile.c", 538
assert(stream); // "stream", "dfile.c", 539
return count - 1;
}
// [fseek].
//
// 0x4E5A74
int dfileSeek(DFile* stream, long offset, int origin)
{
assert(stream); // "stream", "dfile.c", 569
if ((stream->flags & DFILE_ERROR) != 0) {
return 1;
}
if ((stream->flags & DFILE_TEXT) != 0) {
if (offset != 0 && origin != SEEK_SET) {
// NOTE: For unknown reason this function does not allow arbitrary
// seeks in text streams, whether compressed or not. It only
// supports rewinding. Probably because of reading functions which
// handle \r\n sequence as \n.
return 1;
}
}
long offsetFromBeginning;
switch (origin) {
case SEEK_SET:
offsetFromBeginning = offset;
break;
case SEEK_CUR:
offsetFromBeginning = stream->position + offset;
break;
case SEEK_END:
offsetFromBeginning = stream->entry->uncompressedSize + offset;
break;
default:
return 1;
}
if (offsetFromBeginning >= stream->entry->uncompressedSize) {
return 1;
}
long pos = stream->position;
if (offsetFromBeginning == pos) {
stream->flags &= ~(DFILE_HAS_UNGETC | DFILE_EOF);
return 0;
}
if (offsetFromBeginning != 0) {
if (stream->entry->compressed == 1) {
if (offsetFromBeginning < pos) {
// We cannot go backwards in compressed stream, so the only way
// is to start from the beginning.
dfileRewind(stream);
}
// Consume characters one by one until we reach specified offset.
while (offsetFromBeginning > stream->position) {
if (dfileReadCharInternal(stream) == -1) {
return 1;
}
}
} else {
if (fseek(stream->stream, offsetFromBeginning - pos, SEEK_CUR) != 0) {
stream->flags |= DFILE_ERROR;
return 1;
}
// FIXME: I'm not sure what this assignment means. This field is
// only meaningful when reading compressed streams.
stream->compressedBytesRead = offsetFromBeginning;
}
stream->flags &= ~(DFILE_HAS_UNGETC | DFILE_EOF);
return 0;
}
if (fseek(stream->stream, stream->dbase->dataOffset + stream->entry->dataOffset, SEEK_SET) != 0) {
stream->flags |= DFILE_ERROR;
return 1;
}
if (inflateEnd(stream->decompressionStream) != Z_OK) {
stream->flags |= DFILE_ERROR;
return 1;
}
stream->decompressionStream->zalloc = Z_NULL;
stream->decompressionStream->zfree = Z_NULL;
stream->decompressionStream->opaque = Z_NULL;
stream->decompressionStream->next_in = stream->decompressionBuffer;
stream->decompressionStream->avail_in = 0;
if (inflateInit(stream->decompressionStream) != Z_OK) {
stream->flags |= DFILE_ERROR;
return 1;
}
stream->position = 0;
stream->compressedBytesRead = 0;
stream->flags &= ~(DFILE_HAS_UNGETC | DFILE_EOF);
return 0;
}
// [ftell].
//
// 0x4E5C88
long dfileTell(DFile* stream)
{
assert(stream); // "stream", "dfile.c", 654
return stream->position;
}
// [rewind].
//
// 0x4E5CB0
void dfileRewind(DFile* stream)
{
assert(stream); // "stream", "dfile.c", 664
dfileSeek(stream, 0, SEEK_SET);
stream->flags &= ~DFILE_ERROR;
}
// [feof].
//
// 0x4E5D10
int dfileEof(DFile* stream)
{
assert(stream); // "stream", "dfile.c", 685
return stream->flags & DFILE_EOF;
}
// The [bsearch] comparison callback, which is used to find [DBaseEntry] for
// specified [filePath].
//
// 0x4E5D70
int dbaseFindEntryByFilePath(const void* a1, const void* a2)
{
const char* filePath = (const char*)a1;
DBaseEntry* entry = (DBaseEntry*)a2;
return stricmp(filePath, entry->path);
}
// 0x4E5D9C
DFile* dfileOpenInternal(DBase* dbase, const char* filePath, const char* mode, DFile* dfile)
{
DBaseEntry* entry = bsearch(filePath, dbase->entries, dbase->entriesLength, sizeof(*dbase->entries), dbaseFindEntryByFilePath);
if (entry == NULL) {
goto err;
}
if (mode[0] != 'r') {
goto err;
}
if (dfile == NULL) {
dfile = malloc(sizeof(*dfile));
if (dfile == NULL) {
return NULL;
}
memset(dfile, 0, sizeof(*dfile));
dfile->dbase = dbase;
dfile->next = dbase->dfileHead;
dbase->dfileHead = dfile;
} else {
if (dbase != dfile->dbase) {
goto err;
}
if (dfile->stream != NULL) {
fclose(dfile->stream);
dfile->stream = NULL;
}
dfile->compressedBytesRead = 0;
dfile->position = 0;
dfile->flags = 0;
}
dfile->entry = entry;
// Open stream to .DAT file.
dfile->stream = fopen(dbase->path, "rb");
if (dfile->stream == NULL) {
goto err;
}
// Relocate stream to the beginning of data for specified entry.
if (fseek(dfile->stream, dbase->dataOffset + entry->dataOffset, SEEK_SET) != 0) {
goto err;
}
if (entry->compressed == 1) {
// Entry is compressed, setup decompression stream and decompression
// buffer. This step is not needed when previous instance of dfile is
// passed via parameter, which might already have stream and
// buffer allocated.
if (dfile->decompressionStream == NULL) {
dfile->decompressionStream = malloc(sizeof(*dfile->decompressionStream));
if (dfile->decompressionStream == NULL) {
goto err;
}
dfile->decompressionBuffer = malloc(DFILE_DECOMPRESSION_BUFFER_SIZE);
if (dfile->decompressionBuffer == NULL) {
goto err;
}
}
dfile->decompressionStream->zalloc = Z_NULL;
dfile->decompressionStream->zfree = Z_NULL;
dfile->decompressionStream->opaque = Z_NULL;
dfile->decompressionStream->next_in = dfile->decompressionBuffer;
dfile->decompressionStream->avail_in = 0;
if (inflateInit(dfile->decompressionStream) != Z_OK) {
goto err;
}
} else {
// Entry is not compressed, there is no need to keep decompression
// stream and decompression buffer (in case [dfile] was passed via
// parameter).
if (dfile->decompressionStream != NULL) {
free(dfile->decompressionStream);
dfile->decompressionStream = NULL;
}
if (dfile->decompressionBuffer != NULL) {
free(dfile->decompressionBuffer);
dfile->decompressionBuffer = NULL;
}
}
if (mode[1] == 't') {
dfile->flags |= DFILE_TEXT;
}
return dfile;
err:
if (dfile != NULL) {
dfileClose(dfile);
}
return NULL;
}
// 0x4E5F9C
int dfileReadCharInternal(DFile* stream)
{
if (stream->entry->compressed == 1) {
char ch;
if (!dfileReadCompressed(stream, &ch, sizeof(ch))) {
return -1;
}
if ((stream->flags & DFILE_TEXT) != 0) {
// NOTE: I'm not sure if they are comparing as chars or ints. Since
// character literals are ints, let's cast read characters to int as
// well.
if (ch == '\r') {
char nextCh;
if (dfileReadCompressed(stream, &nextCh, sizeof(nextCh))) {
if (nextCh == '\n') {
ch = nextCh;
} else {
// NOTE: Uninline.
dfileUngetCompressed(stream, nextCh & 0xFF);
}
}
}
}
return ch & 0xFF;
}
if (stream->position >= stream->entry->uncompressedSize) {
return -1;
}
int ch = fgetc(stream->stream);
if (ch != -1) {
if ((stream->flags & DFILE_TEXT) != 0) {
// This is a text stream, attempt to detect \r\n sequence.
if (ch == '\r') {
if (stream->position + 1 < stream->entry->uncompressedSize) {
int nextCh = fgetc(stream->stream);
if (nextCh == '\n') {
ch = nextCh;
stream->position++;
} else {
ungetc(nextCh, stream->stream);
}
}
}
}
stream->position++;
}
return ch;
}
// 0x4E6078
bool dfileReadCompressed(DFile* stream, void* ptr, size_t size)
{
if ((stream->flags & DFILE_HAS_COMPRESSED_UNGETC) != 0) {
unsigned char* byteBuffer = ptr;
*byteBuffer++ = stream->compressedUngotten & 0xFF;
ptr = byteBuffer;
size--;
stream->flags &= ~DFILE_HAS_COMPRESSED_UNGETC;
stream->position++;
if (size == 0) {
return true;
}
}
stream->decompressionStream->next_out = ptr;
stream->decompressionStream->avail_out = size;
do {
if (stream->decompressionStream->avail_out == 0) {
// Everything was decompressed.
break;
}
if (stream->decompressionStream->avail_in == 0) {
// No more unprocessed data, request next chunk.
size_t bytesToRead = stream->entry->dataSize - stream->compressedBytesRead;
if (bytesToRead > DFILE_DECOMPRESSION_BUFFER_SIZE) {
bytesToRead = DFILE_DECOMPRESSION_BUFFER_SIZE;
}
if (fread(stream->decompressionBuffer, bytesToRead, 1, stream->stream) != 1) {
break;
}
stream->decompressionStream->avail_in = bytesToRead;
stream->decompressionStream->next_in = stream->decompressionBuffer;
stream->compressedBytesRead += bytesToRead;
}
} while (inflate(stream->decompressionStream, Z_NO_FLUSH) == Z_OK);
if (stream->decompressionStream->avail_out != 0) {
// There are some data still waiting, which means there was in error
// during decompression loop above.
return false;
}
stream->position += size;
return true;
}
// NOTE: Inlined.
//
// 0x4E613C
void dfileUngetCompressed(DFile* stream, int ch)
{
stream->compressedUngotten = ch;
stream->flags |= DFILE_HAS_COMPRESSED_UNGETC;
stream->position--;
}

161
src/dfile.h Normal file
View File

@ -0,0 +1,161 @@
#ifndef DFILE_H
#define DFILE_H
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdbool.h>
#include <stdio.h>
#include <zlib.h>
// The size of decompression buffer for reading compressed [DFile]s.
#define DFILE_DECOMPRESSION_BUFFER_SIZE (0x400)
// Specifies that [DFile] has unget character.
//
// NOTE: There is an unused function at 0x4E5894 which ungets one character and
// stores it in [ungotten]. Since that function is not used, this flag will
// never be set.
#define DFILE_HAS_UNGETC (0x01)
// Specifies that [DFile] has reached end of stream.
#define DFILE_EOF (0x02)
// Specifies that [DFile] is in error state.
//
// [dfileRewind] can be used to clear this flag.
#define DFILE_ERROR (0x04)
// Specifies that [DFile] was opened in text mode.
#define DFILE_TEXT (0x08)
// Specifies that [DFile] has unget compressed character.
#define DFILE_HAS_COMPRESSED_UNGETC (0x10)
typedef struct DBase DBase;
typedef struct DBaseEntry DBaseEntry;
typedef struct DFile DFile;
// A representation of .DAT file.
typedef struct DBase {
// The path of .DAT file that this structure represents.
char* path;
// The offset to the beginning of data section of .DAT file.
int dataOffset;
// The number of entries.
int entriesLength;
// The array of entries.
DBaseEntry* entries;
// The head of linked list of open file handles.
DFile* dfileHead;
} DBase;
typedef struct DBaseEntry {
char* path;
unsigned char compressed;
int uncompressedSize;
int dataSize;
int dataOffset;
} DBaseEntry;
// A handle to open entry in .DAT file.
typedef struct DFile {
DBase* dbase;
DBaseEntry* entry;
int flags;
// The stream of .DAT file opened for reading in binary mode.
//
// This stream is not shared across open handles. Instead every [DFile]
// opens it's own stream via [fopen], which is then closed via [fclose] in
// [dfileClose].
FILE* stream;
// The inflate stream used to decompress data.
//
// This value is NULL if entry is not compressed.
z_streamp decompressionStream;
// The decompression buffer of size [DFILE_DECOMPRESSION_BUFFER_SIZE].
//
// This value is NULL if entry is not compressed.
unsigned char* decompressionBuffer;
// The last ungot character.
//
// See [DFILE_HAS_UNGETC] notes.
int ungotten;
// The last ungot compressed character.
//
// This value is used when reading compressed text streams to detect
// Windows end of line sequence \r\n.
int compressedUngotten;
// The number of bytes read so far from compressed stream.
//
// This value is only used when reading compressed streams. The range is
// 0..entry->dataSize.
int compressedBytesRead;
// The position in read stream.
//
// This value is tracked in terms of uncompressed data (even in compressed
// streams). The range is 0..entry->uncompressedSize.
long position;
// Next [DFile] in linked list.
//
// [DFile]s are stored in [DBase] in reverse order, so it's actually a
// previous opened file, not next.
DFile* next;
} DFile;
typedef struct DFileFindData {
// The name of file that was found during previous search.
char fileName[MAX_PATH];
// The pattern to search.
//
// This value is set automatically when [dbaseFindFirstEntry] succeeds so
// that subsequent calls to [dbaseFindNextEntry] know what to look for.
char pattern[MAX_PATH];
// The index of entry that was found during previous search.
//
// This value is set automatically when [dbaseFindFirstEntry] and
// [dbaseFindNextEntry] succeed so that subsequent calls to [dbaseFindNextEntry]
// knows where to start search from.
int index;
} DFileFindData;
DBase* dbaseOpen(const char* filename);
bool dbaseClose(DBase* dbase);
bool dbaseFindFirstEntry(DBase* dbase, DFileFindData* findFileData, const char* pattern);
bool dbaseFindNextEntry(DBase* dbase, DFileFindData* findFileData);
bool dbaseFindClose(DBase* dbase, DFileFindData* findFileData);
long dfileGetSize(DFile* stream);
int dfileClose(DFile* stream);
DFile* dfileOpen(DBase* dbase, const char* filename, const char* mode);
int dfilePrintFormattedArgs(DFile* stream, const char* format, va_list args);
int dfileReadChar(DFile* stream);
char* dfileReadString(char* str, int size, DFile* stream);
int dfileWriteChar(int ch, DFile* stream);
int dfileWriteString(const char* s, DFile* stream);
size_t dfileRead(void* ptr, size_t size, size_t count, DFile* stream);
size_t dfileWrite(const void* ptr, size_t size, size_t count, DFile* stream);
int dfileSeek(DFile* stream, long offset, int origin);
long dfileTell(DFile* stream);
void dfileRewind(DFile* stream);
int dfileEof(DFile* stream);
int dbaseFindEntryByFilePath(const void* a1, const void* a2);
DFile* dfileOpenInternal(DBase* dbase, const char* filename, const char* mode, DFile* a4);
int dfileReadCharInternal(DFile* stream);
bool dfileReadCompressed(DFile* stream, void* ptr, size_t size);
void dfileUngetCompressed(DFile* stream, int ch);
#endif /* DFILE_H */

733
src/dialog.c Normal file
View File

@ -0,0 +1,733 @@
#include "dialog.h"
#include "core.h"
#include "memory_manager.h"
#include "movie.h"
#include "text_font.h"
#include "widget.h"
#include "window_manager.h"
#include <string.h>
// 0x501623
const float flt_501623 = 31.0;
// 0x501627
const float flt_501627 = 31.0;
// 0x5184B4
int _tods = -1;
// 0x5184B8
int _topDialogLine = 0;
// 0x5184BC
int _topDialogReply = 0;
// 0x5184E4
DialogFunc1* _replyWinDrawCallback = NULL;
// 0x5184E8
DialogFunc2* _optionsWinDrawCallback = NULL;
// 0x5184EC
int gDialogBorderX = 7;
// 0x5184F0
int gDialogBorderY = 7;
// 0x5184F4
int gDialogOptionSpacing = 5;
// 0x5184F8
int _replyRGBset = 0;
// 0x5184FC
int _optionRGBset = 0;
// 0x518500
int _exitDialog = 0;
// 0x518504
int _inDialog = 0;
// 0x518508
int _mediaFlag = 2;
// 0x56DAE0
STRUCT_56DAE0 _dialog[4];
// Reply flags.
//
// 0x56DB60
short word_56DB60;
// 0x56DB64
int dword_56DB64;
// 0x56DB68
int dword_56DB68;
// 0x56DB6C
int dword_56DB6C;
// 0x56DB70
int dword_56DB70;
// 0x56DB74
int _rand2plus;
// 0x56DB7C
int dword_56DB7C;
// 0x56DB80
int dword_56DB80;
// 0x56DB84
int dword_56DB84;
// 0x56DB88
int dword_56DB88;
// 0x56DB8C
int dword_56DB8C;
// 0x56DB90
int _replyPlaying;
// 0x56DB94
int _replyWin = -1;
// 0x56DB98
int gDialogReplyColorG;
// 0x56DB9C
int gDialogReplyColorB;
// 0x56DBA4
int gDialogOptionColorG;
// 0x56DBA8
int gDialogReplyColorR;
// 0x56DBAC
int gDialogOptionColorB;
// 0x56DBB0
int gDialogOptionColorR;
// 0x56DBB4
int _downButton;
// 0x56DBB8
int dword_56DBB8;
// 0x56DBBC
int dword_56DBBC;
// 0x56DBC0
void* off_56DBC0;
// 0x56DBC4
void* off_56DBC4;
// 0x56DBC8
void* off_56DBC8;
// 0x56DBCC
void* off_56DBCC;
// 0x56DBD0
char* gDialogReplyTitle;
// 0x56DBD4
int _upButton;
// 0x56DBD8
int dword_56DBD8;
// 0x56DBDC
int dword_56DBDC;
// 0x56DBE0
void* off_56DBE0;
// 0x56DBE4
void* off_56DBE4;
// 0x56DBE8
void* off_56DBE8;
// 0x56DBEC
void* off_56DBEC;
// 0x42F434
STRUCT_56DAE0_FIELD_4* _getReply()
{
STRUCT_56DAE0_FIELD_4* v0;
STRUCT_56DAE0_FIELD_4_FIELD_C* v1;
v0 = &(_dialog[_tods].field_4[_dialog[_tods].field_C]);
if (v0->field_C == NULL) {
v0->field_14 = 1;
v1 = internal_malloc_safe(sizeof(STRUCT_56DAE0_FIELD_4_FIELD_C), __FILE__, __LINE__); // "..\\int\\DIALOG.C", 789
} else {
v0->field_14++;
v1 = internal_realloc_safe(v0->field_C, sizeof(STRUCT_56DAE0_FIELD_4_FIELD_C) * v0->field_14, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 793
}
v0->field_C = v1;
return v0;
}
// 0x42F4C0
void _replyAddOption(const char* a1, const char* a2, int a3)
{
STRUCT_56DAE0_FIELD_4* v18;
int v17;
char* v14;
char* v15;
v18 = _getReply();
v17 = v18->field_14 - 1;
v18->field_C[v17].field_8 = 2;
if (a1 != NULL) {
v14 = internal_malloc_safe(strlen(a1) + 1, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 805
strcpy(v14, a1);
v18->field_C[v17].field_0 = v14;
} else {
v18->field_C[v17].field_0 = NULL;
}
if (a2 != NULL) {
v15 = internal_malloc_safe(strlen(a2) + 1, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 810
strcpy(v15, a2);
v18->field_C[v17].field_4 = v15;
} else {
v18->field_C[v17].field_4 = NULL;
}
v18->field_C[v17].field_18 = widgetGetFont();
v18->field_C[v17].field_1A = word_56DB60;
v18->field_C[v17].field_14 = a3;
}
// 0x42F624
void _replyAddOptionProc(const char* a1, const char* a2, int a3)
{
STRUCT_56DAE0_FIELD_4* v5;
int v13;
char* v11;
v5 = _getReply();
v13 = v5->field_14 - 1;
v5->field_C[v13].field_8 = 1;
if (a1 != NULL) {
v11 = internal_malloc_safe(strlen(a1) + 1, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 830
strcpy(v11, a1);
v5->field_C[v13].field_0 = v11;
} else {
v5->field_C[v13].field_0 = NULL;
}
v5->field_C[v13].field_4 = (char*)a2;
v5->field_C[v13].field_18 = widgetGetFont();
v5->field_C[v13].field_1A = word_56DB60;
v5->field_C[v13].field_14 = a3;
}
// 0x42F714
void _optionFree(STRUCT_56DAE0_FIELD_4_FIELD_C* a1)
{
if (a1->field_0 != NULL) {
internal_free_safe(a1->field_0, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 844
}
if (a1->field_8 == 2) {
if (a1->field_4 != NULL) {
internal_free_safe(a1->field_4, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 846
}
}
}
// 0x42F754
void _replyFree()
{
int i;
int j;
STRUCT_56DAE0* ptr;
STRUCT_56DAE0_FIELD_4* v6;
ptr = &(_dialog[_tods]);
for (i = 0; i < ptr->field_8; i++) {
v6 = &(_dialog[_tods].field_4[i]);
if (v6->field_C != NULL) {
for (j = 0; j < v6->field_14; j++) {
_optionFree(&(v6->field_C[j]));
}
internal_free_safe(v6->field_C, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 857
}
if (v6->field_8 != NULL) {
internal_free_safe(v6->field_8, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 860
}
if (v6->field_4 != NULL) {
internal_free_safe(v6->field_4, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 862
}
if (v6->field_0 != NULL) {
internal_free_safe(v6->field_0, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 864
}
}
if (ptr->field_4 != NULL) {
internal_free_safe(ptr->field_4, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 867
}
}
// 0x42FB94
int _endDialog()
{
if (_tods == -1) {
return -1;
}
_topDialogReply = _dialog[_tods].field_10;
_replyFree();
if (gDialogReplyTitle != NULL) {
internal_free_safe(gDialogReplyTitle, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 986
gDialogReplyTitle = NULL;
}
--_tods;
return 0;
}
// 0x42FC70
void _printLine(int win, char** strings, int strings_num, int a4, int a5, int a6, int a7, int a8, int a9)
{
int i;
int v11;
for (i = 0; i < strings_num; i++) {
v11 = a7 + i * fontGetLineHeight();
_windowPrintBuf(win, strings[i], strlen(strings[i]), a4, a5 + a7, a6, v11, a8, a9);
}
}
// 0x42FCF0
void _printStr(int win, char* a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9)
{
char** strings;
int strings_num;
strings = _windowWordWrap(a2, a3, 0, &strings_num);
_printLine(win, strings, strings_num, a3, a4, a5, a6, a7, a8);
_windowFreeWordList(strings, strings_num);
}
// 0x430104
int _abortReply(int a1)
{
int result;
int y;
int x;
if (_replyPlaying == 2) {
return _moviePlaying() == 0;
} else if (_replyPlaying == 3) {
return 1;
}
result = 1;
if (a1) {
if (_replyWin != -1) {
if (!(mouseGetEvent() & 0x10)) {
result = 0;
} else {
mouseGetPosition(&x, &y);
if (windowGetAtPoint(x, y) != _replyWin) {
result = 0;
}
}
}
}
return result;
}
// 0x430180
void _endReply()
{
if (_replyPlaying != 2) {
if (_replyPlaying == 1) {
if (!(_mediaFlag & 2) && _replyWin != -1) {
windowDestroy(_replyWin);
_replyWin = -1;
}
} else if (_replyPlaying != 3 && _replyWin != -1) {
windowDestroy(_replyWin);
_replyWin = -1;
}
}
}
// 0x4301E8
void _drawStr(int win, char* str, int font, int width, int height, int left, int top, int a8, int a9, int a10)
{
int old_font;
Rect rect;
old_font = widgetGetFont();
widgetSetFont(font);
_printStr(win, str, width, height, left, top, a8, a9, a10);
rect.left = left;
rect.top = top;
rect.right = width + left;
rect.bottom = height + top;
windowRefreshRect(win, &rect);
widgetSetFont(old_font);
}
// 0x430D40
int _dialogStart(Program* a1)
{
STRUCT_56DAE0* ptr;
if (_tods == 3) {
return 1;
}
ptr = &(_dialog[_tods]);
ptr->field_0 = a1;
ptr->field_4 = 0;
ptr->field_8 = 0;
ptr->field_C = -1;
ptr->field_10 = -1;
ptr->field_14 = 1;
ptr->field_10 = 1;
_tods++;
return 0;
}
// 0x430DB8
int _dialogRestart()
{
if (_tods == -1) {
return 1;
}
_dialog[_tods].field_10 = 0;
return 0;
}
// 0x430DE4
int _dialogGotoReply(const char* a1)
{
STRUCT_56DAE0* ptr;
STRUCT_56DAE0_FIELD_4* v5;
int i;
if (_tods == -1) {
return 1;
}
if (a1 != NULL) {
ptr = &(_dialog[_tods]);
for (i = 0; i < ptr->field_8; i++) {
v5 = &(ptr->field_4[i]);
if (v5->field_4 != NULL && stricmp(v5->field_4, a1) == 0) {
ptr->field_10 = i;
return 0;
}
}
return 1;
}
_dialog[_tods].field_10 = 0;
return 0;
}
// 0x430E84
int dialogSetReplyTitle(const char* a1)
{
if (gDialogReplyTitle != NULL) {
internal_free_safe(gDialogReplyTitle, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2561
}
if (a1 != NULL) {
gDialogReplyTitle = internal_malloc_safe(strlen(a1) + 1, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2564
strcpy(gDialogReplyTitle, a1);
} else {
gDialogReplyTitle = NULL;
}
return 0;
}
// 0x430EFC
int _dialogReply(const char* a1, const char* a2)
{
// TODO: Incomplete.
// _replyAddNew(a1, a2);
return 0;
}
// 0x430F04
int _dialogOption(const char* a1, const char* a2)
{
if (_dialog[_tods].field_C == -1) {
return 0;
}
_replyAddOption(a1, a2, 0);
return 0;
}
// 0x430F38
int _dialogOptionProc(const char* a1, const char* a2)
{
if (_dialog[_tods].field_C == -1) {
return 1;
}
_replyAddOptionProc(a1, a2, 0);
return 0;
}
// 0x431184
int _dialogGetExitPoint()
{
return _topDialogLine + (_topDialogReply << 16);
}
// 0x431198
int _dialogQuit()
{
if (_inDialog) {
_exitDialog = 1;
} else {
_endDialog();
}
return 0;
}
// 0x4311B8
int dialogSetOptionWindow(int a1, int a2, int a3, int a4, int a5)
{
dword_56DB6C = a1;
dword_56DB70 = a2;
dword_56DB64 = a3;
dword_56DB68 = a4;
_rand2plus = a5;
return 0;
}
// 0x4311E0
int dialogSetReplyWindow(int a1, int a2, int a3, int a4, int a5)
{
dword_56DB84 = a1;
dword_56DB88 = a2;
dword_56DB7C = a3;
dword_56DB80 = a4;
dword_56DB8C = a5;
return 0;
}
// 0x431208
int dialogSetBorder(int a1, int a2)
{
gDialogBorderX = a1;
gDialogBorderY = a2;
return 0;
}
// 0x431218
int _dialogSetScrollUp(int a1, int a2, void* a3, void* a4, void* a5, void* a6, int a7)
{
_upButton = a1;
dword_56DBD8 = a2;
if (off_56DBE0 != NULL) {
internal_free_safe(off_56DBE0, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2750
}
off_56DBE0 = a3;
if (off_56DBE4 != NULL) {
internal_free_safe(off_56DBE4, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2752
}
off_56DBE4 = a4;
if (off_56DBE8 != NULL) {
internal_free_safe(off_56DBE8, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2754
}
off_56DBE8 = a5;
if (off_56DBEC != NULL) {
internal_free_safe(off_56DBEC, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2756
}
off_56DBEC = a5;
dword_56DBDC = a7;
return 0;
}
// 0x4312C0
int _dialogSetScrollDown(int a1, int a2, void* a3, void* a4, void* a5, void* a6, int a7)
{
_downButton = a1;
dword_56DBB8 = a2;
if (off_56DBC0 != NULL) {
internal_free_safe(off_56DBC0, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2765
}
off_56DBC0 = a3;
if (off_56DBC4 != NULL) {
internal_free_safe(off_56DBC4, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2767
}
off_56DBC4 = a4;
if (off_56DBC8 != NULL) {
internal_free_safe(off_56DBC8, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2769
}
off_56DBC8 = a5;
if (off_56DBCC != NULL) {
internal_free_safe(off_56DBCC, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2771
}
off_56DBCC = a6;
dword_56DBBC = a7;
return 0;
}
// 0x431368
int dialogSetOptionSpacing(int value)
{
gDialogOptionSpacing = value;
return 0;
}
// 0x431370
int dialogSetOptionColor(float a1, float a2, float a3)
{
gDialogOptionColorR = (int)(a1 * flt_501623);
gDialogOptionColorG = (int)(a2 * flt_501623);
gDialogOptionColorB = (int)(a3 * flt_501623);
_optionRGBset = 1;
return 0;
}
// 0x4313C8
int dialogSetReplyColor(float a1, float a2, float a3)
{
gDialogReplyColorR = (int)(a1 * flt_501627);
gDialogReplyColorG = (int)(a2 * flt_501627);
gDialogReplyColorB = (int)(a3 * flt_501627);
_replyRGBset = 1;
return 0;
}
// 0x431420
int _dialogSetOptionFlags(int flags)
{
word_56DB60 = flags & 0xFFFF;
return 1;
}
// 0x431434
void _dialogClose()
{
if (off_56DBE0) {
internal_free_safe(off_56DBE0, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2818
}
if (off_56DBE4) {
internal_free_safe(off_56DBE4, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2819
}
if (off_56DBE8) {
internal_free_safe(off_56DBE8, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2820
}
if (off_56DBEC) {
internal_free_safe(off_56DBEC, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2821
}
if (off_56DBC0) {
internal_free_safe(off_56DBC0, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2823
}
if (off_56DBC4) {
internal_free_safe(off_56DBC4, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2824
}
if (off_56DBC8) {
internal_free_safe(off_56DBC8, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2825
}
if (off_56DBCC) {
internal_free_safe(off_56DBCC, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 2826
}
}
// 0x431518
int _dialogGetDialogDepth()
{
return _tods;
}
// 0x431520
void _dialogRegisterWinDrawCallbacks(DialogFunc1* a1, DialogFunc2* a2)
{
_replyWinDrawCallback = a1;
_optionsWinDrawCallback = a2;
}
// 0x431530
int _dialogToggleMediaFlag(int a1)
{
if ((a1 & _mediaFlag) == a1) {
_mediaFlag &= ~a1;
} else {
_mediaFlag |= a1;
}
return _mediaFlag;
}
// 0x431554
int _dialogGetMediaFlag()
{
return _mediaFlag;
}

128
src/dialog.h Normal file
View File

@ -0,0 +1,128 @@
#ifndef DIALOG_H
#define DIALOG_H
#include "interpreter.h"
typedef void DialogFunc1(int win);
typedef void DialogFunc2(int win);
typedef struct STRUCT_56DAE0_FIELD_4_FIELD_C {
char* field_0;
char* field_4;
int field_8;
int field_C;
int field_10;
int field_14;
short field_18;
short field_1A;
} STRUCT_56DAE0_FIELD_4_FIELD_C;
typedef struct STRUCT_56DAE0_FIELD_4 {
void* field_0;
char* field_4;
void* field_8;
STRUCT_56DAE0_FIELD_4_FIELD_C* field_C;
int field_10;
int field_14;
int field_18; // probably font number
} STRUCT_56DAE0_FIELD_4;
typedef struct STRUCT_56DAE0 {
Program* field_0;
STRUCT_56DAE0_FIELD_4* field_4;
int field_8;
int field_C;
int field_10;
int field_14;
int field_18;
} STRUCT_56DAE0;
extern const float flt_501623;
extern const float flt_501627;
extern int _tods;
extern int _topDialogLine;
extern int _topDialogReply;
extern DialogFunc1* _replyWinDrawCallback;
extern DialogFunc2* _optionsWinDrawCallback;
extern int gDialogBorderX;
extern int gDialogBorderY;
extern int gDialogOptionSpacing;
extern int _replyRGBset;
extern int _optionRGBset;
extern int _exitDialog;
extern int _inDialog;
extern int _mediaFlag;
extern STRUCT_56DAE0 _dialog[4];
extern short word_56DB60;
extern int dword_56DB64;
extern int dword_56DB68;
extern int dword_56DB6C;
extern int dword_56DB70;
extern int _rand2plus;
extern int dword_56DB7C;
extern int dword_56DB80;
extern int dword_56DB84;
extern int dword_56DB88;
extern int dword_56DB8C;
extern int _replyPlaying;
extern int _replyWin;
extern int gDialogReplyColorG;
extern int gDialogReplyColorB;
extern int gDialogOptionColorG;
extern int gDialogReplyColorR;
extern int gDialogOptionColorB;
extern int gDialogOptionColorR;
extern int _downButton;
extern int dword_56DBB8;
extern int dword_56DBBC;
extern void* off_56DBC0;
extern void* off_56DBC4;
extern void* off_56DBC8;
extern void* off_56DBCC;
extern char* gDialogReplyTitle;
extern int _upButton;
extern int dword_56DBD8;
extern int dword_56DBDC;
extern void* off_56DBE0;
extern void* off_56DBE4;
extern void* off_56DBE8;
extern void* off_56DBEC;
STRUCT_56DAE0_FIELD_4* _getReply();
void _replyAddOption(const char* a1, const char* a2, int a3);
void _replyAddOptionProc(const char* a1, const char* a2, int a3);
void _optionFree(STRUCT_56DAE0_FIELD_4_FIELD_C* a1);
void _replyFree();
int _endDialog();
void _printLine(int win, char** strings, int strings_num, int a4, int a5, int a6, int a7, int a8, int a9);
void _printStr(int win, char* a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9);
int _abortReply(int a1);
void _endReply();
void _drawStr(int win, char* a2, int font, int width, int height, int left, int top, int a8, int a9, int a10);
int _dialogStart(Program* a1);
int _dialogRestart();
int _dialogGotoReply(const char* a1);
int dialogSetReplyTitle(const char* a1);
int _dialogReply(const char* a1, const char* a2);
int _dialogOption(const char* a1, const char* a2);
int _dialogOptionProc(const char* a1, const char* a2);
int _dialogGetExitPoint();
int _dialogQuit();
int dialogSetOptionWindow(int a1, int a2, int a3, int a4, int a5);
int dialogSetReplyWindow(int a1, int a2, int a3, int a4, int a5);
int dialogSetBorder(int a1, int a2);
int _dialogSetScrollUp(int a1, int a2, void* a3, void* a4, void* a5, void* a6, int a7);
int _dialogSetScrollDown(int a1, int a2, void* a3, void* a4, void* a5, void* a6, int a7);
int dialogSetOptionSpacing(int value);
int dialogSetOptionColor(float a1, float a2, float a3);
int dialogSetReplyColor(float a1, float a2, float a3);
int _dialogSetOptionFlags(int flags);
void _dialogClose();
int _dialogGetDialogDepth();
void _dialogRegisterWinDrawCallbacks(DialogFunc1* a1, DialogFunc2* a2);
int _dialogToggleMediaFlag(int a1);
int _dialogGetMediaFlag();
#endif /* DIALOG_H */

306
src/dictionary.c Normal file
View File

@ -0,0 +1,306 @@
#include "dictionary.h"
#include <assert.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
// 0x51E408
MallocProc* gDictionaryMallocProc = dictionaryMallocDefaultImpl;
// 0x51E40C
ReallocProc* gDictionaryReallocProc = dictionaryReallocDefaultImpl;
// 0x51E410
FreeProc* gDictionaryFreeProc = dictionaryFreeDefaultImpl;
// 0x4D9B90
void* dictionaryMallocDefaultImpl(size_t size)
{
return malloc(size);
}
// 0x4D9B98
void* dictionaryReallocDefaultImpl(void* ptr, size_t newSize)
{
return realloc(ptr, newSize);
}
// 0x4D9BA0
void dictionaryFreeDefaultImpl(void* ptr)
{
free(ptr);
}
// 0x4D9BA8
int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize, void* a4)
{
dictionary->entriesCapacity = initialCapacity;
dictionary->valueSize = valueSize;
dictionary->entriesLength = 0;
if (a4 != NULL) {
// NOTE: There is some structure pointed by [a4] with 5 fields. They are
// either memcopied or assigned one by one into field_10 - field_20
// respectively. This parameter is always NULL, so I doubt it's possible
// to understand it's meaning. There are some hints in the unused
// functions though.
assert(false && "Not implemented");
} else {
dictionary->field_10 = 0;
dictionary->field_14 = 0;
dictionary->field_18 = 0;
dictionary->field_1C = 0;
}
int rc = 0;
if (initialCapacity != 0) {
dictionary->entries = gDictionaryMallocProc(sizeof(*dictionary->entries) * initialCapacity);
if (dictionary->entries == NULL) {
rc = -1;
}
} else {
dictionary->entries = NULL;
}
if (rc != -1) {
dictionary->marker = DICTIONARY_MARKER;
}
return rc;
}
// 0x4D9C0C
int dictionarySetCapacity(Dictionary* dictionary, int newCapacity)
{
if (dictionary->marker != DICTIONARY_MARKER) {
return -1;
}
if (newCapacity < dictionary->entriesLength) {
return -1;
}
DictionaryEntry* entries = gDictionaryReallocProc(dictionary->entries, sizeof(*dictionary->entries) * newCapacity);
if (entries == NULL) {
return -1;
}
dictionary->entriesCapacity = newCapacity;
dictionary->entries = entries;
return 0;
}
// 0x4D9C48
int dictionaryFree(Dictionary* dictionary)
{
if (dictionary->marker != DICTIONARY_MARKER) {
return -1;
}
for (int index = 0; index < dictionary->entriesLength; index++) {
DictionaryEntry* entry = &(dictionary->entries[index]);
if (entry->key != NULL) {
gDictionaryFreeProc(entry->key);
}
if (entry->value != NULL) {
gDictionaryFreeProc(entry->value);
}
}
if (dictionary->entries != NULL) {
gDictionaryFreeProc(dictionary->entries);
}
memset(dictionary, 0, sizeof(*dictionary));
return 0;
}
// Finds index for the given key.
//
// Returns 0 if key is found. Otherwise returns -1, in this case [indexPtr]
// specifies an insertion point for given key.
//
// 0x4D9CC4
int dictionaryFindIndexForKey(Dictionary* dictionary, const char* key, int* indexPtr)
{
if (dictionary->marker != DICTIONARY_MARKER) {
return -1;
}
if (dictionary->entriesLength == 0) {
*indexPtr = 0;
return -1;
}
int r = dictionary->entriesLength - 1;
int l = 0;
int mid = 0;
int cmp = 0;
while (r >= l) {
mid = (l + r) / 2;
cmp = stricmp(key, dictionary->entries[mid].key);
if (cmp == 0) {
break;
}
if (cmp > 0) {
l = l + 1;
} else {
r = r - 1;
}
}
if (cmp == 0) {
*indexPtr = mid;
return 0;
}
if (cmp < 0) {
*indexPtr = mid;
} else {
*indexPtr = mid + 1;
}
return -1;
}
// Returns the index of the entry for the specified key, or -1 if it's not
// present in the dictionary.
//
// 0x4D9D5C
int dictionaryGetIndexByKey(Dictionary* dictionary, const char* key)
{
if (dictionary->marker != DICTIONARY_MARKER) {
return -1;
}
int index;
if (dictionaryFindIndexForKey(dictionary, key, &index) != 0) {
return -1;
}
return index;
}
// Adds key-value pair to the dictionary if the specified key is not already
// present.
//
// Returns 0 on success, or -1 on any error (including key already exists
// error).
//
// 0x4D9D88
int dictionaryAddValue(Dictionary* dictionary, const char* key, const void* value)
{
if (dictionary->marker != DICTIONARY_MARKER) {
return -1;
}
int newElementIndex;
if (dictionaryFindIndexForKey(dictionary, key, &newElementIndex) == 0) {
// Element for this key is already exists.
return -1;
}
if (dictionary->entriesLength == dictionary->entriesCapacity) {
// Dictionary reached it's capacity and needs to be enlarged.
if (dictionarySetCapacity(dictionary, 2 * (dictionary->entriesCapacity + 1)) == -1) {
return -1;
}
}
// Make a copy of the key.
char* keyCopy = gDictionaryMallocProc(strlen(key) + 1);
if (keyCopy == NULL) {
return -1;
}
strcpy(keyCopy, key);
// Make a copy of the value.
void* valueCopy = NULL;
if (value != NULL && dictionary->valueSize != 0) {
valueCopy = gDictionaryMallocProc(dictionary->valueSize);
if (valueCopy == NULL) {
gDictionaryFreeProc(keyCopy);
return -1;
}
}
if (valueCopy != NULL && dictionary->valueSize != 0) {
memcpy(valueCopy, value, dictionary->valueSize);
}
// Starting at the end of entries array loop backwards and move entries down
// one by one until we reach insertion point.
for (int index = dictionary->entriesLength; index > newElementIndex; index--) {
DictionaryEntry* src = &(dictionary->entries[index - 1]);
DictionaryEntry* dest = &(dictionary->entries[index]);
memcpy(dest, src, sizeof(*dictionary->entries));
}
DictionaryEntry* entry = &(dictionary->entries[newElementIndex]);
entry->key = keyCopy;
entry->value = valueCopy;
dictionary->entriesLength++;
return 0;
}
// Removes key-value pair from the dictionary if specified key is present in
// the dictionary.
//
// Returns 0 on success, -1 on any error (including key not present error).
//
// 0x4D9EE8
int dictionaryRemoveValue(Dictionary* dictionary, const char* key)
{
if (dictionary->marker != DICTIONARY_MARKER) {
return -1;
}
int indexToRemove;
if (dictionaryFindIndexForKey(dictionary, key, &indexToRemove) == -1) {
return -1;
}
DictionaryEntry* entry = &(dictionary->entries[indexToRemove]);
// Free key and value (which are copies).
gDictionaryFreeProc(entry->key);
if (entry->value != NULL) {
gDictionaryFreeProc(entry->value);
}
dictionary->entriesLength--;
// Starting from the index of the entry we've just removed, loop thru the
// remaining of the array and move entries up one by one.
for (int index = indexToRemove; index < dictionary->entriesLength; index++) {
DictionaryEntry* src = &(dictionary->entries[index + 1]);
DictionaryEntry* dest = &(dictionary->entries[index]);
memcpy(dest, src, sizeof(*dictionary->entries));
}
return 0;
}
// 0x4DA498
void dictionarySetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc)
{
if (mallocProc != NULL && reallocProc != NULL && freeProc != NULL) {
gDictionaryMallocProc = mallocProc;
gDictionaryReallocProc = reallocProc;
gDictionaryFreeProc = freeProc;
} else {
gDictionaryMallocProc = dictionaryMallocDefaultImpl;
gDictionaryReallocProc = dictionaryReallocDefaultImpl;
gDictionaryFreeProc = dictionaryFreeDefaultImpl;
}
}

61
src/dictionary.h Normal file
View File

@ -0,0 +1,61 @@
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include "memory_defs.h"
// NOTE: I guess this marker is used as a type discriminator for implementing
// nested dictionaries. That's why every dictionary-related function starts
// with a check for this value.
#define DICTIONARY_MARKER 0xFEBAFEBA
// A tuple containing individual key-value pair of a dictionary.
typedef struct DictionaryEntry {
char* key;
void* value;
} DictionaryEntry;
// A collection of key/value pairs.
//
// The keys in dictionary are always strings. Internally dictionary entries
// are kept sorted by the key. Both keys and values are copied when new entry
// is added to dictionary. For this reason the size of the value's type is
// provided during dictionary initialization.
typedef struct Dictionary {
int marker;
// The number of key/value pairs in the dictionary.
int entriesLength;
// The capacity of key/value pairs in [entries] array.
int entriesCapacity;
// The size of the dictionary values in bytes.
size_t valueSize;
int field_10;
int field_14;
int field_18;
int field_1C;
int field_20;
// The array of key-value pairs.
DictionaryEntry* entries;
} Dictionary;
extern MallocProc* gDictionaryMallocProc;
extern ReallocProc* gDictionaryReallocProc;
extern FreeProc* gDictionaryFreeProc;
void* dictionaryMallocDefaultImpl(size_t size);
void* dictionaryReallocDefaultImpl(void* ptr, size_t newSize);
void dictionaryFreeDefaultImpl(void* ptr);
int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize, void* a4);
int dictionarySetCapacity(Dictionary* dictionary, int newCapacity);
int dictionaryFree(Dictionary* dictionary);
int dictionaryFindIndexForKey(Dictionary* dictionary, const char* key, int* index);
int dictionaryGetIndexByKey(Dictionary* dictionary, const char* key);
int dictionaryAddValue(Dictionary* dictionary, const char* key, const void* value);
int dictionaryRemoveValue(Dictionary* dictionary, const char* key);
void dictionarySetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc);
#endif /* DICTIONARY_H */

600
src/dinput.c Normal file
View File

@ -0,0 +1,600 @@
#include <initguid.h>
#include "dinput.h"
// NOTE: There is no such define in DirectX SDK. I've taken it from Wine at
// https://github.com/wine-mirror/wine/blob/master/dlls/dinput/data_formats.c.
#define DIDFT_OPTIONAL 0x80000000
// NOTE: This define is different in the newer DirectX. Check DirectX SDK 3.0
// at https://github.com/masonmc/dxsdk3/blob/master/sdk/inc/dinput.h.
#undef DIDFT_ANYINSTANCE
#define DIDFT_ANYINSTANCE 0x0000FF00
// 0x4FCE90
static const DIOBJECTDATAFORMAT dfDIMouse[] = {
{ &GUID_XAxis, DIMOFS_X, DIDFT_ANYINSTANCE | DIDFT_AXIS, 0 },
{ &GUID_YAxis, DIMOFS_Y, DIDFT_ANYINSTANCE | DIDFT_AXIS, 0 },
{ &GUID_ZAxis, DIMOFS_Z, DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, 0 },
{ NULL, DIMOFS_BUTTON0, DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
{ NULL, DIMOFS_BUTTON1, DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
{ NULL, DIMOFS_BUTTON2, DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
{ NULL, DIMOFS_BUTTON3, DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
};
// 0x4FCF00
const DIDATAFORMAT c_dfDIMouse = {
sizeof(DIDATAFORMAT),
sizeof(DIOBJECTDATAFORMAT),
DIDF_RELAXIS,
sizeof(DIMOUSESTATE),
sizeof(dfDIMouse) / sizeof(dfDIMouse[0]),
(LPDIOBJECTDATAFORMAT)dfDIMouse
};
// 0x4FCF20
static const DIOBJECTDATAFORMAT dfDIKeyboard[] = {
{ &GUID_Key, 0, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(0), 0 },
{ &GUID_Key, 1, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(1), 0 },
{ &GUID_Key, 2, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(2), 0 },
{ &GUID_Key, 3, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(3), 0 },
{ &GUID_Key, 4, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(4), 0 },
{ &GUID_Key, 5, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(5), 0 },
{ &GUID_Key, 6, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(6), 0 },
{ &GUID_Key, 7, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(7), 0 },
{ &GUID_Key, 8, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(8), 0 },
{ &GUID_Key, 9, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(9), 0 },
{ &GUID_Key, 10, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(10), 0 },
{ &GUID_Key, 11, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(11), 0 },
{ &GUID_Key, 12, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(12), 0 },
{ &GUID_Key, 13, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(13), 0 },
{ &GUID_Key, 14, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(14), 0 },
{ &GUID_Key, 15, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(15), 0 },
{ &GUID_Key, 16, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(16), 0 },
{ &GUID_Key, 17, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(17), 0 },
{ &GUID_Key, 18, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(18), 0 },
{ &GUID_Key, 19, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(19), 0 },
{ &GUID_Key, 20, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(20), 0 },
{ &GUID_Key, 21, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(21), 0 },
{ &GUID_Key, 22, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(22), 0 },
{ &GUID_Key, 23, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(23), 0 },
{ &GUID_Key, 24, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(24), 0 },
{ &GUID_Key, 25, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(25), 0 },
{ &GUID_Key, 26, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(26), 0 },
{ &GUID_Key, 27, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(27), 0 },
{ &GUID_Key, 28, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(28), 0 },
{ &GUID_Key, 29, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(29), 0 },
{ &GUID_Key, 30, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(30), 0 },
{ &GUID_Key, 31, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(31), 0 },
{ &GUID_Key, 32, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(32), 0 },
{ &GUID_Key, 33, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(33), 0 },
{ &GUID_Key, 34, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(34), 0 },
{ &GUID_Key, 35, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(35), 0 },
{ &GUID_Key, 36, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(36), 0 },
{ &GUID_Key, 37, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(37), 0 },
{ &GUID_Key, 38, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(38), 0 },
{ &GUID_Key, 39, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(39), 0 },
{ &GUID_Key, 40, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(40), 0 },
{ &GUID_Key, 41, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(41), 0 },
{ &GUID_Key, 42, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(42), 0 },
{ &GUID_Key, 43, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(43), 0 },
{ &GUID_Key, 44, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(44), 0 },
{ &GUID_Key, 45, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(45), 0 },
{ &GUID_Key, 46, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(46), 0 },
{ &GUID_Key, 47, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(47), 0 },
{ &GUID_Key, 48, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(48), 0 },
{ &GUID_Key, 49, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(49), 0 },
{ &GUID_Key, 50, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(50), 0 },
{ &GUID_Key, 51, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(51), 0 },
{ &GUID_Key, 52, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(52), 0 },
{ &GUID_Key, 53, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(53), 0 },
{ &GUID_Key, 54, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(54), 0 },
{ &GUID_Key, 55, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(55), 0 },
{ &GUID_Key, 56, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(56), 0 },
{ &GUID_Key, 57, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(57), 0 },
{ &GUID_Key, 58, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(58), 0 },
{ &GUID_Key, 59, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(59), 0 },
{ &GUID_Key, 60, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(60), 0 },
{ &GUID_Key, 61, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(61), 0 },
{ &GUID_Key, 62, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(62), 0 },
{ &GUID_Key, 63, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(63), 0 },
{ &GUID_Key, 64, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(64), 0 },
{ &GUID_Key, 65, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(65), 0 },
{ &GUID_Key, 66, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(66), 0 },
{ &GUID_Key, 67, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(67), 0 },
{ &GUID_Key, 68, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(68), 0 },
{ &GUID_Key, 69, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(69), 0 },
{ &GUID_Key, 70, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(70), 0 },
{ &GUID_Key, 71, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(71), 0 },
{ &GUID_Key, 72, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(72), 0 },
{ &GUID_Key, 73, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(73), 0 },
{ &GUID_Key, 74, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(74), 0 },
{ &GUID_Key, 75, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(75), 0 },
{ &GUID_Key, 76, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(76), 0 },
{ &GUID_Key, 77, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(77), 0 },
{ &GUID_Key, 78, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(78), 0 },
{ &GUID_Key, 79, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(79), 0 },
{ &GUID_Key, 80, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(80), 0 },
{ &GUID_Key, 81, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(81), 0 },
{ &GUID_Key, 82, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(82), 0 },
{ &GUID_Key, 83, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(83), 0 },
{ &GUID_Key, 84, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(84), 0 },
{ &GUID_Key, 85, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(85), 0 },
{ &GUID_Key, 86, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(86), 0 },
{ &GUID_Key, 87, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(87), 0 },
{ &GUID_Key, 88, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(88), 0 },
{ &GUID_Key, 89, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(89), 0 },
{ &GUID_Key, 90, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(90), 0 },
{ &GUID_Key, 91, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(91), 0 },
{ &GUID_Key, 92, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(92), 0 },
{ &GUID_Key, 93, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(93), 0 },
{ &GUID_Key, 94, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(94), 0 },
{ &GUID_Key, 95, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(95), 0 },
{ &GUID_Key, 96, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(96), 0 },
{ &GUID_Key, 97, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(97), 0 },
{ &GUID_Key, 98, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(98), 0 },
{ &GUID_Key, 99, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(99), 0 },
{ &GUID_Key, 100, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(100), 0 },
{ &GUID_Key, 101, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(101), 0 },
{ &GUID_Key, 102, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(102), 0 },
{ &GUID_Key, 103, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(103), 0 },
{ &GUID_Key, 104, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(104), 0 },
{ &GUID_Key, 105, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(105), 0 },
{ &GUID_Key, 106, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(106), 0 },
{ &GUID_Key, 107, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(107), 0 },
{ &GUID_Key, 108, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(108), 0 },
{ &GUID_Key, 109, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(109), 0 },
{ &GUID_Key, 110, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(110), 0 },
{ &GUID_Key, 111, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(111), 0 },
{ &GUID_Key, 112, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(112), 0 },
{ &GUID_Key, 113, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(113), 0 },
{ &GUID_Key, 114, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(114), 0 },
{ &GUID_Key, 115, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(115), 0 },
{ &GUID_Key, 116, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(116), 0 },
{ &GUID_Key, 117, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(117), 0 },
{ &GUID_Key, 118, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(118), 0 },
{ &GUID_Key, 119, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(119), 0 },
{ &GUID_Key, 120, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(120), 0 },
{ &GUID_Key, 121, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(121), 0 },
{ &GUID_Key, 122, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(122), 0 },
{ &GUID_Key, 123, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(123), 0 },
{ &GUID_Key, 124, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(124), 0 },
{ &GUID_Key, 125, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(125), 0 },
{ &GUID_Key, 126, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(126), 0 },
{ &GUID_Key, 127, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(127), 0 },
{ &GUID_Key, 128, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(128), 0 },
{ &GUID_Key, 129, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(129), 0 },
{ &GUID_Key, 130, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(130), 0 },
{ &GUID_Key, 131, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(131), 0 },
{ &GUID_Key, 132, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(132), 0 },
{ &GUID_Key, 133, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(133), 0 },
{ &GUID_Key, 134, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(134), 0 },
{ &GUID_Key, 135, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(135), 0 },
{ &GUID_Key, 136, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(136), 0 },
{ &GUID_Key, 137, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(137), 0 },
{ &GUID_Key, 138, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(138), 0 },
{ &GUID_Key, 139, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(139), 0 },
{ &GUID_Key, 140, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(140), 0 },
{ &GUID_Key, 141, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(141), 0 },
{ &GUID_Key, 142, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(142), 0 },
{ &GUID_Key, 143, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(143), 0 },
{ &GUID_Key, 144, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(144), 0 },
{ &GUID_Key, 145, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(145), 0 },
{ &GUID_Key, 146, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(146), 0 },
{ &GUID_Key, 147, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(147), 0 },
{ &GUID_Key, 148, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(148), 0 },
{ &GUID_Key, 149, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(149), 0 },
{ &GUID_Key, 150, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(150), 0 },
{ &GUID_Key, 151, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(151), 0 },
{ &GUID_Key, 152, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(152), 0 },
{ &GUID_Key, 153, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(153), 0 },
{ &GUID_Key, 154, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(154), 0 },
{ &GUID_Key, 155, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(155), 0 },
{ &GUID_Key, 156, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(156), 0 },
{ &GUID_Key, 157, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(157), 0 },
{ &GUID_Key, 158, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(158), 0 },
{ &GUID_Key, 159, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(159), 0 },
{ &GUID_Key, 160, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(160), 0 },
{ &GUID_Key, 161, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(161), 0 },
{ &GUID_Key, 162, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(162), 0 },
{ &GUID_Key, 163, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(163), 0 },
{ &GUID_Key, 164, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(164), 0 },
{ &GUID_Key, 165, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(165), 0 },
{ &GUID_Key, 166, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(166), 0 },
{ &GUID_Key, 167, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(167), 0 },
{ &GUID_Key, 168, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(168), 0 },
{ &GUID_Key, 169, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(169), 0 },
{ &GUID_Key, 170, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(170), 0 },
{ &GUID_Key, 171, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(171), 0 },
{ &GUID_Key, 172, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(172), 0 },
{ &GUID_Key, 173, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(173), 0 },
{ &GUID_Key, 174, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(174), 0 },
{ &GUID_Key, 175, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(175), 0 },
{ &GUID_Key, 176, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(176), 0 },
{ &GUID_Key, 177, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(177), 0 },
{ &GUID_Key, 178, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(178), 0 },
{ &GUID_Key, 179, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(179), 0 },
{ &GUID_Key, 180, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(180), 0 },
{ &GUID_Key, 181, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(181), 0 },
{ &GUID_Key, 182, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(182), 0 },
{ &GUID_Key, 183, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(183), 0 },
{ &GUID_Key, 184, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(184), 0 },
{ &GUID_Key, 185, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(185), 0 },
{ &GUID_Key, 186, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(186), 0 },
{ &GUID_Key, 187, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(187), 0 },
{ &GUID_Key, 188, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(188), 0 },
{ &GUID_Key, 189, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(189), 0 },
{ &GUID_Key, 190, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(190), 0 },
{ &GUID_Key, 191, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(191), 0 },
{ &GUID_Key, 192, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(192), 0 },
{ &GUID_Key, 193, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(193), 0 },
{ &GUID_Key, 194, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(194), 0 },
{ &GUID_Key, 195, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(195), 0 },
{ &GUID_Key, 196, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(196), 0 },
{ &GUID_Key, 197, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(197), 0 },
{ &GUID_Key, 198, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(198), 0 },
{ &GUID_Key, 199, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(199), 0 },
{ &GUID_Key, 200, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(200), 0 },
{ &GUID_Key, 201, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(201), 0 },
{ &GUID_Key, 202, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(202), 0 },
{ &GUID_Key, 203, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(203), 0 },
{ &GUID_Key, 204, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(204), 0 },
{ &GUID_Key, 205, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(205), 0 },
{ &GUID_Key, 206, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(206), 0 },
{ &GUID_Key, 207, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(207), 0 },
{ &GUID_Key, 208, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(208), 0 },
{ &GUID_Key, 209, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(209), 0 },
{ &GUID_Key, 210, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(210), 0 },
{ &GUID_Key, 211, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(211), 0 },
{ &GUID_Key, 212, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(212), 0 },
{ &GUID_Key, 213, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(213), 0 },
{ &GUID_Key, 214, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(214), 0 },
{ &GUID_Key, 215, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(215), 0 },
{ &GUID_Key, 216, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(216), 0 },
{ &GUID_Key, 217, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(217), 0 },
{ &GUID_Key, 218, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(218), 0 },
{ &GUID_Key, 219, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(219), 0 },
{ &GUID_Key, 220, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(220), 0 },
{ &GUID_Key, 221, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(221), 0 },
{ &GUID_Key, 222, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(222), 0 },
{ &GUID_Key, 223, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(223), 0 },
{ &GUID_Key, 224, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(224), 0 },
{ &GUID_Key, 225, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(225), 0 },
{ &GUID_Key, 226, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(226), 0 },
{ &GUID_Key, 227, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(227), 0 },
{ &GUID_Key, 228, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(228), 0 },
{ &GUID_Key, 229, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(229), 0 },
{ &GUID_Key, 230, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(230), 0 },
{ &GUID_Key, 231, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(231), 0 },
{ &GUID_Key, 232, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(232), 0 },
{ &GUID_Key, 233, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(233), 0 },
{ &GUID_Key, 234, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(234), 0 },
{ &GUID_Key, 235, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(235), 0 },
{ &GUID_Key, 236, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(236), 0 },
{ &GUID_Key, 237, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(237), 0 },
{ &GUID_Key, 238, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(238), 0 },
{ &GUID_Key, 239, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(239), 0 },
{ &GUID_Key, 240, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(240), 0 },
{ &GUID_Key, 241, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(241), 0 },
{ &GUID_Key, 242, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(242), 0 },
{ &GUID_Key, 243, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(243), 0 },
{ &GUID_Key, 244, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(244), 0 },
{ &GUID_Key, 245, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(245), 0 },
{ &GUID_Key, 246, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(246), 0 },
{ &GUID_Key, 247, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(247), 0 },
{ &GUID_Key, 248, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(248), 0 },
{ &GUID_Key, 249, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(249), 0 },
{ &GUID_Key, 250, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(250), 0 },
{ &GUID_Key, 251, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(251), 0 },
{ &GUID_Key, 252, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(252), 0 },
{ &GUID_Key, 253, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(253), 0 },
{ &GUID_Key, 254, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(254), 0 },
{ &GUID_Key, 255, DIDFT_OPTIONAL | DIDFT_BUTTON | DIDFT_MAKEINSTANCE(255), 0 },
};
// 0x4FDF20
static const DIDATAFORMAT c_dfDIKeyboard = {
sizeof(DIDATAFORMAT),
sizeof(DIOBJECTDATAFORMAT),
DIDF_RELAXIS,
256,
sizeof(dfDIKeyboard) / sizeof(dfDIKeyboard[0]),
(LPDIOBJECTDATAFORMAT)dfDIKeyboard
};
// 0x51E458
LPDIRECTINPUTA gDirectInput = NULL;
// 0x51E45C
LPDIRECTINPUTDEVICEA gMouseDevice = NULL;
// 0x51E460
LPDIRECTINPUTDEVICEA gKeyboardDevice = NULL;
// 0x51E464
int gKeyboardDeviceDataIndex = 0;
// 0x51E468
int gKeyboardDeviceDataLength = 0;
// 0x6B2560
DIDEVICEOBJECTDATA gKeyboardDeviceData[KEYBOARD_DEVICE_DATA_CAPACITY];
// 0x4E0400
bool directInputInit()
{
if (gDirectInput != NULL) {
return false;
}
HRESULT hr = gDirectInputCreateAProc(gInstance, DIRECTINPUT_VERSION, &gDirectInput, NULL);
if (hr != DI_OK) {
goto err;
}
if (!mouseDeviceInit()) {
goto err;
}
if (!keyboardDeviceInit()) {
goto err;
}
return true;
err:
keyboardDeviceFree();
mouseDeviceFree();
if (gDirectInput != NULL) {
IDirectInput_Release(gDirectInput);
gDirectInput = NULL;
}
return false;
}
// 0x4E0478
void directInputFree()
{
// NOTE: Uninline.
keyboardDeviceFree();
// NOTE: Uninline.
mouseDeviceFree();
if (gDirectInput != NULL) {
IDirectInput_Release(gDirectInput);
gDirectInput = NULL;
}
}
// 0x4E04E8
bool mouseDeviceAcquire()
{
if (gMouseDevice == NULL) {
return false;
}
HRESULT hr = IDirectInputDevice_Acquire(gMouseDevice);
if (hr != DI_OK && hr != S_FALSE) {
return false;
}
return true;
}
// 0x4E0514
bool mouseDeviceUnacquire()
{
if (gMouseDevice == NULL) {
return false;
}
HRESULT hr = IDirectInputDevice_Unacquire(gMouseDevice);
if (hr != DI_OK) {
return false;
}
return true;
}
// 0x4E053C
bool mouseDeviceGetData(MouseData* mouseState)
{
if (gMouseDevice == NULL) {
return false;
}
if (!mouseDeviceAcquire()) {
return false;
}
DIMOUSESTATE dims;
HRESULT hr = IDirectInputDevice_GetDeviceState(gMouseDevice, sizeof(dims), &dims);
if (hr != DI_OK) {
return false;
}
mouseState->x = dims.lX;
mouseState->y = dims.lY;
mouseState->buttons[0] = (dims.rgbButtons[0] & 0x80) != 0;
mouseState->buttons[1] = (dims.rgbButtons[1] & 0x80) != 0;
return true;
}
// 0x4E05A8
bool keyboardDeviceAcquire()
{
if (gKeyboardDevice == NULL) {
return false;
}
HRESULT hr = IDirectInputDevice_Acquire(gKeyboardDevice);
if (hr != DI_OK && hr != S_FALSE) {
return false;
}
return true;
}
// 0x4E05D4
bool keyboardDeviceUnacquire()
{
if (gKeyboardDevice == NULL) {
return false;
}
HRESULT hr = IDirectInputDevice_Unacquire(gKeyboardDevice);
if (hr != DI_OK) {
return false;
}
return true;
}
// 0x4E05FC
bool keyboardDeviceReset()
{
if (gKeyboardDevice == NULL) {
return false;
}
if (!keyboardDeviceAcquire()) {
return false;
}
DWORD items = -1;
HRESULT hr = IDirectInputDevice_GetDeviceData(gKeyboardDevice, sizeof(DIDEVICEOBJECTDATA), NULL, &items, 0);
if (hr != DI_OK && hr != DI_BUFFEROVERFLOW) {
return false;
}
return true;
}
// 0x4E0650
bool keyboardDeviceGetData(KeyboardData* keyboardData)
{
if (gKeyboardDevice == NULL) {
return false;
}
if (!keyboardDeviceAcquire()) {
return false;
}
if (gKeyboardDeviceDataIndex >= gKeyboardDeviceDataLength) {
DWORD items = KEYBOARD_DEVICE_DATA_CAPACITY;
HRESULT hr = IDirectInputDevice_GetDeviceData(gKeyboardDevice, sizeof(DIDEVICEOBJECTDATA), gKeyboardDeviceData, &items, 0);
if (hr == DI_OK || hr == DI_BUFFEROVERFLOW) {
gKeyboardDeviceDataLength = items;
gKeyboardDeviceDataIndex = 0;
}
}
if (gKeyboardDeviceDataIndex < gKeyboardDeviceDataLength) {
DIDEVICEOBJECTDATA* entry = &(gKeyboardDeviceData[gKeyboardDeviceDataIndex]);
keyboardData->key = entry->dwOfs & 0xFF;
keyboardData->down = (entry->dwData & 0x80) != 0;
gKeyboardDeviceDataIndex++;
return true;
}
return false;
}
// 0x4E070C
bool mouseDeviceInit()
{
HRESULT hr;
hr = IDirectInput_CreateDevice(gDirectInput, &GUID_SysMouse, &gMouseDevice, NULL);
if (hr != DI_OK) {
goto err;
}
hr = IDirectInputDevice_SetCooperativeLevel(gMouseDevice, gProgramWindow, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
if (hr != DI_OK) {
goto err;
}
hr = IDirectInputDevice_SetDataFormat(gMouseDevice, &c_dfDIMouse);
if (hr != DI_OK) {
goto err;
}
return true;
err:
// NOTE: Uninline.
mouseDeviceFree();
return false;
}
// 0x4E078C
void mouseDeviceFree()
{
if (gMouseDevice != NULL) {
IDirectInputDevice_Unacquire(gMouseDevice);
IDirectInputDevice_Release(gMouseDevice);
gMouseDevice = NULL;
}
}
// 0x4E07B8
bool keyboardDeviceInit()
{
HRESULT hr;
hr = IDirectInput_CreateDevice(gDirectInput, &GUID_SysKeyboard, &gKeyboardDevice, NULL);
if (hr != DI_OK) {
goto err;
}
hr = IDirectInputDevice_SetCooperativeLevel(gKeyboardDevice, gProgramWindow, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND);
if (hr != DI_OK) {
goto err;
}
hr = IDirectInputDevice_SetDataFormat(gKeyboardDevice, &c_dfDIKeyboard);
if (hr != DI_OK) {
goto err;
}
DIPROPDWORD dipdw;
dipdw.diph.dwSize = sizeof(DIPROPDWORD);
dipdw.diph.dwHeaderSize = sizeof(DIPROPHEADER);
dipdw.diph.dwObj = 0;
dipdw.diph.dwHow = DIPH_DEVICE;
dipdw.dwData = KEYBOARD_DEVICE_DATA_CAPACITY;
hr = IDirectInputDevice_SetProperty(gKeyboardDevice, DIPROP_BUFFERSIZE, &(dipdw.diph));
if (hr != DI_OK) {
goto err;
}
return true;
err:
// NOTE: Uninline.
keyboardDeviceFree();
return false;
}
// 0x4E0874
void keyboardDeviceFree()
{
if (gKeyboardDevice != NULL) {
IDirectInputDevice_Unacquire(gKeyboardDevice);
IDirectInputDevice_Release(gKeyboardDevice);
gKeyboardDevice = NULL;
}
}

40
src/dinput.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef DINPUT_H
#define DINPUT_H
#include "win32.h"
#define KEYBOARD_DEVICE_DATA_CAPACITY (32)
typedef struct MouseData {
int x;
int y;
unsigned char buttons[2];
} MouseData;
typedef struct KeyboardData {
char key;
char down;
} KeyboardData;
extern LPDIRECTINPUTA gDirectInput;
extern LPDIRECTINPUTDEVICEA gMouseDevice;
extern LPDIRECTINPUTDEVICEA gKeyboardDevice;
extern int gKeyboardDeviceDataIndex;
extern int gKeyboardDeviceDataLength;
extern DIDEVICEOBJECTDATA gKeyboardDeviceData[KEYBOARD_DEVICE_DATA_CAPACITY];
bool directInputInit();
void directInputFree();
bool mouseDeviceAcquire();
bool mouseDeviceUnacquire();
bool mouseDeviceGetData(MouseData* mouseData);
bool keyboardDeviceAcquire();
bool keyboardDeviceUnacquire();
bool keyboardDeviceReset();
bool keyboardDeviceGetData(KeyboardData* keyboardData);
bool mouseDeviceInit();
void mouseDeviceFree();
bool keyboardDeviceInit();
void keyboardDeviceFree();
#endif /* DINPUT_H */

365
src/display_monitor.c Normal file
View File

@ -0,0 +1,365 @@
#include "display_monitor.h"
#include "art.h"
#include "color.h"
#include "combat.h"
#include "core.h"
#include "draw.h"
#include "game_mouse.h"
#include "game_sound.h"
#include "interface.h"
#include "memory.h"
#include "text_font.h"
#include "window_manager.h"
#include <string.h>
// 0x51850C
bool gDisplayMonitorInitialized = false;
// The rectangle that display monitor occupies in the main interface window.
//
// 0x518510
const Rect gDisplayMonitorRect = {
DISPLAY_MONITOR_X,
DISPLAY_MONITOR_Y,
DISPLAY_MONITOR_X + DISPLAY_MONITOR_WIDTH - 1,
DISPLAY_MONITOR_Y + DISPLAY_MONITOR_HEIGHT - 1,
};
// 0x518520
int gDisplayMonitorScrollDownButton = -1;
// 0x518524
int gDisplayMonitorScrollUpButton = -1;
// 0x56DBFC
char gDisplayMonitorLines[DISPLAY_MONITOR_LINES_CAPACITY][DISPLAY_MONITOR_LINE_LENGTH];
// 0x56FB3C
unsigned char* gDisplayMonitorBackgroundFrmData;
// 0x56FB40
int _max_disp;
// 0x56FB44
bool gDisplayMonitorEnabled;
// 0x56FB48
int _disp_curr;
// 0x56FB4C
int _intface_full_width;
// 0x56FB50
int gDisplayMonitorLinesCapacity;
// 0x56FB54
int _disp_start;
// 0x56FB58
unsigned int gDisplayMonitorLastBeepTimestamp;
// 0x431610
int displayMonitorInit()
{
if (!gDisplayMonitorInitialized) {
int oldFont = fontGetCurrent();
fontSetCurrent(DISPLAY_MONITOR_FONT);
gDisplayMonitorLinesCapacity = DISPLAY_MONITOR_LINES_CAPACITY;
_max_disp = DISPLAY_MONITOR_HEIGHT / fontGetLineHeight();
_disp_start = 0;
_disp_curr = 0;
fontSetCurrent(oldFont);
gDisplayMonitorBackgroundFrmData = internal_malloc(DISPLAY_MONITOR_WIDTH * DISPLAY_MONITOR_HEIGHT);
if (gDisplayMonitorBackgroundFrmData == NULL) {
return -1;
}
CacheEntry* backgroundFrmHandle;
int backgroundFid = buildFid(6, 16, 0, 0, 0);
Art* backgroundFrm = artLock(backgroundFid, &backgroundFrmHandle);
if (backgroundFrm == NULL) {
internal_free(gDisplayMonitorBackgroundFrmData);
return -1;
}
unsigned char* backgroundFrmData = artGetFrameData(backgroundFrm, 0, 0);
_intface_full_width = artGetWidth(backgroundFrm, 0, 0);
blitBufferToBuffer(backgroundFrmData + _intface_full_width * DISPLAY_MONITOR_Y + DISPLAY_MONITOR_X,
DISPLAY_MONITOR_WIDTH,
DISPLAY_MONITOR_HEIGHT,
_intface_full_width,
gDisplayMonitorBackgroundFrmData,
DISPLAY_MONITOR_WIDTH);
artUnlock(backgroundFrmHandle);
gDisplayMonitorScrollUpButton = buttonCreate(gInterfaceBarWindow,
DISPLAY_MONITOR_X,
DISPLAY_MONITOR_Y,
DISPLAY_MONITOR_WIDTH,
DISPLAY_MONITOR_HALF_HEIGHT,
-1,
-1,
-1,
-1,
NULL,
NULL,
NULL,
0);
if (gDisplayMonitorScrollUpButton != -1) {
buttonSetMouseCallbacks(gDisplayMonitorScrollUpButton,
displayMonitorScrollUpOnMouseEnter,
displayMonitorOnMouseExit,
displayMonitorScrollUpOnMouseDown,
NULL);
}
gDisplayMonitorScrollDownButton = buttonCreate(gInterfaceBarWindow,
DISPLAY_MONITOR_X,
DISPLAY_MONITOR_Y + DISPLAY_MONITOR_HALF_HEIGHT,
DISPLAY_MONITOR_WIDTH,
DISPLAY_MONITOR_HEIGHT - DISPLAY_MONITOR_HALF_HEIGHT,
-1,
-1,
-1,
-1,
NULL,
NULL,
NULL,
0);
if (gDisplayMonitorScrollDownButton != -1) {
buttonSetMouseCallbacks(gDisplayMonitorScrollDownButton,
displayMonitorScrollDownOnMouseEnter,
displayMonitorOnMouseExit,
displayMonitorScrollDownOnMouseDown,
NULL);
}
gDisplayMonitorEnabled = true;
gDisplayMonitorInitialized = true;
for (int index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
}
return 0;
}
// 0x431800
int displayMonitorReset()
{
if (gDisplayMonitorInitialized) {
for (int index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
}
return 0;
}
// 0x43184C
void displayMonitorExit()
{
if (gDisplayMonitorInitialized) {
internal_free(gDisplayMonitorBackgroundFrmData);
gDisplayMonitorInitialized = false;
}
}
// 0x43186C
void displayMonitorAddMessage(char* str)
{
if (!gDisplayMonitorInitialized) {
return;
}
int oldFont = fontGetCurrent();
fontSetCurrent(DISPLAY_MONITOR_FONT);
char knob = '\x95';
char knobString[2];
knobString[0] = knob;
knobString[1] = '\0';
int knobWidth = fontGetStringWidth(knobString);
if (!isInCombat()) {
unsigned int now = _get_bk_time();
if (getTicksBetween(now, gDisplayMonitorLastBeepTimestamp) >= DISPLAY_MONITOR_BEEP_DELAY) {
gDisplayMonitorLastBeepTimestamp = now;
soundPlayFile("monitor");
}
}
// TODO: Refactor these two loops.
char* v1 = NULL;
while (true) {
while (fontGetStringWidth(str) < DISPLAY_MONITOR_WIDTH - _max_disp - knobWidth) {
char* temp = gDisplayMonitorLines[_disp_start];
int length;
if (knob != '\0') {
*temp++ = knob;
length = DISPLAY_MONITOR_LINE_LENGTH - 2;
knob = '\0';
knobWidth = 0;
} else {
length = DISPLAY_MONITOR_LINE_LENGTH - 1;
}
strncpy(temp, str, length);
gDisplayMonitorLines[_disp_start][DISPLAY_MONITOR_LINE_LENGTH - 1] = '\0';
_disp_start = (_disp_start + 1) % gDisplayMonitorLinesCapacity;
if (v1 == NULL) {
fontSetCurrent(oldFont);
_disp_curr = _disp_start;
displayMonitorRefresh();
return;
}
str = v1 + 1;
*v1 = ' ';
v1 = NULL;
}
char* space = strrchr(str, ' ');
if (space == NULL) {
break;
}
if (v1 != NULL) {
*v1 = ' ';
}
v1 = space;
if (space != NULL) {
*space = '\0';
}
}
char* temp = gDisplayMonitorLines[_disp_start];
int length;
if (knob != '\0') {
temp++;
gDisplayMonitorLines[_disp_start][0] = knob;
length = DISPLAY_MONITOR_LINE_LENGTH - 2;
knob = '\0';
} else {
length = DISPLAY_MONITOR_LINE_LENGTH - 1;
}
strncpy(temp, str, length);
gDisplayMonitorLines[_disp_start][DISPLAY_MONITOR_LINE_LENGTH - 1] = '\0';
_disp_start = (_disp_start + 1) % gDisplayMonitorLinesCapacity;
fontSetCurrent(oldFont);
_disp_curr = _disp_start;
displayMonitorRefresh();
}
// 0x431A78
void displayMonitorRefresh()
{
if (!gDisplayMonitorInitialized) {
return;
}
unsigned char* buf = windowGetBuffer(gInterfaceBarWindow);
if (buf == NULL) {
return;
}
buf += _intface_full_width * DISPLAY_MONITOR_Y + DISPLAY_MONITOR_X;
blitBufferToBuffer(gDisplayMonitorBackgroundFrmData,
DISPLAY_MONITOR_WIDTH,
DISPLAY_MONITOR_HEIGHT,
DISPLAY_MONITOR_WIDTH,
buf,
_intface_full_width);
int oldFont = fontGetCurrent();
fontSetCurrent(DISPLAY_MONITOR_FONT);
for (int index = 0; index < _max_disp; index++) {
int stringIndex = (_disp_curr + gDisplayMonitorLinesCapacity + index - _max_disp) % gDisplayMonitorLinesCapacity;
fontDrawText(buf + index * _intface_full_width * fontGetLineHeight(), gDisplayMonitorLines[stringIndex], DISPLAY_MONITOR_WIDTH, _intface_full_width, _colorTable[992]);
// Even though the display monitor is rectangular, it's graphic is not.
// To give a feel of depth it's covered by some metal canopy and
// considered inclined outwards. This way earlier messages appear a
// little bit far from player's perspective. To implement this small
// detail the destination buffer is incremented by 1.
buf++;
}
windowRefreshRect(gInterfaceBarWindow, &gDisplayMonitorRect);
fontSetCurrent(oldFont);
}
// 0x431B70
void displayMonitorScrollUpOnMouseDown(int btn, int keyCode)
{
if ((gDisplayMonitorLinesCapacity + _disp_curr - 1) % gDisplayMonitorLinesCapacity != _disp_start) {
_disp_curr = (gDisplayMonitorLinesCapacity + _disp_curr - 1) % gDisplayMonitorLinesCapacity;
displayMonitorRefresh();
}
}
// 0x431B9C
void displayMonitorScrollDownOnMouseDown(int btn, int keyCode)
{
if (_disp_curr != _disp_start) {
_disp_curr = (_disp_curr + 1) % gDisplayMonitorLinesCapacity;
displayMonitorRefresh();
}
}
// 0x431BC8
void displayMonitorScrollUpOnMouseEnter(int btn, int keyCode)
{
gameMouseSetCursor(MOUSE_CURSOR_SMALL_ARROW_UP);
}
// 0x431BD4
void displayMonitorScrollDownOnMouseEnter(int btn, int keyCode)
{
gameMouseSetCursor(MOUSE_CURSOR_SMALL_ARROW_DOWN);
}
// 0x431BE0
void displayMonitorOnMouseExit(int btn, int keyCode)
{
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
}
// 0x431BEC
void displayMonitorDisable()
{
if (gDisplayMonitorEnabled) {
buttonDisable(gDisplayMonitorScrollDownButton);
buttonDisable(gDisplayMonitorScrollUpButton);
gDisplayMonitorEnabled = false;
}
}
// 0x431C14
void displayMonitorEnable()
{
if (!gDisplayMonitorEnabled) {
buttonEnable(gDisplayMonitorScrollDownButton);
buttonEnable(gDisplayMonitorScrollUpButton);
gDisplayMonitorEnabled = true;
}
}

54
src/display_monitor.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef DISPLAY_MONITOR_H
#define DISPLAY_MONITOR_H
#include "geometry.h"
#include <stdbool.h>
// The maximum number of lines display monitor can hold. Once this value
// is reached earlier messages are thrown away.
#define DISPLAY_MONITOR_LINES_CAPACITY (100)
// The maximum length of a string in display monitor (in characters).
#define DISPLAY_MONITOR_LINE_LENGTH (80)
#define DISPLAY_MONITOR_X (23)
#define DISPLAY_MONITOR_Y (24)
#define DISPLAY_MONITOR_WIDTH (167)
#define DISPLAY_MONITOR_HEIGHT (60)
#define DISPLAY_MONITOR_HALF_HEIGHT (DISPLAY_MONITOR_HEIGHT / 2)
#define DISPLAY_MONITOR_FONT (101)
#define DISPLAY_MONITOR_BEEP_DELAY (500U)
extern bool gDisplayMonitorInitialized;
extern const Rect gDisplayMonitorRect;
extern int gDisplayMonitorScrollDownButton;
extern int gDisplayMonitorScrollUpButton;
extern char gDisplayMonitorLines[DISPLAY_MONITOR_LINES_CAPACITY][DISPLAY_MONITOR_LINE_LENGTH];
extern unsigned char* gDisplayMonitorBackgroundFrmData;
extern int _max_disp;
extern bool gDisplayMonitorEnabled;
extern int _disp_curr;
extern int _intface_full_width;
extern int gDisplayMonitorLinesCapacity;
extern int _disp_start;
extern unsigned int gDisplayMonitorLastBeepTimestamp;
int displayMonitorInit();
int displayMonitorReset();
void displayMonitorExit();
void displayMonitorAddMessage(char* string);
void displayMonitorRefresh();
void displayMonitorScrollUpOnMouseDown(int btn, int keyCode);
void displayMonitorScrollDownOnMouseDown(int btn, int keyCode);
void displayMonitorScrollUpOnMouseEnter(int btn, int keyCode);
void displayMonitorScrollDownOnMouseEnter(int btn, int keyCode);
void displayMonitorOnMouseExit(int btn, int keyCode);
void displayMonitorDisable();
void displayMonitorEnable();
#endif /* DISPLAY_MONITOR_H */

328
src/draw.c Normal file
View File

@ -0,0 +1,328 @@
#include "draw.h"
#include "color.h"
#include "core.h"
#include "mmx.h"
#include <string.h>
// 0x4D2FC0
void bufferDrawLine(unsigned char* buf, int pitch, int x1, int y1, int x2, int y2, int color)
{
int temp;
int dx;
int dy;
unsigned char* p1;
unsigned char* p2;
unsigned char* p3;
unsigned char* p4;
if (x1 == x2) {
if (y1 > y2) {
temp = y1;
y1 = y2;
y2 = temp;
}
p1 = buf + pitch * y1 + x1;
p2 = buf + pitch * y2 + x2;
while (p1 < p2) {
*p1 = color;
*p2 = color;
p1 += pitch;
p2 -= pitch;
}
} else {
if (x1 > x2) {
temp = x1;
x1 = x2;
x2 = temp;
temp = y1;
y1 = y2;
y2 = temp;
}
p1 = buf + pitch * y1 + x1;
p2 = buf + pitch * y2 + x2;
if (y1 == y2) {
memset(p1, color, p2 - p1);
} else {
dx = x2 - x1;
int v23;
int v22;
int midX = x1 + (x2 - x1) / 2;
if (y1 <= y2) {
dy = y2 - y1;
v23 = pitch;
v22 = midX + ((y2 - y1) / 2 + y1) * pitch;
} else {
dy = y1 - y2;
v23 = -pitch;
v22 = midX + (y1 - (y1 - y2) / 2) * pitch;
}
p3 = buf + v22;
p4 = p3;
if (dx <= dy) {
int v28 = dx - (dy / 2);
int v29 = dy / 4;
while (true) {
*p1 = color;
*p2 = color;
*p3 = color;
*p4 = color;
if (v29 == 0) {
break;
}
if (v28 >= 0) {
p3++;
p2--;
p4--;
p1++;
v28 -= dy;
}
p3 += v23;
p2 -= v23;
p4 -= v23;
p1 += v23;
v28 += dx;
v29--;
}
} else {
int v26 = dy - (dx / 2);
int v27 = dx / 4;
while (true) {
*p1 = color;
*p2 = color;
*p3 = color;
*p4 = color;
if (v27 == 0) {
break;
}
if (v26 >= 0) {
p3 += v23;
p2 -= v23;
p4 -= v23;
p1 += v23;
v26 -= dx;
}
p3++;
p2--;
p4--;
p1++;
v26 += dy;
v27--;
}
}
}
}
}
// 0x4D31A4
void bufferDrawRect(unsigned char* buf, int pitch, int left, int top, int right, int bottom, int color)
{
bufferDrawLine(buf, pitch, left, top, right, top, color);
bufferDrawLine(buf, pitch, left, bottom, right, bottom, color);
bufferDrawLine(buf, pitch, left, top, left, bottom, color);
bufferDrawLine(buf, pitch, right, top, right, bottom, color);
}
// 0x4D322C
void bufferDrawRectShadowed(unsigned char* buf, int pitch, int left, int top, int right, int bottom, int ltColor, int rbColor)
{
bufferDrawLine(buf, pitch, left, top, right, top, ltColor);
bufferDrawLine(buf, pitch, left, bottom, right, bottom, rbColor);
bufferDrawLine(buf, pitch, left, top, left, bottom, ltColor);
bufferDrawLine(buf, pitch, right, top, right, bottom, rbColor);
}
// 0x4D33F0
void blitBufferToBufferStretch(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* dest, int destWidth, int destHeight, int destPitch)
{
int heightRatio = (destHeight << 16) / srcHeight;
int widthRatio = (destWidth << 16) / srcWidth;
int v1 = 0;
int v2 = heightRatio;
for (int srcY = 0; srcY < srcHeight; srcY += 1) {
int v3 = widthRatio;
int v4 = (heightRatio * srcY) >> 16;
int v5 = v2 >> 16;
int v6 = 0;
unsigned char* c = src + v1;
for (int srcX = 0; srcX < srcWidth; srcX += 1) {
int v7 = v3 >> 16;
int v8 = v6 >> 16;
unsigned char* v9 = dest + destPitch * v4 + v8;
for (int destY = v4; destY < v5; destY += 1) {
for (int destX = v8; destX < v7; destX += 1) {
*v9++ = *c;
}
v9 += destPitch;
}
v3 += widthRatio;
c++;
v6 += widthRatio;
}
v1 += srcPitch;
v2 += heightRatio;
}
}
// 0x4D3560
void blitBufferToBufferStretchTrans(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* dest, int destWidth, int destHeight, int destPitch)
{
int heightRatio = (destHeight << 16) / srcHeight;
int widthRatio = (destWidth << 16) / srcWidth;
int v1 = 0;
int v2 = heightRatio;
for (int srcY = 0; srcY < srcHeight; srcY += 1) {
int v3 = widthRatio;
int v4 = (heightRatio * srcY) >> 16;
int v5 = v2 >> 16;
int v6 = 0;
unsigned char* c = src + v1;
for (int srcX = 0; srcX < srcWidth; srcX += 1) {
int v7 = v3 >> 16;
int v8 = v6 >> 16;
if (*c != 0) {
unsigned char* v9 = dest + destPitch * v4 + v8;
for (int destY = v4; destY < v5; destY += 1) {
for (int destX = v8; destX < v7; destX += 1) {
*v9++ = *c;
}
v9 += destPitch;
}
}
v3 += widthRatio;
c++;
v6 += widthRatio;
}
v1 += srcPitch;
v2 += heightRatio;
}
}
// 0x4D36D4
void blitBufferToBuffer(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch)
{
mmxBlit(dest, destPitch, src, srcPitch, width, height);
}
// 0x4D3704
void blitBufferToBufferTrans(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch)
{
mmxBlitTrans(dest, destPitch, src, srcPitch, width, height);
}
// 0x4D387C
void bufferFill(unsigned char* buf, int width, int height, int pitch, int a5)
{
int y;
for (y = 0; y < height; y++) {
memset(buf, a5, width);
buf += pitch;
}
}
// 0x4D38E0
void _buf_texture(unsigned char* buf, int width, int height, int pitch, void* a5, int a6, int a7)
{
// TODO: Incomplete.
}
// 0x4D3A48
void _lighten_buf(unsigned char* buf, int width, int height, int pitch)
{
int skip = pitch - width;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
unsigned char p = *buf;
*buf++ = _intensityColorTable[(p << 8) + 147];
}
buf += skip;
}
}
// Swaps two colors in the buffer.
//
// 0x4D3A8C
void _swap_color_buf(unsigned char* buf, int width, int height, int pitch, int color1, int color2)
{
int step = pitch - width;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int v1 = *buf & 0xFF;
if (v1 == color1) {
*buf = color2 & 0xFF;
} else if (v1 == color2) {
*buf = color1 & 0xFF;
}
buf++;
}
buf += step;
}
}
// 0x4D3AE0
void bufferOutline(unsigned char* buf, int width, int height, int pitch, int color)
{
unsigned char* ptr = buf + pitch;
bool cycle;
for (int y = 0; y < height - 2; y++) {
cycle = true;
for (int x = 0; x < width; x++) {
if (*ptr != 0 && cycle) {
*(ptr - 1) = color & 0xFF;
cycle = false;
} else if (*ptr == 0 && !cycle) {
*ptr = color & 0xFF;
cycle = true;
}
ptr++;
}
ptr += pitch - width;
}
for (int x = 0; x < width; x++) {
ptr = buf + x;
cycle = true;
for (int y = 0; y < height; y++) {
if (*ptr != 0 && cycle) {
// TODO: Check in debugger, might be a bug.
*(ptr - pitch) = color & 0xFF;
cycle = false;
} else if (*ptr == 0 && !cycle) {
*ptr = color & 0xFF;
cycle = true;
}
ptr += pitch;
}
}
}

17
src/draw.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef DRAW_H
#define DRAW_H
void bufferDrawLine(unsigned char* buf, int pitch, int left, int top, int right, int bottom, int color);
void bufferDrawRect(unsigned char* buf, int a2, int a3, int a4, int a5, int a6, int a7);
void bufferDrawRectShadowed(unsigned char* buf, int a2, int a3, int a4, int a5, int a6, int a7, int a8);
void blitBufferToBufferStretch(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* dest, int destWidth, int destHeight, int destPitch);
void blitBufferToBufferStretchTrans(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* dest, int destWidth, int destHeight, int destPitch);
void blitBufferToBuffer(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch);
void blitBufferToBufferTrans(unsigned char* src, int width, int height, int srcPitch, unsigned char* dest, int destPitch);
void bufferFill(unsigned char* buf, int width, int height, int pitch, int a5);
void _buf_texture(unsigned char* buf, int width, int height, int pitch, void* a5, int a6, int a7);
void _lighten_buf(unsigned char* buf, int width, int height, int pitch);
void _swap_color_buf(unsigned char* buf, int width, int height, int pitch, int color1, int color2);
void bufferOutline(unsigned char* buf, int width, int height, int pitch, int a5);
#endif /* DRAW_H */

View File

@ -0,0 +1,42 @@
#include "electronic_registration.h"
#include "game_config.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// 0x440DD0
void runElectronicRegistration()
{
int timesRun = 0;
configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_TIMES_RUN_KEY, &timesRun);
if (timesRun > 0 && timesRun < 5) {
char path[MAX_PATH];
if (GetModuleFileNameA(NULL, path, sizeof(path)) != 0) {
char* pch = strrchr(path, '\\');
if (pch == NULL) {
pch = path;
}
strcpy(pch, "\\ereg");
STARTUPINFOA startupInfo;
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
PROCESS_INFORMATION processInfo;
// FIXME: Leaking processInfo.hProcess and processInfo.hThread:
// https://docs.microsoft.com/en-us/cpp/code-quality/c6335.
if (CreateProcessA("ereg\\reg32a.exe", NULL, NULL, NULL, FALSE, 0, NULL, path, &startupInfo, &processInfo)) {
WaitForSingleObject(processInfo.hProcess, INFINITE);
}
}
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_TIMES_RUN_KEY, timesRun + 1);
} else {
if (timesRun == 0) {
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_TIMES_RUN_KEY, 1);
}
}
}

View File

@ -0,0 +1,6 @@
#ifndef ELECTRONIC_REGISTRATION_H
#define ELECTRONIC_REGISTRATION_H
void runElectronicRegistration();
#endif /* ELECTRONIC_REGISTRATION_H */

647
src/elevator.c Normal file
View File

@ -0,0 +1,647 @@
#include "elevator.h"
#include "core.h"
#include "cycle.h"
#include "debug.h"
#include "draw.h"
#include "game_mouse.h"
#include "game_sound.h"
#include "map.h"
#include "pipboy.h"
#include "scripts.h"
#include "window_manager.h"
#include <ctype.h>
#include <string.h>
// 0x43E950
const int gElevatorFrmIds[ELEVATOR_FRM_COUNT] = {
141, // ebut_in.frm - map elevator screen
142, // ebut_out.frm - map elevator screen
149, // gaj000.frm - map elevator screen
};
// 0x43E95C
const ElevatorBackground gElevatorBackgrounds[ELEVATOR_COUNT] = {
{ 143, -1 },
{ 143, 150 },
{ 144, -1 },
{ 144, 145 },
{ 146, -1 },
{ 146, 147 },
{ 146, -1 },
{ 146, 151 },
{ 148, -1 },
{ 146, -1 },
{ 146, -1 },
{ 146, 147 },
{ 388, -1 },
{ 143, 150 },
{ 148, -1 },
{ 148, -1 },
{ 148, -1 },
{ 143, 150 },
{ 143, 150 },
{ 143, 150 },
{ 143, 150 },
{ 143, 150 },
{ 143, 150 },
{ 143, 150 },
};
// Number of levels for eleveators.
//
// 0x43EA1C
const int gElevatorLevels[ELEVATOR_COUNT] = {
4,
2,
3,
2,
3,
2,
3,
3,
3,
3,
3,
2,
4,
2,
3,
3,
3,
2,
2,
2,
2,
2,
2,
2,
};
// 0x43EA7C
const ElevatorDescription gElevatorDescriptions[ELEVATOR_COUNT][ELEVATOR_LEVEL_MAX] = {
{
{ 14, 0, 18940 },
{ 14, 1, 18936 },
{ 15, 0, 21340 },
{ 15, 1, 21340 },
},
{
{ 13, 0, 20502 },
{ 14, 0, 14912 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 33, 0, 12498 },
{ 33, 1, 20094 },
{ 34, 0, 17312 },
{ 0, 0, -1 },
},
{
{ 34, 0, 16140 },
{ 34, 1, 16140 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 49, 0, 14920 },
{ 49, 1, 15120 },
{ 50, 0, 12944 },
{ 0, 0, -1 },
},
{
{ 50, 0, 24520 },
{ 50, 1, 25316 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 42, 0, 22526 },
{ 42, 1, 22526 },
{ 42, 2, 22526 },
{ 0, 0, -1 },
},
{
{ 42, 2, 14086 },
{ 43, 0, 14086 },
{ 43, 2, 14086 },
{ 0, 0, -1 },
},
{
{ 40, 0, 14104 },
{ 40, 1, 22504 },
{ 40, 2, 17312 },
{ 0, 0, -1 },
},
{
{ 9, 0, 13704 },
{ 9, 1, 23302 },
{ 9, 2, 17308 },
{ 0, 0, -1 },
},
{
{ 28, 0, 19300 },
{ 28, 1, 19300 },
{ 28, 2, 20110 },
{ 0, 0, -1 },
},
{
{ 28, 2, 20118 },
{ 29, 0, 21710 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 28, 0, 20122 },
{ 28, 1, 20124 },
{ 28, 2, 20940 },
{ 29, 0, 22540 },
},
{
{ 12, 1, 16052 },
{ 12, 2, 14480 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 6, 0, 14104 },
{ 6, 1, 22504 },
{ 6, 2, 17312 },
{ 0, 0, -1 },
},
{
{ 30, 0, 14104 },
{ 30, 1, 22504 },
{ 30, 2, 17312 },
{ 0, 0, -1 },
},
{
{ 36, 0, 13704 },
{ 36, 1, 23302 },
{ 36, 2, 17308 },
{ 0, 0, -1 },
},
{
{ 39, 0, 17285 },
{ 36, 0, 19472 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 109, 2, 10701 },
{ 109, 1, 10705 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 109, 2, 14697 },
{ 109, 1, 15099 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 109, 2, 23877 },
{ 109, 1, 25481 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 109, 2, 26130 },
{ 109, 1, 24721 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 137, 0, 23953 },
{ 148, 1, 16526 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
{
{ 62, 0, 13901 },
{ 63, 1, 17923 },
{ 0, 0, -1 },
{ 0, 0, -1 },
},
};
// NOTE: These values are also used as key bindings.
//
// 0x43EEFC
const char gElevatorLevelLabels[ELEVATOR_COUNT][ELEVATOR_LEVEL_MAX] = {
{ '1', '2', '3', '4' },
{ 'G', '1', '\0', '\0' },
{ '1', '2', '3', '\0' },
{ '3', '4', '\0', '\0' },
{ '1', '2', '3', '\0' },
{ '3', '4', '\0', '\0' },
{ '1', '2', '3', '\0' },
{ '3', '4', '6', '\0' },
{ '1', '2', '3', '\0' },
{ '1', '2', '3', '\0' },
{ '1', '2', '3', '\0' },
{ '3', '4', '\0', '\0' },
{ '1', '2', '3', '4' },
{ '1', '2', '\0', '\0' },
{ '1', '2', '3', '\0' },
{ '1', '2', '3', '\0' },
{ '1', '2', '3', '\0' },
{ '1', '2', '\0', '\0' },
{ '1', '2', '\0', '\0' },
{ '1', '2', '\0', '\0' },
{ '1', '2', '\0', '\0' },
{ '1', '2', '\0', '\0' },
{ '1', '2', '\0', '\0' },
{ '1', '2', '\0', '\0' },
};
// 0x51862C
const char* gElevatorSoundEffects[ELEVATOR_LEVEL_MAX - 1][ELEVATOR_LEVEL_MAX] = {
{
"ELV1_1",
"ELV1_1",
"ERROR",
"ERROR",
},
{
"ELV1_2",
"ELV1_2",
"ELV1_1",
"ERROR",
},
{
"ELV1_3",
"ELV1_3",
"ELV2_3",
"ELV1_1",
},
};
// 0x570A2C
Size gElevatorFrmSizes[ELEVATOR_FRM_COUNT];
// 0x570A44
int gElevatorBackgroundFrmWidth;
// 0x570A48
int gElevatorBackgroundFrmHeight;
// 0x570A4C
int gElevatorPanelFrmWidth;
// 0x570A50
int gElevatorPanelFrmHeight;
// 0x570A54
int gElevatorWindow;
// 0x570A58
CacheEntry* gElevatorFrmHandles[ELEVATOR_FRM_COUNT];
// 0x570A64
CacheEntry* gElevatorBackgroundFrmHandle;
// 0x570A68
CacheEntry* gElevatorPanelFrmHandle;
// 0x570A6C
unsigned char* gElevatorWindowBuffer;
// 0x570A70
bool gElevatorWindowIsoWasEnabled;
// 0x570A74
unsigned char* gElevatorFrmData[ELEVATOR_FRM_COUNT];
// 0x570A80
unsigned char* gElevatorBackgroundFrmData;
// 0x570A84
unsigned char* gElevatorPanelFrmData;
// Presents elevator dialog for player to pick a desired level.
//
// 0x43EF5C
int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tilePtr)
{
if (elevator < 0 || elevator >= ELEVATOR_COUNT) {
return -1;
}
if (elevatorWindowInit(elevator) == -1) {
return -1;
}
const ElevatorDescription* elevatorDescription = gElevatorDescriptions[elevator];
int index;
for (index = 0; index < ELEVATOR_LEVEL_MAX; index++) {
if (elevatorDescription[index].map == *mapPtr) {
break;
}
}
if (index < ELEVATOR_LEVEL_MAX) {
if (elevatorDescription[*elevationPtr + index].tile != -1) {
*elevationPtr += index;
}
}
if (elevator == ELEVATOR_SIERRA_2) {
if (*elevationPtr <= 2) {
*elevationPtr -= 2;
} else {
*elevationPtr -= 3;
}
} else if (elevator == ELEVATOR_MILITARY_BASE_LOWER) {
if (*elevationPtr >= 2) {
*elevationPtr -= 2;
}
} else if (elevator == ELEVATOR_MILITARY_BASE_UPPER && *elevationPtr == 4) {
*elevationPtr -= 2;
}
if (*elevationPtr > 3) {
*elevationPtr -= 3;
}
debugPrint("\n the start elev level %d\n", *elevationPtr);
int v18 = (gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width * gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].height) / 13;
float v42 = 12.0f / (float)(gElevatorLevels[elevator] - 1);
blitBufferToBuffer(
gElevatorFrmData[ELEVATOR_FRM_GAUGE] + v18 * (int)((float)(*elevationPtr) * v42),
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].height / 13,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorWindowBuffer + gElevatorBackgroundFrmWidth * 41 + 121,
gElevatorBackgroundFrmWidth);
windowRefresh(gElevatorWindow);
bool done = false;
int keyCode;
while (!done) {
keyCode = _get_input();
if (keyCode == KEY_ESCAPE) {
done = true;
}
if (keyCode >= 500 && keyCode < 504) {
done = true;
}
if (keyCode > 0 && keyCode < 500) {
int level = elevatorGetLevelFromKeyCode(elevator, keyCode);
if (level != 0) {
keyCode = 500 + level - 1;
done = true;
}
}
}
if (keyCode != KEY_ESCAPE) {
keyCode -= 500;
if (*elevationPtr != keyCode) {
float v43 = (float)(gElevatorLevels[elevator] - 1) / 12.0f;
unsigned int delay = (unsigned int)(v43 * 276.92307);
if (keyCode < *elevationPtr) {
v43 = -v43;
}
int numberOfLevelsTravelled = keyCode - *elevationPtr;
if (numberOfLevelsTravelled < 0) {
numberOfLevelsTravelled = -numberOfLevelsTravelled;
}
soundPlayFile(gElevatorSoundEffects[gElevatorLevels[elevator] - 2][numberOfLevelsTravelled]);
float v41 = (float)keyCode * v42;
float v44 = (float)(*elevationPtr) * v42;
do {
unsigned int tick = _get_time();
v44 += v43;
blitBufferToBuffer(
gElevatorFrmData[ELEVATOR_FRM_GAUGE] + v18 * (int)v44,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].height / 13,
gElevatorFrmSizes[ELEVATOR_FRM_GAUGE].width,
gElevatorWindowBuffer + gElevatorBackgroundFrmWidth * 41 + 121,
gElevatorBackgroundFrmWidth);
windowRefresh(gElevatorWindow);
while (getTicksSince(tick) < delay) {
}
} while ((v43 <= 0.0 || v44 < v41) && (v43 > 0.0 || v44 > v41));
coreDelayProcessingEvents(200);
}
}
elevatorWindowFree();
if (keyCode != KEY_ESCAPE) {
const ElevatorDescription* description = &(elevatorDescription[keyCode]);
*mapPtr = description->map;
*elevationPtr = description->elevation;
*tilePtr = description->tile;
}
return 0;
}
// 0x43F324
int elevatorWindowInit(int elevator)
{
gElevatorWindowIsoWasEnabled = isoDisable();
colorCycleDisable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
gameMouseObjectsHide();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
scriptsDisable();
int index;
for (index = 0; index < ELEVATOR_FRM_COUNT; index++) {
int fid = buildFid(6, gElevatorFrmIds[index], 0, 0, 0);
gElevatorFrmData[index] = artLockFrameDataReturningSize(fid, &(gElevatorFrmHandles[index]), &(gElevatorFrmSizes[index].width), &(gElevatorFrmSizes[index].height));
if (gElevatorFrmData[index] == NULL) {
break;
}
}
if (index != ELEVATOR_FRM_COUNT) {
for (int reversedIndex = index - 1; reversedIndex >= 0; reversedIndex--) {
artUnlock(gElevatorFrmHandles[reversedIndex]);
}
if (gElevatorWindowIsoWasEnabled) {
isoEnable();
}
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
return -1;
}
gElevatorPanelFrmData = ELEVATOR_BACKGROUND_NULL;
gElevatorBackgroundFrmData = ELEVATOR_BACKGROUND_NULL;
const ElevatorBackground* elevatorBackground = &(gElevatorBackgrounds[elevator]);
bool backgroundsLoaded = true;
int backgroundFid = buildFid(6, elevatorBackground->backgroundFrmId, 0, 0, 0);
gElevatorBackgroundFrmData = artLockFrameDataReturningSize(backgroundFid, &gElevatorBackgroundFrmHandle, &gElevatorBackgroundFrmWidth, &gElevatorBackgroundFrmHeight);
if (gElevatorBackgroundFrmData != NULL) {
if (elevatorBackground->panelFrmId != -1) {
int panelFid = buildFid(6, elevatorBackground->panelFrmId, 0, 0, 0);
gElevatorPanelFrmData = artLockFrameDataReturningSize(panelFid, &gElevatorPanelFrmHandle, &gElevatorPanelFrmWidth, &gElevatorPanelFrmHeight);
if (gElevatorPanelFrmData == NULL) {
gElevatorPanelFrmData = ELEVATOR_BACKGROUND_NULL;
backgroundsLoaded = false;
}
}
} else {
gElevatorBackgroundFrmData = ELEVATOR_BACKGROUND_NULL;
backgroundsLoaded = false;
}
if (!backgroundsLoaded) {
if (gElevatorBackgroundFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorBackgroundFrmHandle);
}
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorPanelFrmHandle);
}
for (int index = 0; index < ELEVATOR_FRM_COUNT; index++) {
artUnlock(gElevatorFrmHandles[index]);
}
if (gElevatorWindowIsoWasEnabled) {
isoEnable();
}
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
return -1;
}
gElevatorWindow = windowCreate(
(640 - gElevatorBackgroundFrmWidth) / 2,
(379 - gElevatorBackgroundFrmHeight) / 2,
gElevatorBackgroundFrmWidth,
gElevatorBackgroundFrmHeight,
256,
WINDOW_FLAG_0x10 | WINDOW_FLAG_0x02);
if (gElevatorWindow == -1) {
if (gElevatorBackgroundFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorBackgroundFrmHandle);
}
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorPanelFrmHandle);
}
for (int index = 0; index < ELEVATOR_FRM_COUNT; index++) {
artUnlock(gElevatorFrmHandles[index]);
}
if (gElevatorWindowIsoWasEnabled) {
isoEnable();
}
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
return -1;
}
gElevatorWindowBuffer = windowGetBuffer(gElevatorWindow);
memcpy(gElevatorWindowBuffer, (unsigned char*)gElevatorBackgroundFrmData, gElevatorBackgroundFrmWidth * gElevatorBackgroundFrmHeight);
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
blitBufferToBuffer((unsigned char*)gElevatorPanelFrmData,
gElevatorPanelFrmWidth,
gElevatorPanelFrmHeight,
gElevatorPanelFrmWidth,
gElevatorWindowBuffer + gElevatorBackgroundFrmWidth * (gElevatorBackgroundFrmHeight - gElevatorPanelFrmHeight),
gElevatorBackgroundFrmWidth);
}
int y = 40;
for (int level = 0; level < gElevatorLevels[elevator]; level++) {
int btn = buttonCreate(gElevatorWindow,
13,
y,
gElevatorFrmSizes[ELEVATOR_FRM_BUTTON_DOWN].width,
gElevatorFrmSizes[ELEVATOR_FRM_BUTTON_DOWN].height,
-1,
-1,
-1,
500 + level,
gElevatorFrmData[ELEVATOR_FRM_BUTTON_UP],
gElevatorFrmData[ELEVATOR_FRM_BUTTON_DOWN],
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_red_butt_press, NULL);
}
y += 60;
}
return 0;
}
// 0x43F6D0
void elevatorWindowFree()
{
windowDestroy(gElevatorWindow);
if (gElevatorBackgroundFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorBackgroundFrmHandle);
}
if (gElevatorPanelFrmData != ELEVATOR_BACKGROUND_NULL) {
artUnlock(gElevatorPanelFrmHandle);
}
for (int index = 0; index < ELEVATOR_FRM_COUNT; index++) {
artUnlock(gElevatorFrmHandles[index]);
}
scriptsEnable();
if (gElevatorWindowIsoWasEnabled) {
isoEnable();
}
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
}
// 0x43F73C
int elevatorGetLevelFromKeyCode(int elevator, int keyCode)
{
// TODO: Check if result is really unused?
toupper(keyCode);
for (int index = 0; index < ELEVATOR_LEVEL_MAX; index++) {
char c = gElevatorLevelLabels[elevator][index];
if (c == '\0') {
break;
}
if (c == (char)(keyCode & 0xFF)) {
return index + 1;
}
}
return 0;
}

79
src/elevator.h Normal file
View File

@ -0,0 +1,79 @@
#ifndef ELEVATOR_H
#define ELEVATOR_H
#include "art.h"
#include "geometry.h"
// The maximum number of elevator levels.
#define ELEVATOR_LEVEL_MAX (4)
// NOTE: There are two variables which hold background data used in the
// elevator window - [gElevatorBackgroundFrmData] and [gElevatorPanelFrmData].
// For unknown reason they are using -1 to denote that they are not set
// (instead of using NULL).
#define ELEVATOR_BACKGROUND_NULL ((unsigned char*)(-1))
typedef enum Elevator {
ELEVATOR_BROTHERHOOD_OF_STEEL_MAIN,
ELEVATOR_BROTHERHOOD_OF_STEEL_SURFACE,
ELEVATOR_MASTER_UPPER,
ELEVATOR_MASTER_LOWER,
ELEVATOR_MILITARY_BASE_UPPER,
ELEVATOR_MILITARY_BASE_LOWER,
ELEVATOR_GLOW_UPPER,
ELEVATOR_GLOW_LOWER,
ELEVATOR_VAULT_13,
ELEVATOR_NECROPOLIS,
ELEVATOR_SIERRA_1,
ELEVATOR_SIERRA_2,
ELEVATOR_SIERRA_SERVICE,
ELEVATOR_COUNT = 24,
} Elevator;
typedef enum ElevatorFrm {
ELEVATOR_FRM_BUTTON_DOWN,
ELEVATOR_FRM_BUTTON_UP,
ELEVATOR_FRM_GAUGE,
ELEVATOR_FRM_COUNT,
} ElevatorFrm;
typedef struct ElevatorBackground {
int backgroundFrmId;
int panelFrmId;
} ElevatorBackground;
typedef struct ElevatorDescription {
int map;
int elevation;
int tile;
} ElevatorDescription;
extern const int gElevatorFrmIds[ELEVATOR_FRM_COUNT];
extern const ElevatorBackground gElevatorBackgrounds[ELEVATOR_COUNT];
extern const int gElevatorLevels[ELEVATOR_COUNT];
extern const ElevatorDescription gElevatorDescriptions[ELEVATOR_COUNT][ELEVATOR_LEVEL_MAX];
extern const char gElevatorLevelLabels[ELEVATOR_COUNT][ELEVATOR_LEVEL_MAX];
extern const char* gElevatorSoundEffects[ELEVATOR_LEVEL_MAX - 1][ELEVATOR_LEVEL_MAX];
extern Size gElevatorFrmSizes[ELEVATOR_FRM_COUNT];
extern int gElevatorBackgroundFrmWidth;
extern int gElevatorBackgroundFrmHeight;
extern int gElevatorPanelFrmWidth;
extern int gElevatorPanelFrmHeight;
extern int gElevatorWindow;
extern CacheEntry* gElevatorFrmHandles[ELEVATOR_FRM_COUNT];
extern CacheEntry* gElevatorBackgroundFrmHandle;
extern CacheEntry* gElevatorPanelFrmHandle;
extern unsigned char* gElevatorWindowBuffer;
extern bool gElevatorWindowIsoWasEnabled;
extern unsigned char* gElevatorFrmData[ELEVATOR_FRM_COUNT];
extern unsigned char* gElevatorBackgroundFrmData;
extern unsigned char* gElevatorPanelFrmData;
int elevatorSelectLevel(int elevator, int* mapPtr, int* elevationPtr, int* tilePtr);
int elevatorWindowInit(int elevator);
void elevatorWindowFree();
int elevatorGetLevelFromKeyCode(int elevator, int keyCode);
#endif /* ELEVATOR_H */

1153
src/endgame.c Normal file

File diff suppressed because it is too large Load Diff

92
src/endgame.h Normal file
View File

@ -0,0 +1,92 @@
#ifndef ENDGAME_H
#define ENDGAME_H
// Provides [MAX_PATH].
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdbool.h>
typedef enum EndgameDeathEndingReason {
// Dude died.
ENDGAME_DEATH_ENDING_REASON_DEATH = 0,
// 13 years passed.
ENDGAME_DEATH_ENDING_REASON_TIMEOUT = 2,
} EndgameDeathEndingReason;
typedef struct EndgameDeathEnding {
int gvar;
int value;
int worldAreaKnown;
int worldAreaNotKnown;
int min_level;
int percentage;
char voiceOverBaseName[16];
// This flag denotes that the conditions for this ending is met and it was
// selected as a candidate for final random selection.
bool enabled;
} EndgameDeathEnding;
typedef struct EndgameEnding {
int gvar;
int value;
int art_num;
char voiceOverBaseName[12];
int direction;
} EndgameEnding;
extern char _aEnglish_2[];
extern int gEndgameEndingSubtitlesLength;
extern int gEndgameEndingSubtitlesCharactersCount;
extern int gEndgameEndingSubtitlesCurrentLine;
extern int _endgame_maybe_done;
extern EndgameDeathEnding* gEndgameDeathEndings;
extern int gEndgameDeathEndingsLength;
extern char gEndgameDeathEndingFileName[40];
extern bool gEndgameEndingVoiceOverSpeechLoaded;
extern char gEndgameEndingSubtitlesLocalizedPath[MAX_PATH];
extern bool gEndgameEndingSpeechEnded;
extern EndgameEnding* gEndgameEndings;
extern char** gEndgameEndingSubtitles;
extern bool gEndgameEndingSubtitlesEnabled;
extern bool gEndgameEndingSubtitlesEnded;
extern bool _endgame_map_enabled;
extern bool _endgame_mouse_state;
extern int gEndgameEndingsLength;
extern bool gEndgameEndingVoiceOverSubtitlesLoaded;
extern unsigned int gEndgameEndingSubtitlesReferenceTime;
extern unsigned int* gEndgameEndingSubtitlesTimings;
extern int gEndgameEndingSlideshowOldFont;
extern unsigned char* gEndgameEndingSlideshowWindowBuffer;
extern int gEndgameEndingSlideshowWindow;
void endgamePlaySlideshow();
void endgamePlayMovie();
void endgameEndingRenderPanningScene(int direction, const char* narratorFileName);
void endgameEndingRenderStaticScene(int fid, const char* narratorFileName);
int endgameEndingHandleContinuePlaying();
int endgameEndingSlideshowWindowInit();
void endgameEndingSlideshowWindowFree();
void endgameEndingVoiceOverInit(const char* fname);
void endgameEndingVoiceOverReset();
void endgameEndingVoiceOverFree();
void endgameEndingLoadPalette(int type, int id);
void _endgame_voiceover_callback();
int endgameEndingSubtitlesLoad(const char* filePath);
void endgameEndingRefreshSubtitles();
void endgameEndingSubtitlesFree();
void _endgame_movie_callback();
void _endgame_movie_bk_process();
int endgameEndingInit();
void endgameEndingFree();
int endgameDeathEndingInit();
int endgameDeathEndingExit();
void endgameSetupDeathEnding(int reason);
int endgameDeathEndingValidate(int* percentage);
char* endgameDeathEndingGetFileName();
#endif /* ENDGAME_H */

325
src/export.c Normal file
View File

@ -0,0 +1,325 @@
#include "export.h"
#include "interpreter_lib.h"
#include "memory_manager.h"
#include <ctype.h>
#include <string.h>
// 0x570C00
ExternalProcedure gExternalProcedures[1013];
// 0x57BA1C
ExternalVariable gExternalVariables[1013];
// NOTE: Inlined.
//
// 0x440F10
unsigned int _hashName(const char* identifier)
{
unsigned int v1 = 0;
const char* pch = identifier;
while (*pch != '\0') {
int ch = *pch & 0xFF;
v1 += (tolower(ch) & 0xFF) + (v1 * 8) + (v1 >> 29);
pch++;
}
v1 = v1 % 1013;
return v1;
}
// 0x440F58
ExternalProcedure* externalProcedureFind(const char* identifier)
{
// NOTE: Uninline.
unsigned int v1 = _hashName(identifier);
unsigned int v2 = v1;
ExternalProcedure* externalProcedure = &(gExternalProcedures[v1]);
if (externalProcedure->program != NULL) {
if (stricmp(externalProcedure->name, identifier) == 0) {
return externalProcedure;
}
}
do {
v1 += 7;
if (v1 >= 1013) {
v1 -= 1013;
}
externalProcedure = &(gExternalProcedures[v1]);
if (externalProcedure->program != NULL) {
if (stricmp(externalProcedure->name, identifier) == 0) {
return externalProcedure;
}
}
} while (v1 != v2);
return NULL;
}
// 0x441018
ExternalProcedure* externalProcedureAdd(const char* identifier)
{
// NOTE: Uninline.
unsigned int v1 = _hashName(identifier);
unsigned int a2 = v1;
ExternalProcedure* externalProcedure = &(gExternalProcedures[v1]);
if (externalProcedure->name[0] == '\0') {
return externalProcedure;
}
do {
v1 += 7;
if (v1 >= 1013) {
v1 -= 1013;
}
externalProcedure = &(gExternalProcedures[v1]);
if (externalProcedure->name[0] == '\0') {
return externalProcedure;
}
} while (v1 != a2);
return NULL;
}
// 0x4410AC
ExternalVariable* externalVariableFind(const char* identifier)
{
// NOTE: Uninline.
unsigned int v1 = _hashName(identifier);
unsigned int v2 = v1;
ExternalVariable* exportedVariable = &(gExternalVariables[v1]);
if (stricmp(exportedVariable->name, identifier) == 0) {
return exportedVariable;
}
do {
exportedVariable = &(gExternalVariables[v1]);
if (exportedVariable->name[0] == '\0') {
break;
}
v1 += 7;
if (v1 >= 1013) {
v1 -= 1013;
}
exportedVariable = &(gExternalVariables[v1]);
if (stricmp(exportedVariable->name, identifier) == 0) {
return exportedVariable;
}
} while (v1 != v2);
return NULL;
}
// 0x44118C
ExternalVariable* externalVariableAdd(const char* identifier)
{
// NOTE: Uninline.
unsigned int v1 = _hashName(identifier);
unsigned int v2 = v1;
ExternalVariable* exportedVariable = &(gExternalVariables[v1]);
if (exportedVariable->name[0] == '\0') {
return exportedVariable;
}
do {
v1 += 7;
if (v1 >= 1013) {
v1 -= 1013;
}
exportedVariable = &(gExternalVariables[v1]);
if (exportedVariable->name[0] == '\0') {
return exportedVariable;
}
} while (v1 != v2);
return NULL;
}
// 0x44127C
int externalVariableSetValue(Program* program, const char* name, opcode_t opcode, int data)
{
ExternalVariable* exportedVariable = externalVariableFind(name);
if (exportedVariable == NULL) {
return 1;
}
if ((exportedVariable->type & 0xF7FF) == VALUE_TYPE_STRING) {
internal_free_safe(exportedVariable->stringValue, __FILE__, __LINE__); // "..\\int\\EXPORT.C", 169
}
if ((opcode & 0xF7FF) == VALUE_TYPE_STRING) {
if (program != NULL) {
const char* stringValue = programGetString(program, opcode, data);
exportedVariable->type = VALUE_TYPE_DYNAMIC_STRING;
exportedVariable->stringValue = internal_malloc_safe(strlen(stringValue) + 1, __FILE__, __LINE__); // "..\\int\\EXPORT.C", 175
strcpy(exportedVariable->stringValue, stringValue);
}
} else {
exportedVariable->value = data;
exportedVariable->type = opcode;
}
return 0;
}
// 0x4413D4
int externalVariableGetValue(Program* program, const char* name, opcode_t* opcodePtr, int* dataPtr)
{
ExternalVariable* exportedVariable = externalVariableFind(name);
if (exportedVariable == NULL) {
return 1;
}
*opcodePtr = exportedVariable->type;
if ((exportedVariable->type & 0xF7FF) == VALUE_TYPE_STRING) {
*dataPtr = programPushString(program, exportedVariable->stringValue);
} else {
*dataPtr = exportedVariable->value;
}
return 0;
}
// 0x4414B8
int externalVariableCreate(Program* program, const char* identifier)
{
const char* programName = program->name;
ExternalVariable* exportedVariable = externalVariableFind(identifier);
if (exportedVariable != NULL) {
if (stricmp(exportedVariable->programName, programName) != 0) {
return 1;
}
if ((exportedVariable->type & 0xF7FF) == VALUE_TYPE_STRING) {
internal_free_safe(exportedVariable->stringValue, __FILE__, __LINE__); // "..\\int\\EXPORT.C", 234
}
} else {
exportedVariable = externalVariableAdd(identifier);
if (exportedVariable == NULL) {
return 1;
}
strncpy(exportedVariable->name, identifier, 31);
exportedVariable->programName = internal_malloc_safe(strlen(programName) + 1, __FILE__, __LINE__); // // "..\\int\\EXPORT.C", 243
strcpy(exportedVariable->programName, programName);
}
exportedVariable->type = VALUE_TYPE_INT;
exportedVariable->value = 0;
return 0;
}
// 0x4414FC
void _removeProgramReferences(Program* program)
{
for (int index = 0; index < 1013; index++) {
ExternalProcedure* externalProcedure = &(gExternalProcedures[index]);
if (externalProcedure->program == program) {
externalProcedure->name[0] = '\0';
externalProcedure->program = NULL;
}
}
}
// 0x44152C
void _initExport()
{
_interpretRegisterProgramDeleteCallback(_removeProgramReferences);
}
// 0x441538
void externalVariablesClear()
{
for (int index = 0; index < 1013; index++) {
ExternalVariable* exportedVariable = &(gExternalVariables[index]);
if (exportedVariable->name[0] != '\0') {
internal_free_safe(exportedVariable->programName, __FILE__, __LINE__); // ..\\int\\EXPORT.C, 274
}
if (exportedVariable->type == VALUE_TYPE_DYNAMIC_STRING) {
internal_free_safe(exportedVariable->stringValue, __FILE__, __LINE__); // ..\\int\\EXPORT.C, 276
}
}
}
// 0x44158C
Program* externalProcedureGetProgram(const char* identifier, int* addressPtr, int* argumentCountPtr)
{
ExternalProcedure* externalProcedure = externalProcedureFind(identifier);
if (externalProcedure == NULL) {
return NULL;
}
if (externalProcedure->program == NULL) {
return NULL;
}
*addressPtr = externalProcedure->address;
*argumentCountPtr = externalProcedure->argumentCount;
return externalProcedure->program;
}
// 0x4415B0
int externalProcedureCreate(Program* program, const char* identifier, int address, int argumentCount)
{
ExternalProcedure* externalProcedure = externalProcedureFind(identifier);
if (externalProcedure != NULL) {
if (program != externalProcedure->program) {
return 1;
}
} else {
externalProcedure = externalProcedureAdd(identifier);
if (externalProcedure == NULL) {
return 1;
}
strncpy(externalProcedure->name, identifier, 31);
}
externalProcedure->argumentCount = argumentCount;
externalProcedure->address = address;
externalProcedure->program = program;
return 0;
}
// 0x441824
void _exportClearAllVariables()
{
for (int index = 0; index < 1013; index++) {
ExternalVariable* exportedVariable = &(gExternalVariables[index]);
if (exportedVariable->name[0] != '\0') {
if ((exportedVariable->type & 0xF7FF) == VALUE_TYPE_STRING) {
if (exportedVariable->stringValue != NULL) {
internal_free_safe(exportedVariable->stringValue, __FILE__, __LINE__); // "..\\int\\EXPORT.C", 387
}
}
if (exportedVariable->programName != NULL) {
internal_free_safe(exportedVariable->programName, __FILE__, __LINE__); // "..\\int\\EXPORT.C", 393
exportedVariable->programName = NULL;
}
exportedVariable->name[0] = '\0';
exportedVariable->type = 0;
}
}
}

40
src/export.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef EXPORT_H
#define EXPORT_H
#include "interpreter.h"
typedef struct ExternalVariable {
char name[32];
char* programName;
opcode_t type;
union {
int value;
char* stringValue;
};
} ExternalVariable;
typedef struct ExternalProcedure {
char name[32];
Program* program;
int argumentCount;
int address;
} ExternalProcedure;
extern ExternalProcedure gExternalProcedures[1013];
extern ExternalVariable gExternalVariables[1013];
ExternalProcedure* externalProcedureFind(const char* identifier);
ExternalProcedure* externalProcedureAdd(const char* identifier);
ExternalVariable* externalVariableFind(const char* identifier);
ExternalVariable* externalVariableAdd(const char* identifier);
int externalVariableSetValue(Program* program, const char* identifier, opcode_t opcode, int data);
int externalVariableGetValue(Program* program, const char* name, opcode_t* opcodePtr, int* dataPtr);
int externalVariableCreate(Program* program, const char* identifier);
void _removeProgramReferences(Program* program);
void _initExport();
void externalVariablesClear();
Program* externalProcedureGetProgram(const char* identifier, int* addressPtr, int* argumentCountPtr);
int externalProcedureCreate(Program* program, const char* identifier, int address, int argumentCount);
void _exportClearAllVariables();
#endif /* EXPORT_H */

63
src/file_find.c Normal file
View File

@ -0,0 +1,63 @@
#include "file_find.h"
// 0x4E6380
bool fileFindFirst(const char* path, DirectoryFileFindData* findData)
{
#if defined(_MSC_VER)
findData->hFind = FindFirstFileA(path, &(findData->ffd));
if (findData->hFind == INVALID_HANDLE_VALUE) {
return false;
}
#elif defined(__WATCOMC__)
findData->dir = opendir(path);
if (findData->dir == NULL) {
return false;
}
findData->entry = readdir(findData->dir);
if (findData->entry == NULL) {
closedir(findData->dir);
return false;
}
#else
#error Not implemented
#endif
return true;
}
// 0x4E63A8
bool fileFindNext(DirectoryFileFindData* findData)
{
#if defined(_MSC_VER)
if (!FindNextFileA(findData->hFind, &(findData->ffd))) {
return false;
}
#elif defined(__WATCOMC__)
findData->entry = readdir(findData->dir);
if (findData->entry == NULL) {
closedir(findData->dir);
return false;
}
#else
#error Not implemented
#endif
return true;
}
// 0x4E63CC
bool findFindClose(DirectoryFileFindData* findData)
{
#if defined(_MSC_VER)
FindClose(findData->hFind);
#elif defined(__WATCOMC__)
if (closedir(findData->dir) != 0) {
return false;
}
#else
#error Not implemented
#endif
return true;
}

44
src/file_find.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef FILE_FIND_H
#define FILE_FIND_H
#include <stdbool.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// NOTE: This structure is significantly different from what was in the
// original code. Watcom provides opendir/readdir/closedir implementations,
// that use Win32 FindFirstFile/FindNextFile under the hood, which in turn
// is designed to deal with patterns.
//
// The first attempt was to use `dirent` implementation by Toni Ronkko
// (https://github.com/tronkko/dirent), however it appears to be incompatible
// with what is provided by Watcom. Toni's implementation adds `*` wildcard
// unconditionally implying `opendir` accepts directory name only, which I
// guess is fine when your goal is compliance with POSIX implementation.
// However in Watcom `opendir` can handle file patterns gracefully. The problem
// can be seen during game startup when cleaning MAPS directory using
// MAPS\*.SAV pattern. Toni's implementation tries to convert that to pattern
// for Win32 API, thus making it MAPS\*.SAV\*, which is obviously incorrect
// path/pattern for any implementation.
//
// Eventually I've decided to go with compiler-specific implementation, keeping
// original implementation for Watcom (not tested). I'm not sure it will work
// in other compilers, so for now just stick with the error.
typedef struct DirectoryFileFindData {
#if defined(_MSC_VER)
HANDLE hFind;
WIN32_FIND_DATAA ffd;
#elif defined(__WATCOMC__)
DIR* dir;
struct dirent* entry;
#else
#error Not implemented
#endif
} DirectoryFileFindData;
bool fileFindFirst(const char* path, DirectoryFileFindData* findData);
bool fileFindNext(DirectoryFileFindData* findData);
bool findFindClose(DirectoryFileFindData* findData);
#endif /* FILE_FIND_H */

144
src/file_utils.c Normal file
View File

@ -0,0 +1,144 @@
// NOTE: For unknown reason functions in this module use __stdcall instead
// of regular __usercall.
#include "file_utils.h"
#include <stdbool.h>
#include <stdio.h>
#include <zlib.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// 0x452740
int fileCopyDecompressed(const char* existingFilePath, const char* newFilePath)
{
FILE* stream = fopen(existingFilePath, "rb");
if (stream == NULL) {
return -1;
}
int magic[2];
magic[0] = fgetc(stream);
magic[1] = fgetc(stream);
fclose(stream);
if (magic[0] == 0x1F && magic[1] == 0x8B) {
gzFile inStream = gzopen(existingFilePath, "rb");
FILE* outStream = fopen(newFilePath, "wb");
if (inStream != NULL && outStream != NULL) {
for (;;) {
int ch = gzgetc(inStream);
if (ch == -1) {
break;
}
fputc(ch, outStream);
}
gzclose(inStream);
fclose(outStream);
} else {
if (inStream != NULL) {
gzclose(inStream);
}
if (outStream != NULL) {
fclose(outStream);
}
return -1;
}
} else {
CopyFileA(existingFilePath, newFilePath, FALSE);
}
return 0;
}
// 0x452804
int fileCopyCompressed(const char* existingFilePath, const char* newFilePath)
{
FILE* inStream = fopen(existingFilePath, "rb");
if (inStream == NULL) {
return -1;
}
int magic[2];
magic[0] = fgetc(inStream);
magic[1] = fgetc(inStream);
rewind(inStream);
if (magic[0] == 0x1F && magic[1] == 0x8B) {
// Source file is already gzipped, there is no need to do anything
// besides copying.
fclose(inStream);
CopyFileA(existingFilePath, newFilePath, FALSE);
} else {
gzFile outStream = gzopen(newFilePath, "wb");
if (outStream == NULL) {
fclose(inStream);
return -1;
}
// Copy byte-by-byte.
for (;;) {
int ch = fgetc(inStream);
if (ch == -1) {
break;
}
gzputc(outStream, ch);
}
fclose(inStream);
gzclose(outStream);
}
return 0;
}
// TODO: Check, implementation looks odd.
int _gzdecompress_file(const char* existingFilePath, const char* newFilePath)
{
FILE* stream = fopen(existingFilePath, "rb");
if (stream == NULL) {
return -1;
}
int magic[2];
magic[0] = fgetc(stream);
magic[1] = fgetc(stream);
fclose(stream);
// TODO: Is it broken?
if (magic[0] != 0x1F || magic[1] != 0x8B) {
gzFile gzstream = gzopen(existingFilePath, "rb");
if (gzstream == NULL) {
return -1;
}
stream = fopen(newFilePath, "wb");
if (stream == NULL) {
gzclose(gzstream);
return -1;
}
while (1) {
int ch = gzgetc(gzstream);
if (ch == -1) {
break;
}
fputc(ch, stream);
}
gzclose(gzstream);
fclose(stream);
} else {
CopyFileA(existingFilePath, newFilePath, FALSE);
}
return 0;
}

8
src/file_utils.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef FILE_UTILS_H
#define FILE_UTILS_H
int fileCopyDecompressed(const char* existingFilePath, const char* newFilePath);
int fileCopyCompressed(const char* existingFilePath, const char* newFilePath);
int _gzdecompress_file(const char* existingFilePath, const char* newFilePath);
#endif /* FILE_UTILS_H */

337
src/font_manager.c Normal file
View File

@ -0,0 +1,337 @@
#include "font_manager.h"
#include "color.h"
#include "db.h"
#include "memory_manager.h"
#include <stdio.h>
#include <string.h>
// 0x518680
bool gInterfaceFontsInitialized = false;
// 0x518684
int gInterfaceFontsLength = 0;
// 0x518688
FontManager gModernFontManager = {
100,
110,
interfaceFontSetCurrentImpl,
interfaceFontDrawImpl,
interfaceFontGetLineHeightImpl,
interfaceFontGetStringWidthImpl,
interfaceFontGetCharacterWidthImpl,
interfaceFontGetMonospacedStringWidthImpl,
interfaceFontGetLetterSpacingImpl,
interfaceFontGetBufferSizeImpl,
interfaceFontGetMonospacedCharacterWidthImpl,
};
// 0x586838
InterfaceFontDescriptor gInterfaceFontDescriptors[INTERFACE_FONT_MAX];
// 0x58E938
int gCurrentInterfaceFont;
// 0x58E93C
InterfaceFontDescriptor* gCurrentInterfaceFontDescriptor;
// 0x441C80
int interfaceFontsInit()
{
int currentFont = -1;
for (int font = 0; font < INTERFACE_FONT_MAX; font++) {
if (interfaceFontLoad(font) == -1) {
gInterfaceFontDescriptors[font].field_0 = 0;
gInterfaceFontDescriptors[font].data = NULL;
} else {
++gInterfaceFontsLength;
if (currentFont == -1) {
currentFont = font;
}
}
}
if (currentFont == -1) {
return -1;
}
gInterfaceFontsInitialized = true;
interfaceFontSetCurrentImpl(currentFont + 100);
return 0;
}
// 0x441CEC
void interfaceFontsExit()
{
for (int font = 0; font < INTERFACE_FONT_MAX; font++) {
if (gInterfaceFontDescriptors[font].data != NULL) {
internal_free_safe(gInterfaceFontDescriptors[font].data, __FILE__, __LINE__); // FONTMGR.C, 124
}
}
}
// 0x441D20
int interfaceFontLoad(int font_index)
{
InterfaceFontDescriptor* fontDescriptor = &(gInterfaceFontDescriptors[font_index]);
char path[56];
sprintf(path, "font%d.aaf", font_index);
File* stream = fileOpen(path, "rb");
if (stream == NULL) {
return false;
}
int fileSize = fileGetSize(stream);
int sig;
if (fileRead(&sig, 4, 1, stream) != 1) goto err;
sig = _byteswap_ulong(sig);
if (sig != 0x41414646) goto err;
if (fileRead(&(fontDescriptor->field_0), 2, 1, stream) != 1) goto err;
fontDescriptor->field_0 = _byteswap_ushort(fontDescriptor->field_0);
if (fileRead(&(fontDescriptor->letterSpacing), 2, 1, stream) != 1) goto err;
fontDescriptor->letterSpacing = _byteswap_ushort(fontDescriptor->letterSpacing);
if (fileRead(&(fontDescriptor->wordSpacing), 2, 1, stream) != 1) goto err;
fontDescriptor->wordSpacing = _byteswap_ushort(fontDescriptor->wordSpacing);
if (fileRead(&(fontDescriptor->field_6), 2, 1, stream) != 1) goto err;
fontDescriptor->field_6 = _byteswap_ushort(fontDescriptor->field_6);
for (int index = 0; index < 256; index++) {
InterfaceFontGlyph* glyph = &(fontDescriptor->glyphs[index]);
if (fileRead(&(glyph->width), 2, 1, stream) != 1) goto err;
glyph->width = _byteswap_ushort(glyph->width);
if (fileRead(&(glyph->field_2), 2, 1, stream) != 1) goto err;
glyph->field_2 = _byteswap_ushort(glyph->field_2);
if (fileRead(&(glyph->field_4), 4, 1, stream) != 1) goto err;
glyph->field_4 = _byteswap_ulong(glyph->field_4);
}
fileSize -= sizeof(InterfaceFontDescriptor);
fontDescriptor->data = internal_malloc_safe(fileSize, __FILE__, __LINE__); // FONTMGR.C, 259
if (fontDescriptor->data == NULL) goto err;
if (fileRead(fontDescriptor->data, fileSize, 1, stream) != 1) {
internal_free_safe(fontDescriptor->data, __FILE__, __LINE__); // FONTMGR.C, 268
goto err;
}
fileClose(stream);
return 0;
err:
fileClose(stream);
return -1;
}
// 0x442120
void interfaceFontSetCurrentImpl(int font)
{
if (!gInterfaceFontsInitialized) {
return;
}
font -= 100;
if (gInterfaceFontDescriptors[font].data != NULL) {
gCurrentInterfaceFont = font;
gCurrentInterfaceFontDescriptor = &(gInterfaceFontDescriptors[font]);
}
}
// 0x442168
int interfaceFontGetLineHeightImpl()
{
if (!gInterfaceFontsInitialized) {
return 0;
}
return gCurrentInterfaceFontDescriptor->field_6 + gCurrentInterfaceFontDescriptor->field_0;
}
// 0x442188
int interfaceFontGetStringWidthImpl(const char* string)
{
if (!gInterfaceFontsInitialized) {
return 0;
}
const char* pch = string;
int width = 0;
while (*pch != '\0') {
int v3;
int v4;
if (*pch == ' ') {
v3 = gCurrentInterfaceFontDescriptor->letterSpacing;
v4 = gCurrentInterfaceFontDescriptor->wordSpacing;
} else {
v3 = gCurrentInterfaceFontDescriptor->glyphs[*pch & 0xFF].width;
v4 = gCurrentInterfaceFontDescriptor->letterSpacing;
}
width += v3 + v4;
pch++;
}
return width;
}
// 0x4421DC
int interfaceFontGetCharacterWidthImpl(int ch)
{
int width;
if (!gInterfaceFontsInitialized) {
return 0;
}
if (ch == ' ') {
width = gCurrentInterfaceFontDescriptor->wordSpacing;
} else {
width = gCurrentInterfaceFontDescriptor->glyphs[ch].width;
}
return width;
}
// 0x442210
int interfaceFontGetMonospacedStringWidthImpl(const char* str)
{
if (!gInterfaceFontsInitialized) {
return 0;
}
return interfaceFontGetMonospacedCharacterWidthImpl() * strlen(str);
}
// 0x442240
int interfaceFontGetLetterSpacingImpl()
{
if (!gInterfaceFontsInitialized) {
return 0;
}
return gCurrentInterfaceFontDescriptor->letterSpacing;
}
// 0x442258
int interfaceFontGetBufferSizeImpl(const char* str)
{
if (!gInterfaceFontsInitialized) {
return 0;
}
return interfaceFontGetStringWidthImpl(str) * interfaceFontGetLineHeightImpl();
}
// 0x442278
int interfaceFontGetMonospacedCharacterWidthImpl()
{
if (!gInterfaceFontsInitialized) {
return 0;
}
int v1;
if (gCurrentInterfaceFontDescriptor->wordSpacing <= gCurrentInterfaceFontDescriptor->field_8) {
v1 = gCurrentInterfaceFontDescriptor->field_6;
} else {
v1 = gCurrentInterfaceFontDescriptor->letterSpacing;
}
return v1 + gCurrentInterfaceFontDescriptor->field_0;
}
// 0x4422B4
void interfaceFontDrawImpl(unsigned char* buf, const char* string, int length, int pitch, int color)
{
if (!gInterfaceFontsInitialized) {
return;
}
if ((color & FONT_SHADOW) != 0) {
color &= ~FONT_SHADOW;
// NOTE: Other font options preserved. This is different from text font
// shadows.
interfaceFontDrawImpl(buf + pitch + 1, string, length, pitch, (color & ~0xFF) | _colorTable[0]);
}
unsigned char* palette = _getColorBlendTable(color & 0xFF);
int monospacedCharacterWidth;
if ((color & FONT_MONO) != 0) {
// NOTE: Uninline.
monospacedCharacterWidth = interfaceFontGetMonospacedCharacterWidthImpl();
}
unsigned char* ptr = buf;
while (*string != '\0') {
char ch = *string++;
int characterWidth;
if (ch == ' ') {
characterWidth = gCurrentInterfaceFontDescriptor->wordSpacing;
} else {
characterWidth = gCurrentInterfaceFontDescriptor->glyphs[ch & 0xFF].width;
}
unsigned char* end;
if ((color & FONT_MONO) != 0) {
end = ptr + monospacedCharacterWidth;
ptr += (monospacedCharacterWidth - characterWidth - gCurrentInterfaceFontDescriptor->letterSpacing) / 2;
} else {
end = ptr + characterWidth + gCurrentInterfaceFontDescriptor->letterSpacing;
}
if (end - buf > length) {
break;
}
InterfaceFontGlyph* glyph = &(gCurrentInterfaceFontDescriptor->glyphs[ch & 0xFF]);
unsigned char* glyphDataPtr = gCurrentInterfaceFontDescriptor->data + glyph->field_4;
// Skip blank pixels (difference between font's line height and glyph height).
ptr += (gCurrentInterfaceFontDescriptor->field_0 - glyph->field_2) * pitch;
for (int y = 0; y < glyph->field_2; y++) {
for (int x = 0; x < glyph->width; x++) {
unsigned char byte = *glyphDataPtr++;
*ptr++ = palette[(byte << 8) + *ptr];
}
ptr += pitch - glyph->width;
}
ptr = end;
}
if ((color & FONT_UNDERLINE) != 0) {
int length = ptr - buf;
unsigned char* underlinePtr = buf + pitch * (gCurrentInterfaceFontDescriptor->field_0 - 1);
for (int index = 0; index < length; index++) {
*underlinePtr++ = color & 0xFF;
}
}
_freeColorBlendTable(color & 0xFF);
}

49
src/font_manager.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef FONT_MANAGER_H
#define FONT_MANAGER_H
#include "text_font.h"
#include <stdbool.h>
// The maximum number of interface fonts.
#define INTERFACE_FONT_MAX (16)
typedef struct InterfaceFontGlyph {
short width;
short field_2;
int field_4;
} InterfaceFontGlyph;
typedef struct InterfaceFontDescriptor {
short field_0;
short letterSpacing;
short wordSpacing;
short field_6;
short field_8;
short field_A;
InterfaceFontGlyph glyphs[256];
unsigned char* data;
} InterfaceFontDescriptor;
extern bool gInterfaceFontsInitialized;
extern int gInterfaceFontsLength;
extern FontManager gModernFontManager;
extern InterfaceFontDescriptor gInterfaceFontDescriptors[INTERFACE_FONT_MAX];
extern int gCurrentInterfaceFont;
extern InterfaceFontDescriptor* gCurrentInterfaceFontDescriptor;
int interfaceFontsInit();
void interfaceFontsExit();
int interfaceFontLoad(int font);
void interfaceFontSetCurrentImpl(int font);
int interfaceFontGetLineHeightImpl();
int interfaceFontGetStringWidthImpl(const char* string);
int interfaceFontGetCharacterWidthImpl(int ch);
int interfaceFontGetMonospacedStringWidthImpl(const char* string);
int interfaceFontGetLetterSpacingImpl();
int interfaceFontGetBufferSizeImpl(const char* string);
int interfaceFontGetMonospacedCharacterWidthImpl();
void interfaceFontDrawImpl(unsigned char* buf, const char* string, int length, int pitch, int color);
#endif /* FONT_MANAGER_H */

1268
src/game.c Normal file

File diff suppressed because it is too large Load Diff

44
src/game.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef GAME_H
#define GAME_H
#include "game_vars.h"
#include "message.h"
#include <stdbool.h>
extern char _aGame_0[];
extern bool gGameUiDisabled;
extern int _game_state_cur;
extern bool gIsMapper;
extern int* gGameGlobalVars;
extern int gGameGlobalVarsLength;
extern const char* asc_5186C8;
extern int _game_user_wants_to_quit;
extern MessageList gMiscMessageList;
extern int _master_db_handle;
extern int _critter_db_handle;
int gameInitWithOptions(const char* windowTitle, bool isMapper, int a3, int a4, int argc, char** argv);
void gameReset();
void gameExit();
int gameHandleKey(int eventCode, bool isInCombatMode);
void gameUiDisable(int a1);
void gameUiEnable();
bool gameUiIsDisabled();
int gameGetGlobalVar(int var);
int gameSetGlobalVar(int var, int value);
int gameLoadGlobalVars();
int globalVarsRead(const char* path, const char* section, int* out_vars_num, int** out_vars);
int _game_state();
int _game_state_request(int a1);
void _game_state_update();
int gameTakeScreenshot(int width, int height, unsigned char* buffer, unsigned char* palette);
void gameFreeGlobalVars();
void showHelp();
int showQuitConfirmationDialog();
int gameDbInit();
void showSplash();
#endif /* GAME_H */

180
src/game_config.c Normal file
View File

@ -0,0 +1,180 @@
#include "game_config.h"
#include <stdio.h>
#include <string.h>
// A flag indicating if [gGameConfig] was initialized.
//
// 0x5186D0
bool gGameConfigInitialized = false;
// fallout2.cfg
//
// 0x58E950
Config gGameConfig;
// NOTE: There are additional 4 bytes following this array at 0x58EA7C, which
// probably means it's size is 264 bytes.
//
// 0x58E978
char gGameConfigFilePath[FILENAME_MAX];
// Inits main game config.
//
// [isMapper] is a flag indicating whether we're initing config for a main
// game, or a mapper. This value is `false` for the game itself.
//
// [argc] and [argv] are command line arguments. The engine assumes there is
// at least 1 element which is executable path at index 0. There is no
// additional check for [argc], so it will crash if you pass NULL, or an empty
// array into [argv].
//
// The executable path from [argv] is used resolve path to `fallout2.cfg`,
// which should be in the same folder. This function provide defaults if
// `fallout2.cfg` is not present, or cannot be read for any reason.
//
// Finally, this function merges key-value pairs from [argv] if any, see
// [configParseCommandLineArguments] for expected format.
//
// 0x444570
bool gameConfigInit(bool isMapper, int argc, char** argv)
{
if (gGameConfigInitialized) {
return false;
}
if (!configInit(&gGameConfig)) {
return false;
}
// Initialize defaults.
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, "game");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_DAT_KEY, "master.dat");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, "data");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_DAT_KEY, "critter.dat");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CRITTER_PATCHES_KEY, "data");
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, ENGLISH);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SCROLL_LOCK_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_INTERRUPT_WALK_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_ART_CACHE_SIZE_KEY, 8);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_COLOR_CYCLING_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_HASHING_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_SPLASH_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_FREE_SPACE_KEY, 20480);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_GAME_DIFFICULTY_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_DIFFICULTY_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_VIOLENCE_LEVEL_KEY, 3);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TARGET_HIGHLIGHT_KEY, 2);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_ITEM_HIGHLIGHT_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_LOOKS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_MESSAGES_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_TAUNTS_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_LANGUAGE_FILTER_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_RUNNING_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_COMBAT_SPEED_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_PLAYER_SPEED_KEY, 0);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_BASE_DELAY_KEY, 3.5);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_TEXT_LINE_DELAY_KEY, 1.399994);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_BRIGHTNESS_KEY, 1.0);
configSetDouble(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_MOUSE_SENSITIVITY_KEY, 1.0);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_INITIALIZE_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DEVICE_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_PORT_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_IRQ_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_DMA_KEY, -1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SOUNDS_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MASTER_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SNDFX_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_SPEECH_VOLUME_KEY, 22281);
configSetInt(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_CACHE_SIZE_KEY, 448);
configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH1_KEY, "sound\\music\\");
configSetString(&gGameConfig, GAME_CONFIG_SOUND_KEY, GAME_CONFIG_MUSIC_PATH2_KEY, "sound\\music\\");
configSetString(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_MODE_KEY, "environment");
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_TILE_NUM_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_SHOW_LOAD_INFO_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_DEBUG_KEY, GAME_CONFIG_OUTPUT_MAP_DATA_INFO_KEY, 0);
if (isMapper) {
configSetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, "mapper");
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_OVERRIDE_LIBRARIAN_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_LIBRARIAN_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_USE_ART_NOT_PROTOS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_REBUILD_PROTOS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_OBJECTS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_FIX_MAP_INVENTORY_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_IGNORE_REBUILD_ERRORS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SHOW_PID_NUMBERS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SAVE_TEXT_MAPS_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_RUN_MAPPER_AS_GAME_KEY, 0);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_DEFAULT_F8_AS_GAME_KEY, 1);
configSetInt(&gGameConfig, GAME_CONFIG_MAPPER_KEY, GAME_CONFIG_SORT_SCRIPT_LIST_KEY, 0);
}
// Make `fallout2.cfg` file path.
char* executable = argv[0];
char* ch = strrchr(executable, '\\');
if (ch != NULL) {
*ch = '\0';
sprintf(gGameConfigFilePath, "%s\\%s", executable, GAME_CONFIG_FILE_NAME);
*ch = '\\';
} else {
strcpy(gGameConfigFilePath, GAME_CONFIG_FILE_NAME);
}
// Read contents of `fallout2.cfg` into config. The values from the file
// will override the defaults above.
configRead(&gGameConfig, gGameConfigFilePath, false);
// Add key-values from command line, which overrides both defaults and
// whatever was loaded from `fallout2.cfg`.
configParseCommandLineArguments(&gGameConfig, argc, argv);
gGameConfigInitialized = true;
return true;
}
// Saves game config into `fallout2.cfg`.
//
// 0x444C14
bool gameConfigSave()
{
if (!gGameConfigInitialized) {
return false;
}
if (!configWrite(&gGameConfig, gGameConfigFilePath, false)) {
return false;
}
return true;
}
// Frees game config, optionally saving it.
//
// 0x444C3C
bool gameConfigExit(bool shouldSave)
{
if (!gGameConfigInitialized) {
return false;
}
bool result = true;
if (shouldSave) {
if (!configWrite(&gGameConfig, gGameConfigFilePath, false)) {
result = false;
}
}
configFree(&gGameConfig);
gGameConfigInitialized = false;
return result;
}

125
src/game_config.h Normal file
View File

@ -0,0 +1,125 @@
#ifndef GAME_CONFIG_H
#define GAME_CONFIG_H
#include "config.h"
#include <stdbool.h>
// The file name of the main config file.
#define GAME_CONFIG_FILE_NAME "fallout2.cfg"
#define GAME_CONFIG_SYSTEM_KEY "system"
#define GAME_CONFIG_PREFERENCES_KEY "preferences"
#define GAME_CONFIG_SOUND_KEY "sound"
#define GAME_CONFIG_MAPPER_KEY "mapper"
#define GAME_CONFIG_DEBUG_KEY "debug"
#define GAME_CONFIG_EXECUTABLE_KEY "executable"
#define GAME_CONFIG_MASTER_DAT_KEY "master_dat"
#define GAME_CONFIG_MASTER_PATCHES_KEY "master_patches"
#define GAME_CONFIG_CRITTER_DAT_KEY "critter_dat"
#define GAME_CONFIG_CRITTER_PATCHES_KEY "critter_patches"
#define GAME_CONFIG_LANGUAGE_KEY "language"
#define GAME_CONFIG_SCROLL_LOCK_KEY "scroll_lock"
#define GAME_CONFIG_INTERRUPT_WALK_KEY "interrupt_walk"
#define GAME_CONFIG_ART_CACHE_SIZE_KEY "art_cache_size"
#define GAME_CONFIG_COLOR_CYCLING_KEY "color_cycling"
#define GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY "cycle_speed_factor"
#define GAME_CONFIG_HASHING_KEY "hashing"
#define GAME_CONFIG_SPLASH_KEY "splash"
#define GAME_CONFIG_FREE_SPACE_KEY "free_space"
#define GAME_CONFIG_TIMES_RUN_KEY "times_run"
#define GAME_CONFIG_GAME_DIFFICULTY_KEY "game_difficulty"
#define GAME_CONFIG_RUNNING_BURNING_GUY_KEY "running_burning_guy"
#define GAME_CONFIG_COMBAT_DIFFICULTY_KEY "combat_difficulty"
#define GAME_CONFIG_VIOLENCE_LEVEL_KEY "violence_level"
#define GAME_CONFIG_TARGET_HIGHLIGHT_KEY "target_highlight"
#define GAME_CONFIG_ITEM_HIGHLIGHT_KEY "item_highlight"
#define GAME_CONFIG_COMBAT_LOOKS_KEY "combat_looks"
#define GAME_CONFIG_COMBAT_MESSAGES_KEY "combat_messages"
#define GAME_CONFIG_COMBAT_TAUNTS_KEY "combat_taunts"
#define GAME_CONFIG_LANGUAGE_FILTER_KEY "language_filter"
#define GAME_CONFIG_RUNNING_KEY "running"
#define GAME_CONFIG_SUBTITLES_KEY "subtitles"
#define GAME_CONFIG_COMBAT_SPEED_KEY "combat_speed"
#define GAME_CONFIG_PLAYER_SPEED_KEY "player_speed"
#define GAME_CONFIG_TEXT_BASE_DELAY_KEY "text_base_delay"
#define GAME_CONFIG_TEXT_LINE_DELAY_KEY "text_line_delay"
#define GAME_CONFIG_BRIGHTNESS_KEY "brightness"
#define GAME_CONFIG_MOUSE_SENSITIVITY_KEY "mouse_sensitivity"
#define GAME_CONFIG_INITIALIZE_KEY "initialize"
#define GAME_CONFIG_DEVICE_KEY "device"
#define GAME_CONFIG_PORT_KEY "port"
#define GAME_CONFIG_IRQ_KEY "irq"
#define GAME_CONFIG_DMA_KEY "dma"
#define GAME_CONFIG_SOUNDS_KEY "sounds"
#define GAME_CONFIG_MUSIC_KEY "music"
#define GAME_CONFIG_SPEECH_KEY "speech"
#define GAME_CONFIG_MASTER_VOLUME_KEY "master_volume"
#define GAME_CONFIG_MUSIC_VOLUME_KEY "music_volume"
#define GAME_CONFIG_SNDFX_VOLUME_KEY "sndfx_volume"
#define GAME_CONFIG_SPEECH_VOLUME_KEY "speech_volume"
#define GAME_CONFIG_CACHE_SIZE_KEY "cache_size"
#define GAME_CONFIG_MUSIC_PATH1_KEY "music_path1"
#define GAME_CONFIG_MUSIC_PATH2_KEY "music_path2"
#define GAME_CONFIG_DEBUG_SFXC_KEY "debug_sfxc"
#define GAME_CONFIG_MODE_KEY "mode"
#define GAME_CONFIG_SHOW_TILE_NUM_KEY "show_tile_num"
#define GAME_CONFIG_SHOW_SCRIPT_MESSAGES_KEY "show_script_messages"
#define GAME_CONFIG_SHOW_LOAD_INFO_KEY "show_load_info"
#define GAME_CONFIG_OUTPUT_MAP_DATA_INFO_KEY "output_map_data_info"
#define GAME_CONFIG_EXECUTABLE_KEY "executable"
#define GAME_CONFIG_OVERRIDE_LIBRARIAN_KEY "override_librarian"
#define GAME_CONFIG_LIBRARIAN_KEY "librarian"
#define GAME_CONFIG_USE_ART_NOT_PROTOS_KEY "use_art_not_protos"
#define GAME_CONFIG_REBUILD_PROTOS_KEY "rebuild_protos"
#define GAME_CONFIG_FIX_MAP_OBJECTS_KEY "fix_map_objects"
#define GAME_CONFIG_FIX_MAP_INVENTORY_KEY "fix_map_inventory"
#define GAME_CONFIG_IGNORE_REBUILD_ERRORS_KEY "ignore_rebuild_errors"
#define GAME_CONFIG_SHOW_PID_NUMBERS_KEY "show_pid_numbers"
#define GAME_CONFIG_SAVE_TEXT_MAPS_KEY "save_text_maps"
#define GAME_CONFIG_RUN_MAPPER_AS_GAME_KEY "run_mapper_as_game"
#define GAME_CONFIG_DEFAULT_F8_AS_GAME_KEY "default_f8_as_game"
#define GAME_CONFIG_SORT_SCRIPT_LIST_KEY "sort_script_list"
#define GAME_CONFIG_PLAYER_SPEEDUP_KEY "player_speedup"
#define ENGLISH "english"
#define FRENCH "french"
#define GERMAN "german"
#define ITALIAN "italian"
#define SPANISH "spanish"
typedef enum GameDifficulty {
GAME_DIFFICULTY_EASY,
GAME_DIFFICULTY_NORMAL,
GAME_DIFFICULTY_HARD,
} GameDifficulty;
typedef enum CombatDifficulty {
COMBAT_DIFFICULTY_EASY,
COMBAT_DIFFICULTY_NORMAL,
COMBAT_DIFFICULTY_HARD,
} CombatDifficulty;
typedef enum ViolenceLevel {
VIOLENCE_LEVEL_NONE,
VIOLENCE_LEVEL_MINIMAL,
VIOLENCE_LEVEL_NORMAL,
VIOLENCE_LEVEL_MAXIMUM_BLOOD,
} ViolenceLevel;
typedef enum TargetHighlight {
TARGET_HIGHLIGHT_OFF,
TARGET_HIGHLIGHT_ON,
TARGET_HIGHLIGHT_TARGETING_ONLY,
} TargetHighlight;
extern bool gGameConfigInitialized;
extern Config gGameConfig;
extern char gGameConfigFilePath[];
bool gameConfigInit(bool isMapper, int argc, char** argv);
bool gameConfigSave();
bool gameConfigExit(bool shouldSave);
#endif /* GAME_CONFIG_H */

4388
src/game_dialog.c Normal file

File diff suppressed because it is too large Load Diff

287
src/game_dialog.h Normal file
View File

@ -0,0 +1,287 @@
#ifndef GAME_DIALOG_H
#define GAME_DIALOG_H
#include "art.h"
#include "geometry.h"
#include "interpreter.h"
#include "lips.h"
#include "message.h"
#include "obj_types.h"
#include <stdbool.h>
#define DIALOG_REVIEW_ENTRIES_CAPACITY 80
#define DIALOG_OPTION_ENTRIES_CAPACITY 30
typedef enum GameDialogReviewWindowButton {
GAME_DIALOG_REVIEW_WINDOW_BUTTON_SCROLL_UP,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_SCROLL_DOWN,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_DONE,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_COUNT,
} GameDialogReviewWindowButton;
typedef enum GameDialogReviewWindowButtonFrm {
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_ARROW_UP_NORMAL,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_ARROW_UP_PRESSED,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_ARROW_DOWN_NORMAL,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_ARROW_DOWN_PRESSED,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_DONE_NORMAL,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_DONE_PRESSED,
GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_COUNT,
} GameDialogReviewWindowButtonFrm;
typedef enum GameDialogReaction {
GAME_DIALOG_REACTION_GOOD = 49,
GAME_DIALOG_REACTION_NEUTRAL = 50,
GAME_DIALOG_REACTION_BAD = 51,
} GameDialogReaction;
typedef struct GameDialogReviewEntry {
int replyMessageListId;
int replyMessageId;
// Can be NULL.
char* replyText;
int optionMessageListId;
int optionMessageId;
char* optionText;
} GameDialogReviewEntry;
typedef struct GameDialogOptionEntry {
int messageListId;
int messageId;
int reaction;
int proc;
int btn;
int field_14;
char text[900];
int field_39C;
} GameDialogOptionEntry;
// Provides button configuration for party member combat control and
// customization interface.
typedef struct GameDialogButtonData {
int x;
int y;
int upFrmId;
int downFrmId;
int disabledFrmId;
CacheEntry* upFrmHandle;
CacheEntry* downFrmHandle;
CacheEntry* disabledFrmHandle;
int keyCode;
int value;
} GameDialogButtonData;
typedef struct STRUCT_5189E4 {
int messageId;
int value;
} STRUCT_5189E4;
typedef enum PartyMemberCustomizationOption {
PARTY_MEMBER_CUSTOMIZATION_OPTION_AREA_ATTACK_MODE,
PARTY_MEMBER_CUSTOMIZATION_OPTION_RUN_AWAY_MODE,
PARTY_MEMBER_CUSTOMIZATION_OPTION_BEST_WEAPON,
PARTY_MEMBER_CUSTOMIZATION_OPTION_DISTANCE,
PARTY_MEMBER_CUSTOMIZATION_OPTION_ATTACK_WHO,
PARTY_MEMBER_CUSTOMIZATION_OPTION_CHEM_USE,
PARTY_MEMBER_CUSTOMIZATION_OPTION_COUNT,
} PartyMemberCustomizationOption;
extern int _Dogs[3];
extern int _dialog_state_fix;
extern int gGameDialogOptionEntriesLength;
extern int gGameDialogReviewEntriesLength;
extern unsigned char* gGameDialogDisplayBuffer;
extern int gGameDialogReplyWindow;
extern int gGameDialogOptionsWindow;
extern bool _gdialog_window_created;
extern int _boxesWereDisabled;
extern int gGameDialogFidgetFid;
extern CacheEntry* gGameDialogFidgetFrmHandle;
extern Art* gGameDialogFidgetFrm;
extern int gGameDialogBackground;
extern int _lipsFID;
extern int _lipsFID;
extern Art* _lipsFp;
extern bool gGameDialogLipSyncStarted;
extern int _dialogue_state;
extern int _dialogue_switch_mode;
extern int _gdialog_state;
extern bool _gdDialogWentOff;
extern bool _gdDialogTurnMouseOff;
extern int _gdReenterLevel;
extern bool _gdReplyTooBig;
extern Object* _peon_table_obj;
extern Object* _barterer_table_obj;
extern Object* _barterer_temp_obj;
extern int gGameDialogBarterModifier;
extern int gGameDialogBackgroundWindow;
extern int gGameDialogWindow;
extern Rect _backgrndRects[8];
extern int _talk_need_to_center;
extern bool _can_start_new_fidget;
extern int _gd_replyWin;
extern int _gd_optionsWin;
extern int gGameDialogOldMusicVolume;
extern int gGameDialogOldCenterTile;
extern int gGameDialogOldDudeTile;
extern unsigned char* _light_BlendTable;
extern unsigned char* _dark_BlendTable;
extern int _dialogue_just_started;
extern int _dialogue_seconds_since_last_input;
extern CacheEntry* gGameDialogReviewWindowButtonFrmHandles[GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_COUNT];
extern CacheEntry* _reviewBackKey;
extern CacheEntry* gGameDialogReviewWindowBackgroundFrmHandle;
extern unsigned char* gGameDialogReviewWindowBackgroundFrmData;
extern const int gGameDialogReviewWindowButtonWidths[GAME_DIALOG_REVIEW_WINDOW_BUTTON_COUNT];
extern const int gGameDialogReviewWindowButtonHeights[GAME_DIALOG_REVIEW_WINDOW_BUTTON_COUNT];
extern int gGameDialogReviewWindowButtonFrmIds[GAME_DIALOG_REVIEW_WINDOW_BUTTON_FRM_COUNT];
extern Object* gGameDialogSpeaker;
extern bool gGameDialogSpeakerIsPartyMember;
extern int gGameDialogHeadFid;
extern int gGameDialogSid;
extern int _head_phoneme_lookup[PHONEME_COUNT];
extern int _phone_anim;
extern int _loop_cnt;
extern unsigned int _tocksWaiting;
extern const char* _react_strs[3];
extern int _dialogue_subwin_len;
extern GameDialogButtonData gGameDialogDispositionButtonsData[5];
extern STRUCT_5189E4 _custom_settings[PARTY_MEMBER_CUSTOMIZATION_OPTION_COUNT][6];
extern GameDialogButtonData _custom_button_info[PARTY_MEMBER_CUSTOMIZATION_OPTION_COUNT];
extern int _totalHotx;
extern int _custom_current_selected[PARTY_MEMBER_CUSTOMIZATION_OPTION_COUNT];
extern MessageList gCustomMessageList;
extern unsigned char _light_GrayTable[256];
extern unsigned char _dark_GrayTable[256];
extern unsigned char* _backgrndBufs[8];
extern Rect _optionRect;
extern Rect _replyRect;
extern GameDialogReviewEntry gDialogReviewEntries[DIALOG_REVIEW_ENTRIES_CAPACITY];
extern int _custom_buttons_start;
extern int _control_buttons_start;
extern int gGameDialogReviewWindowOldFont;
extern CacheEntry* gGameDialogRedButtonUpFrmHandle;
extern int _gdialog_buttons[9];
extern CacheEntry* gGameDialogUpperHighlightFrmHandle;
extern CacheEntry* gGameDialogReviewButtonUpFrmHandle;
extern int gGameDialogLowerHighlightFrmHeight;
extern CacheEntry* gGameDialogReviewButtonDownFrmHandle;
extern unsigned char* gGameDialogRedButtonDownFrmData;
extern int gGameDialogLowerHighlightFrmWidth;
extern unsigned char* gGameDialogRedButtonUpFrmData;
extern int gGameDialogUpperHighlightFrmWidth;
extern Art* gGameDialogLowerHighlightFrm;
extern int gGameDialogUpperHighlightFrmHeight;
extern CacheEntry* gGameDialogRedButtonDownFrmHandle;
extern CacheEntry* gGameDialogLowerHighlightFrmHandle;
extern Art* gGameDialogUpperHighlightFrm;
extern int _oldFont;
extern unsigned int gGameDialogFidgetLastUpdateTimestamp;
extern int gGameDialogFidgetReaction;
extern Program* gDialogReplyProgram;
extern int gDialogReplyMessageListId;
extern int gDialogReplyMessageId;
extern int dword_58F4E0;
extern char gDialogReplyText[900];
extern GameDialogOptionEntry gDialogOptionEntries[DIALOG_OPTION_ENTRIES_CAPACITY];
extern int _talkOldFont;
extern unsigned int gGameDialogFidgetUpdateDelay;
extern int gGameDialogFidgetFrmCurrentFrame;
int gameDialogInit();
int _gdialogReset();
int gameDialogReset();
int gameDialogExit();
bool _gdialogActive();
void gameDialogEnter(Object* a1, int a2);
void _gdialogSystemEnter();
void gameDialogStartLips(const char* a1);
void gameDialogEndLips();
int gameDialogEnable();
int gameDialogDisable();
int _gdialogInitFromScript(int headFid, int reaction);
int _gdialogExitFromScript();
void gameDialogSetBackground(int a1);
void gameDialogRenderSupplementaryMessage(char* msg);
int _gdialogStart();
int _gdialogSayMessage();
int gameDialogAddMessageOptionWithProcIdentifier(int messageListId, int messageId, const char* a3, int reaction);
int gameDialogAddTextOptionWithProcIdentifier(int messageListId, const char* text, const char* a3, int reaction);
int gameDialogAddMessageOptionWithProc(int messageListId, int messageId, int proc, int reaction);
int gameDialogAddTextOptionWithProc(int messageListId, const char* text, int proc, int reaction);
int gameDialogSetMessageReply(Program* a1, int a2, int a3);
int gameDialogSetTextReply(Program* a1, int a2, const char* a3);
int _gdialogGo();
void _gdialogUpdatePartyStatus();
int gameDialogAddMessageOption(int a1, int a2, int a3);
int gameDialogAddTextOption(int a1, const char* a2, int a3);
int gameDialogReviewWindowInit(int* win);
int gameDialogReviewWindowFree(int* win);
int gameDialogShowReview();
void gameDialogReviewButtonOnMouseUp(int btn, int keyCode);
void gameDialogReviewWindowUpdate(int win, int origin);
void dialogReviewEntriesClear();
int gameDialogAddReviewMessage(int messageListId, int messageId);
int gameDialogAddReviewText(const char* text);
int gameDialogSetReviewOptionMessage(int messageListId, int messageId);
int gameDialogSetReviewOptionText(const char* text);
int _gdProcessInit();
void _gdProcessCleanup();
int _gdProcessExit();
void gameDialogRenderCaps();
int _gdProcess();
int _gdProcessChoice(int a1);
void gameDialogOptionOnMouseEnter(int a1);
void gameDialogOptionOnMouseExit(int a1);
void gameDialogRenderReply();
void _gdProcessUpdate();
int _gdCreateHeadWindow();
void _gdDestroyHeadWindow();
void _gdSetupFidget(int headFid, int reaction);
void gameDialogWaitForFidgetToComplete();
void _gdPlayTransition(int a1);
void _reply_arrow_up(int btn, int a2);
void _reply_arrow_down(int btn, int a2);
void _reply_arrow_restore(int btn, int a2);
void _demo_copy_title(int win);
void _demo_copy_options(int win);
void _gDialogRefreshOptionsRect(int win, Rect* drawRect);
void gameDialogTicker();
void _talk_to_critter_reacts(int a1);
void _gdialog_scroll_subwin(int a1, int a2, unsigned char* a3, unsigned char* a4, unsigned char* a5, int a6, int a7);
int _text_num_lines(const char* a1, int a2);
int gameDialogDrawText(unsigned char* buffer, Rect* rect, char* string, int* a4, int height, int pitch, int color, int a7);
void gameDialogSetBarterModifier(int modifier);
int gameDialogBarter(int modifier);
void _barter_end_to_talk_to();
int _gdialog_barter_create_win();
void _gdialog_barter_destroy_win();
void _gdialog_barter_cleanup_tables();
int partyMemberControlWindowInit();
void partyMemberControlWindowFree();
void partyMemberControlWindowUpdate();
void gameDialogCombatControlButtonOnMouseUp(int a1, int a2);
int _gdPickAIUpdateMsg(Object* obj);
int _gdCanBarter();
void partyMemberControlWindowHandleEvents();
int partyMemberCustomizationWindowInit();
void partyMemberCustomizationWindowFree();
void partyMemberCustomizationWindowHandleEvents();
void partyMemberCustomizationWindowUpdate();
void _gdCustomSelectRedraw(unsigned char* dest, int pitch, int type, int selectedIndex);
int _gdCustomSelect(int a1);
void _gdCustomUpdateSetting(int option, int value);
void gameDialogBarterButtonUpMouseUp(int btn, int a2);
int _gdialog_window_create();
void _gdialog_window_destroy();
int gameDialogWindowRenderBackground();
int _talkToRefreshDialogWindowRect(Rect* rect);
void gameDialogRenderHighlight(unsigned char* src, int srcWidth, int srcHeight, int srcPitch, unsigned char* dest, int x, int y, int destPitch, unsigned char* a9, unsigned char* a10);
void gameDialogRenderTalkingHead(Art* art, int frame);
void gameDialogPrepareHighlights();
#endif /* GAME_DIALOG_H */

34
src/game_memory.c Normal file
View File

@ -0,0 +1,34 @@
#include "game_memory.h"
#include "db.h"
#include "dictionary.h"
#include "memory.h"
#include "memory_manager.h"
// 0x44B250
int gameMemoryInit()
{
dictionarySetMemoryProcs(internal_malloc, internal_realloc, internal_free);
_db_register_mem(internal_malloc, internal_strdup, internal_free);
memoryManagerSetProcs(gameMemoryMalloc, gameMemoryRealloc, gameMemoryFree);
return 0;
}
// 0x44B294
void* gameMemoryMalloc(size_t size)
{
return internal_malloc(size);
}
// 0x44B29C
void* gameMemoryRealloc(void* ptr, size_t newSize)
{
return internal_realloc(ptr, newSize);
}
// 0x44B2A4
void gameMemoryFree(void* ptr)
{
internal_free(ptr);
}

11
src/game_memory.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef GAME_MEMORY_H
#define GAME_MEMORY_H
#include "memory_defs.h"
int gameMemoryInit();
void* gameMemoryMalloc(size_t size);
void* gameMemoryRealloc(void* ptr, size_t newSize);
void gameMemoryFree(void* ptr);
#endif /* GAME_MEMORY_H */

2369
src/game_mouse.c Normal file

File diff suppressed because it is too large Load Diff

190
src/game_mouse.h Normal file
View File

@ -0,0 +1,190 @@
#ifndef GAME_MOUSE_H
#define GAME_MOUSE_H
#include "art.h"
#include "geometry.h"
#include "obj_types.h"
#include <stdbool.h>
typedef enum ScrollableDirections {
SCROLLABLE_W = 0x01,
SCROLLABLE_E = 0x02,
SCROLLABLE_N = 0x04,
SCROLLABLE_S = 0x08,
} ScrollableDirections;
typedef enum GameMouseMode {
GAME_MOUSE_MODE_MOVE,
GAME_MOUSE_MODE_ARROW,
GAME_MOUSE_MODE_CROSSHAIR,
GAME_MOUSE_MODE_USE_CROSSHAIR,
GAME_MOUSE_MODE_USE_FIRST_AID,
GAME_MOUSE_MODE_USE_DOCTOR,
GAME_MOUSE_MODE_USE_LOCKPICK,
GAME_MOUSE_MODE_USE_STEAL,
GAME_MOUSE_MODE_USE_TRAPS,
GAME_MOUSE_MODE_USE_SCIENCE,
GAME_MOUSE_MODE_USE_REPAIR,
GAME_MOUSE_MODE_COUNT,
FIRST_GAME_MOUSE_MODE_SKILL = GAME_MOUSE_MODE_USE_FIRST_AID,
GAME_MOUSE_MODE_SKILL_COUNT = GAME_MOUSE_MODE_COUNT - FIRST_GAME_MOUSE_MODE_SKILL,
} GameMouseMode;
typedef enum GameMouseActionMenuItem {
GAME_MOUSE_ACTION_MENU_ITEM_CANCEL = 0,
GAME_MOUSE_ACTION_MENU_ITEM_DROP = 1,
GAME_MOUSE_ACTION_MENU_ITEM_INVENTORY = 2,
GAME_MOUSE_ACTION_MENU_ITEM_LOOK = 3,
GAME_MOUSE_ACTION_MENU_ITEM_ROTATE = 4,
GAME_MOUSE_ACTION_MENU_ITEM_TALK = 5,
GAME_MOUSE_ACTION_MENU_ITEM_USE = 6,
GAME_MOUSE_ACTION_MENU_ITEM_UNLOAD = 7,
GAME_MOUSE_ACTION_MENU_ITEM_USE_SKILL = 8,
GAME_MOUSE_ACTION_MENU_ITEM_PUSH = 9,
GAME_MOUSE_ACTION_MENU_ITEM_COUNT,
} GameMouseActionMenuItem;
typedef enum MouseCursorType {
MOUSE_CURSOR_NONE,
MOUSE_CURSOR_ARROW,
MOUSE_CURSOR_SMALL_ARROW_UP,
MOUSE_CURSOR_SMALL_ARROW_DOWN,
MOUSE_CURSOR_SCROLL_NW,
MOUSE_CURSOR_SCROLL_N,
MOUSE_CURSOR_SCROLL_NE,
MOUSE_CURSOR_SCROLL_E,
MOUSE_CURSOR_SCROLL_SE,
MOUSE_CURSOR_SCROLL_S,
MOUSE_CURSOR_SCROLL_SW,
MOUSE_CURSOR_SCROLL_W,
MOUSE_CURSOR_SCROLL_NW_INVALID,
MOUSE_CURSOR_SCROLL_N_INVALID,
MOUSE_CURSOR_SCROLL_NE_INVALID,
MOUSE_CURSOR_SCROLL_E_INVALID,
MOUSE_CURSOR_SCROLL_SE_INVALID,
MOUSE_CURSOR_SCROLL_S_INVALID,
MOUSE_CURSOR_SCROLL_SW_INVALID,
MOUSE_CURSOR_SCROLL_W_INVALID,
MOUSE_CURSOR_CROSSHAIR,
MOUSE_CURSOR_PLUS,
MOUSE_CURSOR_DESTROY,
MOUSE_CURSOR_USE_CROSSHAIR,
MOUSE_CURSOR_WATCH,
MOUSE_CURSOR_WAIT_PLANET,
MOUSE_CURSOR_WAIT_WATCH,
MOUSE_CURSOR_TYPE_COUNT,
FIRST_GAME_MOUSE_ANIMATED_CURSOR = MOUSE_CURSOR_WAIT_PLANET,
} MouseCursorType;
extern bool gGameMouseInitialized;
extern int _gmouse_enabled;
extern int _gmouse_mapper_mode;
extern int _gmouse_click_to_scroll;
extern int _gmouse_scrolling_enabled;
extern int gGameMouseCursor;
extern CacheEntry* gGameMouseCursorFrmHandle;
extern const int gGameMouseCursorFrmIds[MOUSE_CURSOR_TYPE_COUNT];
extern bool gGameMouseObjectsInitialized;
extern bool _gmouse_3d_hover_test;
extern unsigned int _gmouse_3d_last_move_time;
extern Art* gGameMouseActionMenuFrm;
extern CacheEntry* gGameMouseActionMenuFrmHandle;
extern int gGameMouseActionMenuFrmWidth;
extern int gGameMouseActionMenuFrmHeight;
extern int gGameMouseActionMenuFrmDataSize;
extern int _gmouse_3d_menu_frame_hot_x;
extern int _gmouse_3d_menu_frame_hot_y;
extern unsigned char* gGameMouseActionMenuFrmData;
extern Art* gGameMouseActionPickFrm;
extern CacheEntry* gGameMouseActionPickFrmHandle;
extern int gGameMouseActionPickFrmWidth;
extern int gGameMouseActionPickFrmHeight;
extern int gGameMouseActionPickFrmDataSize;
extern int _gmouse_3d_pick_frame_hot_x;
extern int _gmouse_3d_pick_frame_hot_y;
extern unsigned char* gGameMouseActionPickFrmData;
extern Art* gGameMouseActionHitFrm;
extern CacheEntry* gGameMouseActionHitFrmHandle;
extern int gGameMouseActionHitFrmWidth;
extern int gGameMouseActionHitFrmHeight;
extern int gGameMouseActionHitFrmDataSize;
extern unsigned char* gGameMouseActionHitFrmData;
extern Art* gGameMouseBouncingCursorFrm;
extern CacheEntry* gGameMouseBouncingCursorFrmHandle;
extern int gGameMouseBouncingCursorFrmWidth;
extern int gGameMouseBouncingCursorFrmHeight;
extern int gGameMouseBouncingCursorFrmDataSize;
extern unsigned char* gGameMouseBouncingCursorFrmData;
extern Art* gGameMouseHexCursorFrm;
extern CacheEntry* gGameMouseHexCursorFrmHandle;
extern int gGameMouseHexCursorFrmWidth;
extern int gGameMouseHexCursorHeight;
extern int gGameMouseHexCursorDataSize;
extern unsigned char* gGameMouseHexCursorFrmData;
extern unsigned char gGameMouseActionMenuItemsLength;
extern unsigned char* _gmouse_3d_menu_actions_start;
extern unsigned char gGameMouseActionMenuHighlightedItemIndex;
extern const short gGameMouseActionMenuItemFrmIds[GAME_MOUSE_ACTION_MENU_ITEM_COUNT];
extern int _gmouse_3d_modes_enabled;
extern int gGameMouseMode;
extern int gGameMouseModeFrmIds[GAME_MOUSE_MODE_COUNT];
extern const int gGameMouseModeSkills[GAME_MOUSE_MODE_SKILL_COUNT];
extern int gGameMouseAnimatedCursorNextFrame;
extern unsigned int gGameMouseAnimatedCursorLastUpdateTimestamp;
extern int _gmouse_bk_last_cursor;
extern bool gGameMouseItemHighlightEnabled;
extern Object* gGameMouseHighlightedItem;
extern bool _gmouse_clicked_on_edge;
extern int dword_518D9C;
extern int gGameMouseActionMenuItems[GAME_MOUSE_ACTION_MENU_ITEM_COUNT];
extern int gGameMouseLastX;
extern int gGameMouseLastY;
extern Object* gGameMouseBouncingCursor;
extern Object* gGameMouseHexCursor;
extern Object* gGameMousePointedObject;
int gameMouseInit();
int gameMouseReset();
void gameMouseExit();
void _gmouse_enable();
void _gmouse_disable(int a1);
void _gmouse_enable_scrolling();
void _gmouse_disable_scrolling();
int _gmouse_get_click_to_scroll();
int _gmouse_is_scrolling();
void gameMouseRefresh();
void _gmouse_handle_event(int mouseX, int mouseY, int mouseState);
int gameMouseSetCursor(int cursor);
int gameMouseGetCursor();
void _gmouse_3d_enable_modes();
void gameMouseSetMode(int a1);
int gameMouseGetMode();
void gameMouseCycleMode();
void _gmouse_3d_refresh();
int gameMouseSetBouncingCursorFid(int fid);
void gameMouseResetBouncingCursorFid();
void gameMouseObjectsShow();
void gameMouseObjectsHide();
bool gameMouseObjectsIsVisible();
Object* gameMouseGetObjectUnderCursor(int objectType, bool a2, int elevation);
int gameMouseRenderPrimaryAction(int x, int y, int menuItem, int width, int height);
int _gmouse_3d_pick_frame_hot(int* a1, int* a2);
int gameMouseRenderActionMenuItems(int x, int y, const int* menuItems, int menuItemsCount, int width, int height);
int gameMouseHighlightActionMenuItemAtIndex(int menuItemIndex);
int gameMouseRenderAccuracy(const char* string, int color);
int gameMouseRenderActionPoints(const char* string, int color);
void gameMouseLoadItemHighlight();
int gameMouseObjectsInit();
int gameMouseObjectsReset();
void gameMouseObjectsFree();
int gameMouseActionMenuInit();
void gameMouseActionMenuFree();
int gameMouseUpdateHexCursorFid(Rect* rect);
int _gmouse_3d_move_to(int x, int y, int elevation, Rect* a4);
int gameMouseHandleScrolling(int x, int y, int cursor);
void _gmouse_remove_item_outline(Object* object);
int objectIsDoor(Object* object);
#endif /* GAME_MOUSE_H */

339
src/game_movie.c Normal file
View File

@ -0,0 +1,339 @@
#include "game_movie.h"
#include "color.h"
#include "core.h"
#include "cycle.h"
#include "debug.h"
#include "game.h"
#include "game_config.h"
#include "game_mouse.h"
#include "game_sound.h"
#include "movie.h"
#include "movie_effect.h"
#include "palette.h"
#include "text_font.h"
#include "widget.h"
#include "window_manager.h"
#include <stdio.h>
#include <string.h>
// 0x50352A
const float flt_50352A = 0.032258064f;
// 0x518DA0
const char* gMovieFileNames[MOVIE_COUNT] = {
"iplogo.mve",
"intro.mve",
"elder.mve",
"vsuit.mve",
"afailed.mve",
"adestroy.mve",
"car.mve",
"cartucci.mve",
"timeout.mve",
"tanker.mve",
"enclave.mve",
"derrick.mve",
"artimer1.mve",
"artimer2.mve",
"artimer3.mve",
"artimer4.mve",
"credits.mve",
};
// 0x518DE4
char* gMoviePaletteFilePaths[MOVIE_COUNT] = {
NULL,
"art\\cuts\\introsub.pal",
"art\\cuts\\eldersub.pal",
NULL,
"art\\cuts\\artmrsub.pal",
NULL,
NULL,
NULL,
"art\\cuts\\artmrsub.pal",
NULL,
NULL,
NULL,
"art\\cuts\\artmrsub.pal",
"art\\cuts\\artmrsub.pal",
"art\\cuts\\artmrsub.pal",
"art\\cuts\\artmrsub.pal",
"art\\cuts\\crdtssub.pal",
};
// 0x518E28
bool gGameMovieIsPlaying = false;
// 0x518E2C
bool gGameMovieFaded = false;
// 0x596C78
unsigned char gGameMoviesSeen[MOVIE_COUNT];
// 0x596C89
char gGameMovieSubtitlesFilePath[MAX_PATH];
// gmovie_init
// 0x44E5C0
int gameMoviesInit()
{
int v1 = 0;
if (backgroundSoundIsEnabled()) {
v1 = backgroundSoundGetVolume();
}
movieSetVolume(v1);
movieSetBuildSubtitleFilePathProc(gameMovieBuildSubtitlesFilePath);
memset(gGameMoviesSeen, 0, sizeof(gGameMoviesSeen));
gGameMovieIsPlaying = false;
gGameMovieFaded = false;
return 0;
}
// 0x44E60C
void gameMoviesReset()
{
memset(gGameMoviesSeen, 0, sizeof(gGameMoviesSeen));
gGameMovieIsPlaying = false;
gGameMovieFaded = false;
}
// 0x44E638
int gameMoviesLoad(File* stream)
{
if (fileRead(gGameMoviesSeen, sizeof(*gGameMoviesSeen), MOVIE_COUNT, stream) != MOVIE_COUNT) {
return -1;
}
return 0;
}
// 0x44E664
int gameMoviesSave(File* stream)
{
if (fileWrite(gGameMoviesSeen, sizeof(*gGameMoviesSeen), MOVIE_COUNT, stream) != MOVIE_COUNT) {
return -1;
}
return 0;
}
// gmovie_play
// 0x44E690
int gameMoviePlay(int movie, int flags)
{
gGameMovieIsPlaying = true;
const char* movieFileName = gMovieFileNames[movie];
debugPrint("\nPlaying movie: %s\n", movieFileName);
char* language;
if (!configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language)) {
debugPrint("\ngmovie_play() - Error: Unable to determine language!\n");
gGameMovieIsPlaying = false;
return -1;
}
char movieFilePath[MAX_PATH];
int movieFileSize;
bool movieFound = false;
if (stricmp(language, ENGLISH) != 0) {
sprintf(movieFilePath, "art\\%s\\cuts\\%s", language, gMovieFileNames[movie]);
movieFound = dbGetFileSize(movieFilePath, &movieFileSize) == 0;
}
if (!movieFound) {
sprintf(movieFilePath, "art\\cuts\\%s", gMovieFileNames[movie]);
movieFound = dbGetFileSize(movieFilePath, &movieFileSize) == 0;
}
if (!movieFound) {
debugPrint("\ngmovie_play() - Error: Unable to open %s\n", gMovieFileNames[movie]);
gGameMovieIsPlaying = false;
return -1;
}
if ((flags & GAME_MOVIE_FADE_IN) != 0) {
paletteFadeTo(gPaletteBlack);
gGameMovieFaded = true;
}
int win = windowCreate(0, 0, 640, 480, 0, WINDOW_FLAG_0x10);
if (win == -1) {
gGameMovieIsPlaying = false;
return -1;
}
if ((flags & GAME_MOVIE_STOP_MUSIC) != 0) {
backgroundSoundDelete();
} else if ((flags & GAME_MOVIE_PAUSE_MUSIC) != 0) {
backgroundSoundPause();
}
windowRefresh(win);
bool subtitlesEnabled = false;
int v1 = 4;
configGetBool(&gGameConfig, GAME_CONFIG_PREFERENCES_KEY, GAME_CONFIG_SUBTITLES_KEY, &subtitlesEnabled);
if (subtitlesEnabled) {
char* subtitlesFilePath = gameMovieBuildSubtitlesFilePath(movieFilePath);
int subtitlesFileSize;
if (dbGetFileSize(subtitlesFilePath, &subtitlesFileSize) == 0) {
v1 = 12;
} else {
subtitlesEnabled = false;
}
}
movieSetFlags(v1);
int oldTextColor;
int oldFont;
if (subtitlesEnabled) {
char* subtitlesPaletteFilePath;
if (gMoviePaletteFilePaths[movie] != NULL) {
subtitlesPaletteFilePath = gMoviePaletteFilePaths[movie];
} else {
subtitlesPaletteFilePath = "art\\cuts\\subtitle.pal";
}
colorPaletteLoad(subtitlesPaletteFilePath);
oldTextColor = widgetGetTextColor();
widgetSetTextColor(1.0, 1.0, 1.0);
oldFont = fontGetCurrent();
widgetSetFont(101);
}
bool cursorWasHidden = cursorIsHidden();
if (cursorWasHidden) {
gameMouseSetCursor(MOUSE_CURSOR_NONE);
mouseShowCursor();
}
while (mouseGetEvent() != 0) {
_mouse_info();
}
mouseHideCursor();
colorCycleDisable();
movieEffectsLoad(movieFilePath);
_zero_vid_mem();
_movieRun(win, movieFilePath);
int v11 = 0;
int buttons;
do {
if (!_moviePlaying() || _game_user_wants_to_quit || _get_input() != -1) {
break;
}
int x;
int y;
_mouse_get_raw_state(&x, &y, &buttons);
v11 |= buttons;
} while ((v11 & 1) == 0 && (v11 & 2) == 0 || (buttons & 1) != 0 || (buttons & 2) != 0);
_movieStop();
_moviefx_stop();
_movieUpdate();
paletteSetEntries(gPaletteBlack);
gGameMoviesSeen[movie] = 1;
colorCycleEnable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
if (!cursorWasHidden) {
mouseShowCursor();
}
if (subtitlesEnabled) {
colorPaletteLoad("color.pal");
widgetSetFont(oldFont);
float r = (float)((_Color2RGB_(oldTextColor) & 0x7C00) >> 10) * flt_50352A;
float g = (float)((_Color2RGB_(oldTextColor) & 0x3E0) >> 5) * flt_50352A;
float b = (float)(_Color2RGB_(oldTextColor) & 0x1F) * flt_50352A;
widgetSetTextColor(r, g, b);
}
windowDestroy(win);
if ((flags & GAME_MOVIE_PAUSE_MUSIC) != 0) {
backgroundSoundResume();
}
if ((flags & GAME_MOVIE_FADE_OUT) != 0) {
if (!subtitlesEnabled) {
colorPaletteLoad("color.pal");
}
paletteFadeTo(_cmap);
gGameMovieFaded = false;
}
gGameMovieIsPlaying = false;
return 0;
}
// 0x44EAE4
void gameMovieFadeOut()
{
if (gGameMovieFaded) {
paletteFadeTo(_cmap);
gGameMovieFaded = false;
}
}
// 0x44EB04
bool gameMovieIsSeen(int movie)
{
return gGameMoviesSeen[movie] == 1;
}
// 0x44EB14
bool gameMovieIsPlaying()
{
return gGameMovieIsPlaying;
}
// 0x44EB1C
char* gameMovieBuildSubtitlesFilePath(char* movieFilePath)
{
char* language;
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language);
char* path = movieFilePath;
char* separator = strrchr(path, '\\');
if (separator != NULL) {
path = separator + 1;
}
sprintf(gGameMovieSubtitlesFilePath, "text\\%s\\cuts\\%s", language, path);
char* pch = strrchr(gGameMovieSubtitlesFilePath, '.');
if (*pch != '\0') {
*pch = '\0';
}
strcpy(gGameMovieSubtitlesFilePath + strlen(gGameMovieSubtitlesFilePath), ".SVE");
return gGameMovieSubtitlesFilePath;
}

59
src/game_movie.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef GAME_MOVIE_H
#define GAME_MOVIE_H
#include "db.h"
#include <stdbool.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef enum GameMovieFlags {
GAME_MOVIE_FADE_IN = 0x01,
GAME_MOVIE_FADE_OUT = 0x02,
GAME_MOVIE_STOP_MUSIC = 0x04,
GAME_MOVIE_PAUSE_MUSIC = 0x08,
} GameMovieFlags;
typedef enum GameMovie {
MOVIE_IPLOGO,
MOVIE_INTRO,
MOVIE_ELDER,
MOVIE_VSUIT,
MOVIE_AFAILED,
MOVIE_ADESTROY,
MOVIE_CAR,
MOVIE_CARTUCCI,
MOVIE_TIMEOUT,
MOVIE_TANKER,
MOVIE_ENCLAVE,
MOVIE_DERRICK,
MOVIE_ARTIMER1,
MOVIE_ARTIMER2,
MOVIE_ARTIMER3,
MOVIE_ARTIMER4,
MOVIE_CREDITS,
MOVIE_COUNT,
} GameMovie;
extern const float flt_50352A;
extern const char* gMovieFileNames[MOVIE_COUNT];
extern char* gMoviePaletteFilePaths[MOVIE_COUNT];
extern bool gGameMovieIsPlaying;
extern bool gGameMovieFaded;
extern char gGameMovieSubtitlesFilePath[MAX_PATH];
extern unsigned char gGameMoviesSeen[MOVIE_COUNT];
int gameMoviesInit();
void gameMoviesReset();
int gameMoviesLoad(File* stream);
int gameMoviesSave(File* stream);
int gameMoviePlay(int movie, int flags);
void gameMovieFadeOut();
bool gameMovieIsSeen(int movie);
bool gameMovieIsPlaying();
char* gameMovieBuildSubtitlesFilePath(char* movieFilePath);
#endif /* GAME_MOVIE_H */

25
src/game_palette.c Normal file
View File

@ -0,0 +1,25 @@
#include "game_palette.h"
#include "color.h"
// 0x44EBC0
int _HighRGB_(int a1)
{
// TODO: Some strange bit arithmetic.
int v1 = _Color2RGB_(a1);
int r = (v1 & 0x7C00) >> 10;
int g = (v1 & 0x3E0) >> 5;
int b = (v1 & 0x1F);
int result = g;
if (r > result) {
result = r;
}
result = result & 0xFF;
if (result <= b) {
result = b;
}
return result;
}

6
src/game_palette.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef GAME_PALETTE_H
#define GAME_PALETTE_H
int _HighRGB_(int a1);
#endif /* GAME_PALETTE_H */

2107
src/game_sound.c Normal file

File diff suppressed because it is too large Load Diff

166
src/game_sound.h Normal file
View File

@ -0,0 +1,166 @@
#ifndef GAME_SOUND_H
#define GAME_SOUND_H
#include "obj_types.h"
#include "sound.h"
#include <stdbool.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef enum WeaponSoundEffect {
WEAPON_SOUND_EFFECT_READY,
WEAPON_SOUND_EFFECT_ATTACK,
WEAPON_SOUND_EFFECT_OUT_OF_AMMO,
WEAPON_SOUND_EFFECT_AMMO_FLYING,
WEAPON_SOUND_EFFECT_HIT,
WEAPON_SOUND_EFFECT_COUNT,
} WeaponSoundEffect;
typedef enum SoundEffectActionType {
SOUND_EFFECT_ACTION_TYPE_ACTIVE,
SOUND_EFFECT_ACTION_TYPE_PASSIVE,
} SoundEffectActionType;
typedef enum ScenerySoundEffect {
SCENERY_SOUND_EFFECT_OPEN,
SCENERY_SOUND_EFFECT_CLOSED,
SCENERY_SOUND_EFFECT_LOCKED,
SCENERY_SOUND_EFFECT_UNLOCKED,
SCENERY_SOUND_EFFECT_USED,
SCENERY_SOUND_EFFECT_COUNT,
} ScenerySoundEffect;
typedef enum CharacterSoundEffect {
CHARACTER_SOUND_EFFECT_UNUSED,
CHARACTER_SOUND_EFFECT_KNOCKDOWN,
CHARACTER_SOUND_EFFECT_PASS_OUT,
CHARACTER_SOUND_EFFECT_DIE,
CHARACTER_SOUND_EFFECT_CONTACT,
} CharacterSoundEffect;
typedef void(SoundEndCallback)();
extern char _aSoundSfx[];
extern char _aSoundMusic_0[];
extern char _aSoundSpeech_0[];
extern bool gGameSoundInitialized;
extern bool gGameSoundDebugEnabled;
extern bool gMusicEnabled;
extern int dword_518E3;
extern int _gsound_background_fade;
extern bool gSpeechEnabled;
extern bool gSoundEffectsEnabled;
extern int _gsound_active_effect_counter;
extern Sound* gBackgroundSound;
extern Sound* gSpeechSound;
extern SoundEndCallback* gBackgroundSoundEndCallback;
extern SoundEndCallback* gSpeechEndCallback;
extern char _snd_lookup_weapon_type[WEAPON_SOUND_EFFECT_COUNT];
extern char _snd_lookup_scenery_action[SCENERY_SOUND_EFFECT_COUNT];
extern int _background_storage_requested;
extern int _background_loop_requested;
extern char* _sound_sfx_path;
extern char* _sound_music_path1;
extern char* _sound_music_path2;
extern char* _sound_speech_path;
extern int gMasterVolume;
extern int gMusicVolume;
extern int gSpeechVolume;
extern int gSoundEffectsVolume;
extern int _detectDevices;
extern int _lastTime_1;
extern char _background_fname_copied[MAX_PATH];
extern char _sfx_file_name[13];
extern char gBackgroundSoundFileName[270];
int gameSoundInit();
void gameSoundReset();
int gameSoundExit();
void soundEffectsEnable();
void soundEffectsDisable();
int soundEffectsIsEnabled();
int gameSoundSetMasterVolume(int value);
int gameSoundGetMasterVolume();
int soundEffectsSetVolume(int value);
int soundEffectsGetVolume();
void backgroundSoundDisable();
void backgroundSoundEnable();
int backgroundSoundIsEnabled();
void backgroundSoundSetVolume(int value);
int backgroundSoundGetVolume();
int _gsound_background_volume_get_set(int a1);
void backgroundSoundSetEndCallback(SoundEndCallback* callback);
int backgroundSoundGetDuration();
int backgroundSoundLoad(const char* fileName, int a2, int a3, int a4);
int _gsound_background_play_level_music(const char* a1, int a2);
void backgroundSoundDelete();
void backgroundSoundRestart(int value);
void backgroundSoundPause();
void backgroundSoundResume();
void speechDisable();
void speechEnable();
int speechIsEnabled();
void speechSetVolume(int value);
int speechGetVolume();
int _gsound_speech_volume_get_set(int volume);
void speechSetEndCallback(SoundEndCallback* callback);
int speechGetDuration();
int speechLoad(const char* fname, int a2, int a3, int a4);
int _gsound_speech_play_preloaded();
void speechDelete();
void speechPause();
void speechResume();
int _gsound_play_sfx_file_volume(const char* a1, int a2);
Sound* soundEffectLoad(const char* name, Object* a2);
Sound* soundEffectLoadWithVolume(const char* a1, Object* a2, int a3);
void soundEffectDelete(Sound* a1);
int _gsnd_anim_sound(Sound* a1);
int soundEffectPlay(Sound* a1);
int _gsound_compute_relative_volume(Object* obj);
char* sfxBuildCharName(Object* a1, int anim, int extra);
char* gameSoundBuildAmbientSoundEffectName(const char* a1);
char* gameSoundBuildInterfaceName(const char* a1);
char* sfxBuildWeaponName(int effectType, Object* weapon, int hitMode, Object* target);
char* sfxBuildSceneryName(int actionType, int action, const char* name);
char* sfxBuildOpenName(Object* a1, int a2);
void _gsound_red_butt_press(int btn, int keyCode);
void _gsound_red_butt_release(int btn, int keyCode);
void _gsound_toggle_butt_press_(int btn, int keyCode);
void _gsound_med_butt_press(int btn, int keyCode);
void _gsound_med_butt_release(int btn, int keyCode);
void _gsound_lrg_butt_press(int btn, int keyCode);
void _gsound_lrg_butt_release(int btn, int keyCode);
int soundPlayFile(const char* name);
void _gsound_bkg_proc();
int gameSoundFileOpen(const char* fname, int access, ...);
long _gsound_write_();
long gameSoundFileTellNotImplemented(int handle);
int gameSoundFileWrite(int handle, const void* buf, unsigned int size);
int gameSoundFileClose(int handle);
int gameSoundFileRead(int handle, void* buf, unsigned int size);
long gameSoundFileSeek(int handle, long offset, int origin);
long gameSoundFileTell(int handle);
long gameSoundFileGetSize(int handle);
bool gameSoundIsCompressed(char* filePath);
void speechCallback(void* userData, int a2);
void backgroundSoundCallback(void* userData, int a2);
void soundEffectCallback(void* userData, int a2);
int _gsound_background_allocate(Sound** out_s, int a2, int a3);
int gameSoundFindBackgroundSoundPathWithCopy(char* dest, const char* src);
int gameSoundFindBackgroundSoundPath(char* dest, const char* src);
int gameSoundFindSpeechSoundPath(char* dest, const char* src);
void gameSoundDeleteOldMusicFile();
int backgroundSoundPlay();
int speechPlay();
int _gsound_get_music_path(char** out_value, const char* key);
Sound* _gsound_get_sound_ready_for_effect();
bool _gsound_file_exists_f(const char* fname);
int _gsound_setup_paths();
int _gsound_sfx_q_start();
int ambientSoundEffectEventProcess(Object* a1, void* a2);
#endif /* GAME_SOUND_H */

703
src/game_vars.h Normal file
View File

@ -0,0 +1,703 @@
#ifndef GAME_VARS_H
#define GAME_VARS_H
typedef enum GameGlobalVar {
GVAR_PLAYER_REPUTATION,
GVAR_CHILDKILLER_REPUTATION,
GVAR_CHAMPION_REPUTATION,
GVAR_BERSERKER_REPUTATION,
GVAR_BAD_MONSTER,
GVAR_GOOD_MONSTER,
GVAR_PLAYER_MARRIED,
GVAR_ENEMY_ARROYO,
GVAR_KNOWLEDGE_HEALING_POWDER,
GVAR_KILL_EVIL_PLANTS,
GVAR_START_ARROYO_TRIAL,
GVAR_REPUTATION_SLAVER,
GVAR_REPUTATION_SLAVE_OWNER,
GVAR_DEN_MOM_STATUS,
GVAR_ENEMY_DEN,
GVAR_EXILE_DEN,
GVAR_DEN_ANNA_STATUS,
GVAR_DEN_WAREHOUSE_ACCESS,
GVAR_PLAYER_GOT_CAR,
GVAR_DEN_VIC_STATUS,
GVAR_DEN_MAGGIE_STILL,
GVAR_NUKA_COLA_ADDICT,
GVAR_BUFF_OUT_ADDICT,
GVAR_MENTATS_ADDICT,
GVAR_PSYCHO_ADDICT,
GVAR_RADAWAY_ADDICT,
GVAR_ALCOHOL_ADDICT,
GVAR_LOAD_MAP_INDEX,
GVAR_RUNNING_BURNING_GUY,
GVAR_VIC_DEVICE,
GVAR_SLAVE_RUN,
GVAR_SLAVES_COUNT,
GVAR_MAGGIE_STATUS,
GVAR_SLAVES_LOST,
GVAR_SLAVERS_LOST,
GVAR_PIP_BOY_ANNA_DIARY,
GVAR_FRANKIE_STATUS,
GVAR_KARMA_HOLY_WARRIOR,
GVAR_KARMA_GUARDIAN_OF_THE_WASTES,
GVAR_KARMA_SHIELD_OF_HOPE,
GVAR_KARMA_DEFENDER,
GVAR_KARMA_WANDERER,
GVAR_KARMA_BETRAYER,
GVAR_KARMA_SWORD_OF_DESPAIR,
GVAR_KARMA_SCOURGE_OF_THE_WASTE,
GVAR_KARMA_DEMON_SPAWN,
GVAR_MAP_EXIT_TILE,
GVAR_TOWN_REP_ARROYO,
GVAR_TOWN_REP_KLAMATH,
GVAR_TOWN_REP_THE_DEN,
GVAR_TOWN_REP_VAULT_CITY,
GVAR_TOWN_REP_GECKO,
GVAR_TOWN_REP_MODOC,
GVAR_TOWN_REP_SIERRA_BASE,
GVAR_TOWN_REP_BROKEN_HILLS,
GVAR_TOWN_REP_NEW_RENO,
GVAR_TOWN_REP_REDDING,
GVAR_TOWN_REP_NCR,
GVAR_TOWN_REP_BURIED_VAULT,
GVAR_TOWN_REP_VAULT_13,
GVAR_TOWN_REP_COLUSA,
GVAR_TOWN_REP_SAN_FRANCISCO,
GVAR_TOWN_REP_ENCLAVE,
GVAR_TOWN_REP_ABBEY,
GVAR_TOWN_REP_EPA,
GVAR_TOWN_REP_PRIMITIVE_TRIBE,
GVAR_TOWN_REP_RAIDERS,
GVAR_MAP_NEXT_TILE,
GVAR_ENEMY_KLAMATH,
GVAR_TORR_HARMED,
GVAR_TORR_DEAD,
GVAR_TORR_MISSING,
GVAR_TORR_SEARCH_SUCCESS,
GVAR_TRAPPER_RETURNED,
GVAR_DUNTONS_ANGRY,
GVAR_RUSTLE_FAIL_VIOLENT,
GVAR_RUSTLE_FAIL,
GVAR_RUSTLE_SUCCESS,
GVAR_TORR_GUARD_SUCCESS,
GVAR_VAULT_CITIZEN,
GVAR_VAULT_PLOW_PROBLEM,
GVAR_VAULT_CITIZENSHIP,
GVAR_VAULT_GECKO_PLANT,
GVAR_VAULT_PLANT_STATUS,
GVAR_VAULT_REDDING_PROBLEM,
GVAR_JET_QUEST,
GVAR_DAY_PASS_SHOWN,
GVAR_VAULT_CITIZEN_TEST,
GVAR_VAULT_RAIDERS,
GVAR_VAULT_DELIVER_HOLODISK,
GVAR_VAULT_FIND_THOMAS,
GVAR_QUEST_VAULT_CITIZEN,
GVAR_QUEST_PLOW_PROBLEM,
GVAR_QUEST_GECKO_PLANT,
GVAR_QUEST_REDDING_PROBLEM,
GVAR_QUEST_JET_QUEST,
GVAR_QUEST_RAIDERS,
GVAR_QUEST_DELIVER_HOLODISK,
GVAR_QUEST_FIND_THOMAS,
GVAR_MODOC_KILL_ALL_BRAHMIN_TIME,
GVAR_QUEST_VIC_DEVICE,
GVAR_QUEST_MAGGIE_STILL,
GVAR_QUEST_KILL_EVIL_PLANTS,
GVAR_QUEST_RUSTLE_CATTLE,
GVAR_BUST_SKEEVE,
GVAR_DUDE_STOMACH,
GVAR_MODOC_FAMILY_FEUD_SEED_ONE,
GVAR_MODOC_FAMILY_FEUD_SEED_TWO,
GVAR_MODOC_BRAHMIN_SEED,
GVAR_MODOC_KARL_PIP,
GVAR_MODOC_KARL_SEED,
GVAR_MODOC_VERMIN_HUNTER_SEED,
GVAR_MODOC_GHOST_FARM_SEED,
GVAR_SLAG_ATTACK,
GVAR_JONNY_STATE,
GVAR_JONNY_TILE,
GVAR_MODOC_BRAHMIN_ALIVE,
GVAR_MODOC_DOGS_ALIVE,
GVAR_MODOC_TOOL_FLAG,
GVAR_MODOC_SLAUGHTER_BESS_TIME,
GVAR_KARL_STATE,
GVAR_MODOC_BODIES,
GVAR_MODOC_SLAUGHTER_FLAG,
GVAR_MODOC_ROSE_FLAG,
GVAR_MODOC_TANNERY_FLAG,
GVAR_MODOC_POST_FLAG,
GVAR_HOSTILE_SLAVE_COUNT,
GVAR_LADDIE_STATE,
GVAR_LADDIE_TILE,
GVAR_MODOC_JONNY_HOME,
GVAR_MODOC_SPOKE_PROTECTOR,
GVAR_MODOC_MESSAGE,
GVAR_MUTATE,
GVAR_MUTATE_WHEN,
GVAR_SALVATORE_FAMILY_COUNTER,
GVAR_BISHOP_FAMILY_COUNTER,
GVAR_MORDINO_FAMILY_COUNTER,
GVAR_ENEMY_VAULT_CITY,
GVAR_VAULT_GET_LYNETTE_REWARD,
GVAR_VAULT_GET_MCCLURE_PART,
GVAR_VAULT_SERVANT,
GVAR_VAULT_VILLAGE,
GVAR_QUEST_VAULT_SERVANT,
GVAR_QUEST_VAULT_VILLAGE,
GVAR_VAULT_MONSTER_COUNT,
GVAR_SERVANT_RAID_DATE,
GVAR_ENEMY_VAULT_VILLAGE,
GVAR_BROKEN_HILLS_FRAUD,
GVAR_VAULT_BEEN_TO_RAIDERS,
GVAR_SIERRA_BASE_CONTAMINATION_TIMER,
GVAR_SIERRA_BASE_LEVEL_BREACH,
GVAR_SIERRA_BASE_ALERT,
GVAR_SIERRA_BASE_ENEMY,
GVAR_SIERRA_BASE_POWER,
GVAR_SIERRA_BASE_SECURITY,
GVAR_BRAIN_BOT_BRAIN,
GVAR_SIERRA_LOCKOUT,
GVAR_SIERRA_PASSWORD,
GVAR_GECKO_ECON_DISK,
GVAR_GECKO_REQ_FORM,
GVAR_GECKO_SKEETER_PART,
GVAR_GECKO_ANKH,
GVAR_DEN_SMITTY_PART,
GVAR_MCCLURE_KNOWN,
GVAR_HOLODISK_SIERRA_EVACUATION,
GVAR_HOLODISK_SIERRA_MED_LOG,
GVAR_HOLODISK_SIERRA_EXP_LOG,
GVAR_GECKO_SKEETER_STATUS,
GVAR_NCR_TANDI_WORK,
GVAR_NCR_TANDI_JOB_ACCEPT,
GVAR_NCR_BEAT_HOSS,
GVAR_NCR_SQUAT_DEAL,
GVAR_NCR_V15_DARION_DEAD,
GVAR_NCR_V15_DARION_DEAL,
GVAR_NEWRENO_SNUFF_WESTIN,
GVAR_NEWRENO_SNUFF_CARLSON,
GVAR_VAULT13_CLEAR,
GVAR_NCR_SPY_KNOWN,
GVAR_NCR_TANDI_WARN_CARLSON,
GVAR_RUSTLE_ACCEPT,
GVAR_RUSTLE_REFUSE,
GVAR_RUSTLE_REWARD,
GVAR_TORR_GUARD_STATUS,
GVAR_ARROYO_SPEAR,
GVAR_RUSTLE_OVER,
GVAR_NCR_BRAHMIN_PROTECT,
GVAR_NCR_DEATHCLAW_DEN,
GVAR_SLAVE_RUN_KILLED,
GVAR_DUNTON_DEAD,
GVAR_NCR_CAR_JACKED,
GVAR_NCR_MERK_WORK,
GVAR_ARROYO_DOG,
GVAR_HAVE_MUTATED,
GVAR_MUTATE_STAGE,
GVAR_PLAYER_SEX_LEVEL,
GVAR_NCR_VORTIS_QUEST_STATE,
GVAR_NCR_RANGERS_KNOWN,
GVAR_SMILEY_STATUS,
GVAR_STILL_STATUS,
GVAR_STILL_FAILURE,
GVAR_GRAVE_FLAGS_1,
GVAR_GRAVE_FLAGS_2,
GVAR_TORR_BRAHMIN_KILLED,
GVAR_ENEMY_TORR,
GVAR_ENEMY_DUNTON,
GVAR_ENEMY_SMILEY,
GVAR_NCR_SCMERK_HEREBEFORE,
GVAR_NCR_SCMERK_HOSTILE,
GVAR_NCR_SCMERK_PERSONAL_ENEMY,
GVAR_NCR_SCMERK_STATUS,
GVAR_NCR_SCMERK_SEED_STATUS,
GVAR_NCR_LENNY_MET,
GVAR_NCR_ELRON_ADJUST,
GVAR_NCR_FAKE_VAULT13_MAP,
GVAR_NCR_FAKE_VAULT13_HOLODISK,
GVAR_MILITARY_BASE_FLAGS,
GVAR_WRIGHT_FAMILY_COUNTER,
GVAR_NCR_MIRA_STATE,
GVAR_NCR_ROPE_KNOWN,
GVAR_NEW_RENO_WARNING_TIMER,
GVAR_HOLODISK_MB_OUTSIDE,
GVAR_HOLODISK_MB_LEVEL_1,
GVAR_HOLODISK_MB_LEVEL_2,
GVAR_HOLODISK_MB_LEVEL_3,
GVAR_HOLODISK_MB_LEVEL_4,
GVAR_NCR_GTEGRD_ATTACK,
GVAR_NCR_GATE_NIGHT,
GVAR_NCR_ENCLAVE_INFO,
GVAR_NCR_WESTIN_SEED,
GVAR_NCR_DOROTHY_SEED,
GVAR_NEW_RENO_MADE_MAN,
GVAR_NEW_RENO_PRIZEFIGHTER,
GVAR_NEW_RENO_PORN_STAR,
GVAR_VAULT13_FOUND_GECK,
GVAR_NCR_POWER_ON,
GVAR_SULIK_FREE,
GVAR_TORR_SEARCH_ACCEPT,
GVAR_NCR_HENRY_HYPO,
GVAR_ENEMY_GECKO,
GVAR_GECKO_COOLANT,
GVAR_NCR_POWERPLANT,
GVAR_NCR_PLAYER_RANGER,
GVAR_NCR_JACK_STATE,
GVAR_8_BALL_TOILET_SECRET,
GVAR_8_BALL_TRASH_SECRET,
GVAR_NCR_GENERIC_STATE,
GVAR_NEW_RENO_MCGEE_SEED,
GVAR_NEW_RENO_MCGEE_KNOWN,
GVAR_NEW_RENO_MCGEE_ATTACKED,
GVAR_GECKO_BRAIN_DEAD,
GVAR_GECKO_BRAIN_FRIEND,
GVAR_SALVATORE_WARNINGS,
GVAR_BISHOP_WARNINGS,
GVAR_MORDINO_WARNINGS,
GVAR_WRIGHT_WARNINGS,
GVAR_NEW_RENO_BISHOP,
GVAR_NCR_SNUFF_BISHOP,
GVAR_NEW_RENO_CARLSON_PRICE,
GVAR_NEW_RENO_WESTIN_PRICE,
GVAR_NEW_RENO_HAS_REP_PRIZEFIGHTER,
GVAR_NEW_RENO_ANGELA,
GVAR_PLACEHOLDER_002,
GVAR_NCR_ELISE_SEED,
GVAR_NEW_RENO_MRS_BISHOP,
GVAR_NCR_FELIX_SEED,
GVAR_NCR_BISHOP_PRICE,
GVAR_NCR_CATTLE_DRIVE,
GVAR_NCR_CATTLE_TIME_MIN,
GVAR_NCR_CATTLE_TIME_MAX,
GVAR_CARAVAN_STATUS,
GVAR_CARAVAN_START,
GVAR_CARAVAN_END,
GVAR_CARAVAN_DRIVERS,
GVAR_CARAVAN_GUARDS,
GVAR_CARAVAN_CARTS,
GVAR_CARAVAN_ENCOUNTERS,
GVAR_CARAVAN_BRAHMIN,
GVAR_CARAVAN_MASTERS,
GVAR_CARAVAN_DRIVERS_TOTAL,
GVAR_CARAVAN_GUARDS_TOTAL,
GVAR_CARAVAN_CARTS_TOTAL,
GVAR_CARAVAN_BRAHMIN_TOTAL,
GVAR_CARAVAN_MASTERS_TOTAL,
GVAR_CARAVAN_ENCOUNTERS_TOTAL,
GVAR_NEW_RENO_MYRON,
GVAR_NEW_RENO_WRIGHT_FLAGS,
GVAR_NEW_RENO_WRIGHT_MYSTERY,
GVAR_DEN_SLAVER_WARNINGS,
GVAR_V13_V15_DALIA_STATE,
GVAR_PARTY_CHILDKILLER,
GVAR_MODOC_STAGE_TIMER,
GVAR_MODOC_STAGE_STATE,
GVAR_REDDING_WHORE_CUT,
GVAR_V15_SEED_STATUS,
GVAR_TOWN_REP_VAULT_15,
GVAR_ADDICT_TRAGIC,
GVAR_ADDICT_JET,
GVAR_MODOC_GENERIC_FLAG_1,
GVAR_DEN_CEASAR_STATUS,
GVAR_MODOC_BRAHMIN_ESCAPED,
GVAR_BH_CHAD,
GVAR_BH_FTM,
GVAR_BH_MINE,
GVAR_BH_JAIL,
GVAR_BH_CONSPIRACY,
GVAR_BH_MISSING,
GVAR_BH_MIGHTY_MAN,
GVAR_BH_MINING,
GVAR_TOWN_REP_GHOST_FARM,
GVAR_ENEMY_BROKEN_HILLS,
GVAR_SLAG_CNT,
GVAR_NEW_RENO_SALVATORE_RESPECT,
GVAR_NEW_RENO_TRACK_LLOYD,
GVAR_NEW_RENO_GUARD_ASSIGNMENT,
GVAR_NEW_RENO_FLAG_1,
GVAR_NEW_RENO_SALVATORE,
GVAR_NEW_RENO_TRIBUTE,
GVAR_NEW_RENO_SALVATORE_PISTOL,
GVAR_NEW_RENO_ESCAPE,
GVAR_GRAVES_UNEARTHED,
GVAR_MOORE_STATE,
GVAR_MOORE_ACCEPT_DELIVERY,
GVAR_MOORE_REFUSE_DELIVERY,
GVAR_SPECIAL_ENCOUNTER_FLAGS,
GVAR_BH_BOSS,
GVAR_BH_HENCH_COUNT,
GVAR_BH_MALE_NAMES_USED,
GVAR_BH_FEMALE_NAMES_USED,
GVAR_BH_HENCH_KILLED,
GVAR_BH_CHECKED,
GVAR_BH_CARAVAN,
GVAR_BH_RANK_KILLED,
GVAR_REDDING_EXCAVATOR_CHIP,
GVAR_REDDING_JET_LEVEL,
GVAR_MAYOR_REDDING_STATUS,
GVAR_REDDING_MARGE_STATUS,
GVAR_REDDING_DAN_STATUS,
GVAR_REDDING_JOHNSON_STATUS,
GVAR_CATTLE_DRIVE_CARAVAN,
GVAR_MEDICINE_CARAVAN,
GVAR_JET_CARAVAN,
GVAR_GOLD_CARAVAN,
GVAR_REDDING_CARAVAN_STATUS,
GVAR_NEW_RENO_SAD,
GVAR_NEW_RENO_WRIGHT_STILL,
GVAR_NEW_RENO_FLAG_2,
GVAR_NEW_RENO_WRIGHT_STILL_MISSION,
GVAR_NEW_RENO_JULES_KITTY,
GVAR_NEW_RENO_STOLEN_CAR,
GVAR_NEW_RENO_JULES_ELDRIDGE,
GVAR_GRUTHAR_DSTATUS,
GVAR_WHIRLY,
GVAR_MYSTERIOUS_STRANGER,
GVAR_MYSTERIOUS_STRANGER_LEVEL,
GVAR_NEW_RENO_DELIVERY,
GVAR_NEW_RENO_EXTORTION_BROS,
GVAR_NEW_RENO_ASSASSINATION,
GVAR_NEW_RENO_LIL_JESUS_REFERS,
GVAR_SEX_COUNTER,
GVAR_RND_SALES_NAME,
GVAR_RND_SALES_ENCOUNTER,
GVAR_SAN_FRAN_FLAGS,
GVAR_SAN_FRAN_SUB,
GVAR_SAN_FRAN_TANKER,
GVAR_SAN_FRAN_SHIHACKED,
GVAR_SAN_FRAN_BADGER,
GVAR_SAN_FRAN_ELRON,
GVAR_SAN_FRAN_SPLEEN,
GVAR_KNOW_DOC_HOLIDAY,
GVAR_DUMAR_STATUS,
GVAR_NEW_RENO_JET_SOURCE,
GVAR_DEN_BECKY_JOB,
GVAR_HOLY_GRENADE,
GVAR_RAIDERS_FLAGS,
GVAR_DEN_FRED_STATUS,
GVAR_DEN_DEREK_STATUS,
GVAR_DEN_ROBBY_STATUS,
GVAR_RAIDERS_COUNT,
GVAR_DEN_HEATHER_STATUS,
GVAR_SUPER_CAR,
GVAR_BAR_BRAWL,
GVAR_WADE_STATUS,
GVAR_STANWELL_STATUS,
GVAR_SAVINELLI_STATUS,
GVAR_IMPLANTS_KNOWN,
GVAR_FROG_MORTON,
GVAR_REDDING_MORTON_BROTHERS,
GVAR_REDDING_SHERIFF,
GVAR_MODOC_ENDINGS,
GVAR_WANAMINGO_OCCUPADO,
GVAR_QUEST_RAT_GOD,
GVAR_QUEST_RESCUE_TORR,
GVAR_VAULT_JET_SOURCE,
GVAR_QUEST_SUPER_REPAIR_KIT,
GVAR_QUEST_PLASMA_TRANSFORMER,
GVAR_GECKO_MELTDOWN,
GVAR_QUEST_REPAIR_POWER_PLANT,
GVAR_QUEST_OPTIMIZE_POWER_PLANT,
GVAR_PARTY_NO_FOLLOW,
GVAR_RND_KAGA_STATE,
GVAR_VAULT_CITY_VENT,
GVAR_VAULT_PIP,
GVAR_MODOC_GENERIC_FLAG_2,
GVAR_SLAUGHTER_SLAG_TIME,
GVAR_PIPBOY_TOUR_GUIDE,
GVAR_PIPBOY_CREDITS,
GVAR_NCR_GRANT_HOSTILE,
GVAR_NCR_WFIELD_NOTIFY,
GVAR_ENDGAME_MOVIE_ARROYO,
GVAR_ENDGAME_MOVIE_MODOC,
GVAR_ENDGAME_MOVIE_DEN,
GVAR_ENDGAME_MOVIE_VAULT_CITY,
GVAR_ENDGAME_MOVIE_RENO,
GVAR_ENDGAME_MOVIE_RENO_ADD1,
GVAR_ENDGAME_MOVIE_RENO_ADD2,
GVAR_ENDGAME_MOVIE_RENO_ADD3,
GVAR_ENDGAME_MOVIE_RENO_ADD4,
GVAR_ENDGAME_MOVIE_GECKO,
GVAR_ENDGAME_MOVIE_REDDING,
GVAR_ENDGAME_MOVIE_BROKEN_HILLS,
GVAR_ENDGAME_MOVIE_NCR,
GVAR_ENDGAME_MOVIE_VAULT_15,
GVAR_ENDGAME_MOVIE_VAULT_13,
GVAR_ENDGAME_MOVIE_SAN_FRAN_SHI,
GVAR_ENDGAME_MOVIE_SAN_FRAN_ELRON,
GVAR_ENDGAME_MOVIE_SAN_FRAN_PUNKS,
GVAR_SAN_FRAN_STRUGGLE,
GVAR_SAN_FRAN_ELRON_WHIRLY,
GVAR_DR_TROY_STATUS,
GVAR_V13_STATUS_FLAGS,
GVAR_GECKO_TIMER_MELTDOWN,
GVAR_ENCLAVE_POWER_PLANT,
GVAR_ENCLAVE_GRANITE_JOINED,
GVAR_ENCLAVE_ALARM,
GVAR_ENCLAVE_TIMER,
GVAR_ENCLAVE_REACTOR,
GVAR_VAULT_LYNETTE_STATUS,
GVAR_DOC_JOHNSON_STATUS,
GVAR_NCR_GEN_FLAGS,
GVAR_CAR_BLOWER,
GVAR_ENCLAVE_COMPUTER,
GVAR_ENCLAVE_MARTIN,
GVAR_ENCLAVE_ELDER,
GVAR_JAIL_BREAK,
GVAR_SAN_FRAN_ARMOR,
GVAR_DEN_FLAG_1,
GVAR_DEN_FLAG_2,
GVAR_DEN_FLAG_3,
GVAR_SAN_FRAN_SPLEEN_TIME,
GVAR_PLAYER_WAS_MARRIED,
GVAR_DEN_SMITTY_DELIVER,
GVAR_SMITTY_DELIVER_TIME,
GVAR_DEN_VIC_KNOWN,
GVAR_CAR_UPGRADE_FUEL_CELL_REGULATOR,
GVAR_DEN_GANGWAR,
GVAR_NEW_RENO_CAR_UPGRADE,
GVAR_NEW_RENO_SUPER_CAR,
GVAR_DEN_SEE_VIC,
GVAR_STILL_START,
GVAR_QUEST_JOSHUA,
GVAR_ARDIN_FREEDOM,
GVAR_TOTAL_WANAMINGOS,
GVAR_SAN_FRAN_DAVE,
GVAR_VC_MET_ED,
GVAR_FRED_MONEY,
GVAR_CAN_ASK_ARDIN_ABOUT_SMILEY,
GVAR_VAULT_USED_TEACHING_SYSTEM,
GVAR_DEN_GANG_1_COUNT,
GVAR_DEN_GANG_2_COUNT,
GVAR_DEN_GANG_D_DAY,
GVAR_DEN_METZGER_GANG_KILL_TIMER,
GVAR_DEN_GANG_TRAP,
GVAR_DEN_GANG_DOOR,
GVAR_V15_CRISSY_QUEST,
GVAR_V15_KILL_DARION,
GVAR_V15_NCR_DEAL,
GVAR_V15_NCR_SPY,
GVAR_SAN_FRAN_EG_NOTIFY,
GVAR_SAN_FRAN_EG_A_OBJ,
GVAR_ELRON_GUARDS,
GVAR_ARROYO_RETURN_GECK,
GVAR_NCR_BRAHMN_QST,
GVAR_NCR_DRPAPR_QST,
GVAR_NCR_ELMBISHOP_QST,
GVAR_NCR_LYNETTE_HOLO_QST,
GVAR_NCR_ENLONE_LETTER_QST,
GVAR_NCR_KILL_ELRON_QST,
GVAR_V13_COMP_QST,
GVAR_V13_GORIS_QST,
GVAR_GIMP_FLAG,
GVAR_GECKO_ASSIGNED,
GVAR_MODOC_SHITTY_DEATH,
GVAR_ENEMY_REDDING,
GVAR_VAL_TOOLS,
GVAR_FALLOUT_2,
GVAR_NEW_RENO_FLAG_3,
GVAR_MR_BISHOP_SAFE,
GVAR_VAULT_BOOZE_SMUGGLING,
GVAR_ENCLAVE_COUNTDOWN,
GVAR_ENCLAVE_FRANK_DEAD,
GVAR_NCR_BRAHMIN_QST,
GVAR_NEW_RENO_KITTY_MAGAZINES,
GVAR_NCR_FREE_SLAVES_QST,
GVAR_NEW_RENO_STUART_DEAL,
GVAR_NEW_RENO_FIGHT_LEVEL,
GVAR_ENEMY_VAULT_COURTYARD,
GVAR_NEW_RENO_ROUND_NUMBER,
GVAR_NEW_RENO_ROUND_TIME,
GVAR_NEW_RENO_DUDE_SCORE,
GVAR_NEW_RENO_BOXER_SCORE,
GVAR_NEW_RENO_FIGHT_STATUS,
GVAR_NAVARRO_BASE_ALERT,
GVAR_NAVARRO_FOB,
GVAR_NAVARRO_K9,
GVAR_NAVARRO_POWER_CENTER,
GVAR_NAVARRO_VERTIBIRDS,
GVAR_STANWELL_PAYOUT,
GVAR_WADE_PAYOUT,
GVAR_SAVINE_PAYOUT,
GVAR_SAN_FRAN_SHI_WHIRLY,
GVAR_SIERRA_GNN_HOLODISK,
GVAR_SIERRA_MISSION_HOLODISK,
GVAR_ELRON_HOLODISK,
GVAR_NEW_RENO_KILL_DADDY_WEAPON,
GVAR_READ_FRANCIS_NOTE,
GVAR_ENEMY_CONSPIRATORS,
GVAR_MARCUS_DEAD,
GVAR_RAIDER_SECRET_ENTRANCE_KNOWN,
GVAR_COMING_FROM_INSIDE_RAIDERS,
GVAR_VAULT_STARK_RECON,
GVAR_NEW_RENO_MRS_BISHOP_COMBINATION,
GVAR_TALKED_TO_ELDER,
GVAR_SAN_FRAN_FUEL_TANK_QST,
GVAR_SAN_FRAN_NAV_TANK_QST,
GVAR_SAN_FRAN_FOB_TANK_QST,
GVAR_SAN_FRAN_ELRON_GAS_QST,
GVAR_SAN_FRAN_BADGER_GFRIEND_QST,
GVAR_SAN_FRAN_LOPAN_KDRAGON_QST,
GVAR_SAN_FRAN_DRAGON_KLOPAN_QST,
GVAR_SAN_FRAN_ARMOR_QST,
GVAR_FINISHED_STARK_RECON,
GVAR_VAULT_CITY_DESIGNER_NOTES,
GVAR_BH_POWER,
GVAR_NEW_RENO_SUSPECT_JJJ,
GVAR_NEW_RENO_SUSPECT_JULES,
GVAR_NEW_RENO_SUSPECT_LIL_JESUS,
GVAR_NEW_RENO_SUSPECT_RENESCO,
GVAR_NEW_RENO_WESTIN_SNUFF_PIP,
GVAR_NEW_RENO_CARLSON_SNUFF_PIP,
GVAR_NEW_RENO_ELDRIDGE_PISTOL_QUEST,
GVAR_DEN_CAR_PART_PIP,
GVAR_DEN_ANNA_LOCKET_PIP,
GVAR_NEW_RENO_POISON_STILL_TIME,
GVAR_SAN_FRAN_WONG_EAT_TIME,
GVAR_NAVARRO_XARN,
GVAR_SAN_FRAN_KILL_OZ9_QST,
GVAR_NEW_RENO_ETHYL_MEETING_TIME,
GVAR_SAN_FRAN_VERTI_STEAL_SHI_QST,
GVAR_SAN_FRAN_VERTI_STEAL_ELE_QST,
GVAR_SAN_FRAN_KILL_EMP_QST,
GVAR_SAN_FRAN_VERTI_SHI_QST,
GVAR_SAN_FRAN_VERTI_ELE_QST,
GVAR_BROKEN_HILLS_CARAVAN_POOCH_SCREW,
GVAR_CHAD_DEAD,
GVAR_SAN_FRAN_JASHUA_STATUS,
GVAR_SAN_FRAN_BOS_QUEST,
GVAR_NCR_GUARDS_CHECK_OBJ,
GVAR_ENEMY_BANK_GUARDS,
GVAR_ENCLAVE_TURRET_GUARD,
GVAR_ENCLAVE_TURRET_DETENTION,
GVAR_ENCLAVE_TURRET_SCIENCE,
GVAR_ENCLAVE_TURRET_PRESIDENT,
GVAR_ENCLAVE_TURRET_MAIN,
GVAR_HOLODISK_ENCLAVE_SECURITY,
GVAR_HOLODISK_ENCLAVE_STATE,
GVAR_HOLODISK_ENCLAVE_WORD,
GVAR_HOLODISK_ENCLAVE_CHEMICAL,
GVAR_HOLODISK_ENCLAVE_ATOMIC,
GVAR_ENCLAVE_TURRET_HELP_PLAYER,
GVAR_NEW_RENO_GUARD_MESSAGE_TIMER,
GVAR_MORTON_GANG,
GVAR_GECKO_WORKING_ON_PLANT,
GVAR_VIGNETTE_SEQUENCE,
GVAR_PLANT_SCHEDULED_FOR_CHANGE,
GVAR_DROP_PLAYER_BY_VAULT_8,
GVAR_ENCLAVE_COM_LINE,
GVAR_LEFT_CAR_AT_RAIDERS,
GVAR_RAIDERS_CAR_ELEVATION,
GVAR_SEXPERT,
GVAR_GIGALO,
GVAR_DUDE_VIRGIN,
GVAR_MADE_MAN_SALVATORE,
GVAR_MADE_MAN_BISHOP,
GVAR_MADE_MAN_MORDINO,
GVAR_MADE_MAN_WRIGHT,
GVAR_NCR_SPY_HOLO_DOWNLOAD,
GVAR_NCR_HISTORY_HOLO_DOWNLOAD,
GVAR_NCR_WESTIN_HOLO_DOWNLOAD,
GVAR_TYPHON_QUEST_STATUS,
GVAR_8_BALL_VAULT_TERMINAL,
GVAR_RAIDERS_DEAD,
GVAR_KLAMATH_GENERATOR,
GVAR_ENTERED_GUARDIAN,
GVAR_BATH_HOUSE_REJECT,
GVAR_SKYNET,
GVAR_SPECIAL_ENCOUNTER_BRIDGE,
GVAR_SPECIAL_ENCOUNTER_HOLY2,
GVAR_SPECIAL_ENCOUNTER_TOXIC,
GVAR_SPECIAL_ENCOUNTER_PARIAH,
GVAR_SPECIAL_ENCOUNTER_BRAHMIN,
GVAR_SPECIAL_ENCOUNTER_WHALE,
GVAR_SPECIAL_ENCOUNTER_HEAD,
GVAR_SPECIAL_ENCOUNTER_SHUTTLE,
GVAR_SPECIAL_ENCOUNTER_GUARDIAN,
GVAR_SPECIAL_ENCOUNTER_HOLY1,
GVAR_SPECIAL_ENCOUNTER_WOODSMAN,
GVAR_GECKO_FIND_WOODY,
GVAR_SPECIAL_ENCOUNTER_CAFE,
GVAR_GECKO_DESCENDANT_KNOWN,
GVAR_FIND_VIC,
GVAR_SPECIAL_ENCOUNTER_UNWASHED,
GVAR_KLAMATH_SCORPIONS_KILLED,
GVAR_KLAMATH_SCORPIONS_TOTAL,
GVAR_ENCLAVE_ENEMY_GUARD,
GVAR_ENCLAVE_ENEMY_PRESIDENT,
GVAR_ENCLAVE_ENEMY_TRAPS,
GVAR_ENCLAVE_ENEMY_REACTOR,
GVAR_ENCLAVE_ENEMY_DETENTION,
GVAR_TOWN_REP_NAVARRO,
GVAR_GAVE_GECK_EXP,
GVAR_DUDE_START_SEQ_1,
GVAR_MODOC_GHOST_SEED_PIP,
GVAR_PARTY_MEMBERS_HIDDEN,
GVAR_CAR_PLACED_TILE,
GVAR_RESERVED_VAR1,
GVAR_RESERVED_VAR2,
GVAR_RESERVED_VAR3,
GVAR_RESERVED_VAR4,
GVAR_RESERVED_VAR5,
GVAR_RESERVED_VAR6,
GVAR_RESERVED_VAR7,
GVAR_RESERVED_VAR8,
GVAR_RESERVED_VAR9,
GVAR_RESERVED_VAR10,
GVAR_RESERVED_VAR11,
GVAR_RESERVED_VAR12,
GVAR_RESERVED_VAR13,
GVAR_RESERVED_VAR14,
GVAR_RESERVED_VAR15,
GVAR_RESERVED_VAR16,
GVAR_RESERVED_VAR17,
GVAR_RESERVED_VAR18,
GVAR_RESERVED_VAR19,
GVAR_RESERVED_VAR20,
GVAR_RESERVED_VAR21,
GVAR_RESERVED_VAR22,
GVAR_RESERVED_VAR23,
GVAR_RESERVED_VAR24,
GVAR_RESERVED_VAR25,
GVAR_RESERVED_VAR26,
GVAR_RESERVED_VAR27,
GVAR_RESERVED_VAR28,
GVAR_RESERVED_VAR29,
GVAR_RESERVED_VAR30,
GVAR_RESERVED_VAR31,
GVAR_RESERVED_VAR32,
GVAR_RESERVED_VAR33,
GVAR_RESERVED_VAR34,
GVAR_RESERVED_VAR35,
GVAR_RESERVED_VAR36,
GVAR_RESERVED_VAR37,
GVAR_RESERVED_VAR38,
GVAR_RESERVED_VAR39,
GVAR_RESERVED_VAR40,
GVAR_RESERVED_VAR41,
GVAR_RESERVED_VAR42,
GVAR_RESERVED_VAR43,
GVAR_RESERVED_VAR44,
GVAR_RESERVED_VAR45,
GVAR_RESERVED_VAR46,
GVAR_RESERVED_VAR47,
GVAR_RESERVED_VAR48,
GVAR_RESERVED_VAR49,
GVAR_RESERVED_VAR50,
GVAR_RESERVED_VAR51,
GVAR_RESERVED_VAR52,
GVAR_RESERVED_VAR53,
GVAR_RESERVED_VAR54,
GVAR_RESERVED_VAR55,
GVAR_RESERVED_VAR56,
GVAR_RESERVED_VAR57,
GVAR_RESERVED_VAR58,
GVAR_RESERVED_VAR59,
GVAR_MODOC_JONNY_PIP,
GVAR_NEW_RENO_FLAG_4,
GVAR_PATCH_INVAIDITATOR,
} GameGlobalVar;
#endif /* GAME_VARS_H */

183
src/geometry.c Normal file
View File

@ -0,0 +1,183 @@
#include "geometry.h"
#include "memory.h"
#include <stdlib.h>
// 0x51DEF4
RectListNode* _rectList = NULL;
// 0x4C6900
void _GNW_rect_exit()
{
while (_rectList != NULL) {
RectListNode* next = _rectList->next;
internal_free(_rectList);
_rectList = next;
}
}
// 0x4C6924
void _rect_clip_list(RectListNode** rectListNodePtr, Rect* rect)
{
Rect v1;
rectCopy(&v1, rect);
// NOTE: Original code is slightly different.
while (*rectListNodePtr != NULL) {
RectListNode* rectListNode = *rectListNodePtr;
if (v1.right >= rectListNode->rect.left
&& v1.bottom >= rectListNode->rect.top
&& v1.left <= rectListNode->rect.right
&& v1.top <= rectListNode->rect.bottom) {
Rect v2;
rectCopy(&v2, &(rectListNode->rect));
*rectListNodePtr = rectListNode->next;
rectListNode->next = _rectList;
_rectList = rectListNode;
if (v2.top < v1.top) {
RectListNode* newRectListNode = _rect_malloc();
if (newRectListNode == NULL) {
return;
}
rectCopy(&(newRectListNode->rect), &v2);
newRectListNode->rect.bottom = v1.top - 1;
newRectListNode->next = *rectListNodePtr;
*rectListNodePtr = newRectListNode;
rectListNodePtr = &(newRectListNode->next);
v2.top = v1.top;
}
if (v2.bottom > v1.bottom) {
RectListNode* newRectListNode = _rect_malloc();
if (newRectListNode == NULL) {
return;
}
rectCopy(&(newRectListNode->rect), &v2);
newRectListNode->rect.top = v1.bottom + 1;
newRectListNode->next = *rectListNodePtr;
*rectListNodePtr = newRectListNode;
rectListNodePtr = &(newRectListNode->next);
v2.bottom = v1.bottom;
}
if (v2.left < v1.left) {
RectListNode* newRectListNode = _rect_malloc();
if (newRectListNode == NULL) {
return;
}
rectCopy(&(newRectListNode->rect), &v2);
newRectListNode->rect.right = v1.left - 1;
newRectListNode->next = *rectListNodePtr;
*rectListNodePtr = newRectListNode;
rectListNodePtr = &(newRectListNode->next);
}
if (v2.right > v1.right) {
RectListNode* newRectListNode = _rect_malloc();
if (newRectListNode == NULL) {
return;
}
rectCopy(&(newRectListNode->rect), &v2);
newRectListNode->rect.left = v1.right + 1;
newRectListNode->next = *rectListNodePtr;
*rectListNodePtr = newRectListNode;
rectListNodePtr = &(newRectListNode->next);
}
} else {
rectListNodePtr = &(rectListNode->next);
}
}
}
// 0x4C6BB8
RectListNode* _rect_malloc()
{
if (_rectList == NULL) {
for (int index = 0; index < 10; index++) {
RectListNode* rectListNode = internal_malloc(sizeof(*rectListNode));
if (rectListNode == NULL) {
break;
}
// NOTE: Uninline.
_rect_free(rectListNode);
}
}
if (_rectList == NULL) {
return NULL;
}
RectListNode* rectListNode = _rectList;
_rectList = _rectList->next;
return rectListNode;
}
// 0x4C6C04
void _rect_free(RectListNode* rectListNode)
{
rectListNode->next = _rectList;
_rectList = rectListNode;
}
// Calculates a union of two source rectangles and places it into result
// rectangle.
//
// 0x4C6C18
void rectUnion(const Rect* s1, const Rect* s2, Rect* r)
{
r->left = min(s1->left, s2->left);
r->top = min(s1->top, s2->top);
r->right = max(s1->right, s2->right);
r->bottom = max(s1->bottom, s2->bottom);
}
// Calculates intersection of two source rectangles and places it into third
// rectangle and returns 0. If two source rectangles do not have intersection
// it returns -1 and resulting rectangle is a copy of s1.
//
// 0x4C6C68
int rectIntersection(const Rect* s1, const Rect* s2, Rect* r)
{
r->left = s1->left;
r->top = s1->top;
r->right = s1->right;
r->bottom = s1->bottom;
if (s1->left <= s2->right && s2->left <= s1->right && s2->bottom >= s1->top && s2->top <= s1->bottom) {
if (s2->left > s1->left) {
r->left = s2->left;
}
if (s2->right < s1->right) {
r->right = s2->right;
}
if (s2->top > s1->top) {
r->top = s2->top;
}
if (s2->bottom < s1->bottom) {
r->bottom = s2->bottom;
}
return 0;
}
return -1;
}

Some files were not shown because too many files have changed in this diff Show More