348 Commits

Author SHA1 Message Date
Alexander Batalov
dcd450a9b7 Bump version to 1.2.0 2023-01-20 10:02:02 +03:00
Alexander Batalov
bfbf692cc0 Add iOS release workflow 2023-01-20 09:57:52 +03:00
Alexander Batalov
2714dc1ea1 Fix memory alignment
See #135, #122
2023-01-19 20:27:22 +03:00
Alexander Batalov
8604d9c401 Fix check 2023-01-16 17:33:19 +03:00
Alexander Batalov
b9261c3da2 Fix directory name reading 2023-01-16 17:01:52 +03:00
Alexander Batalov
ed7176b796 Fix combatai_notify_onlookers 2023-01-16 16:55:31 +03:00
Alexander Batalov
6cac53d20b Rename critter maneuver flags 2023-01-16 16:49:50 +03:00
Alexander Batalov
ad3860790c Fix NPCs not joining combat 2023-01-16 16:42:50 +03:00
Alexander Batalov
bffe81d0b3 Improve cycle.cc readability 2023-01-05 11:29:00 +03:00
Alexander Batalov
6ab08bd22b Remove some unused db functions 2023-01-05 10:39:46 +03:00
Alexander Batalov
060c79fc20 Remove electronic registration 2023-01-05 10:27:16 +03:00
Alexander Batalov
ac64fde502 Fix object rendering
Previous solution did not work well on high resolutions due to
incorrect tile calculations - was not updating edges and sometimes
hanged in endless loop trying to find upper-left or bottom-right
tiles.

New solution follows Sfall's HRP implementation.
2023-01-05 05:59:45 +03:00
Alexander Batalov
7496afa4f8 Clarify textObjectAdd param 2023-01-05 04:06:18 +03:00
Alexander Batalov
66955f893a Review light.cc 2023-01-03 23:56:52 +03:00
Alexander Batalov
9ee4cb4a26 Refactor min/max usage 2023-01-03 23:00:38 +03:00
Alexander Batalov
a5cefd6c8b Clear dirty rect during map updates
Previous solution to replace squares was destructive in nature and
could possibly lead to unexpected things (especially with mods where
tile 0x293 might not be opaque black).

New solution follows mapper refresh routines where entire dirty rect
is reset to black as a preparation step.
2023-01-03 21:10:20 +03:00
Alexander Batalov
d040ea814a Fix objects being animated while playing movies 2023-01-02 21:15:43 +03:00
Alexander Batalov
03145e4fcd Fix endgame slideshow issues
See #3
Closes #215
2023-01-02 14:34:42 +03:00
Alexander Batalov
812079004c Make credits fullscreen
See #3, #215
2023-01-02 13:09:03 +03:00
Alexander Batalov
6224af6178 Fix help screen issues
Closes #179
2023-01-02 11:13:32 +03:00
Alexander Batalov
a8d3cdd148 Improve worldmap.cc readability 2022-12-31 14:12:04 +03:00
Alexander Batalov
95cc863fde Fix special encounter icon blinking 2022-12-30 16:45:49 +03:00
Alexander Batalov
cc1562b056 Add fading effects when moving to/from worldmap
Follow-up to #193
2022-12-30 00:41:29 +03:00
Alexander Batalov
07f3c82444 Fix map loading background
Integrated interface bar area remained visible during map loading.
2022-12-29 23:33:09 +03:00
Alexander Batalov
9976728e04 Fix worldmap background
Closes #193
2022-12-29 21:54:40 +03:00
Alexander Batalov
5922d15b1c Fix various dialog box issues
- Better vertical positioning (account for word wrapping)
- Better horizontal positioning (consider both right and left gaps for
calculating available width).
- Prevent vertical text overflow.
2022-12-29 13:02:59 +03:00
Alexander Batalov
248d6dfb92 Add shadow to city labels 2022-12-29 10:43:02 +03:00
Alexander Batalov
73cc93a3e4 Cleanup some interface bar functions 2022-12-29 10:21:15 +03:00
Alexander Batalov
34259269a4 Improve "Whomever is attacking me" targeting
Fixes #197
2022-12-29 09:37:51 +03:00
Alexander Batalov
11217974c6 Improve aiFindAttackers 2022-12-28 19:45:17 +03:00
Alexander Batalov
0e13989f9c Fix critter lists sorting functions 2022-12-28 17:54:09 +03:00
Alexander Batalov
57dcf42b5c Fix custom disposition button 2022-12-28 17:33:01 +03:00
Alexander Batalov
c2c26db5d9 Fix max throw range calculation 2022-12-28 12:33:10 +03:00
Alexander Batalov
b6ee5fcbe9 Fix configGetIntList 2022-12-28 09:14:30 +03:00
Alexander Batalov
0e11569397 Add ai_check_drugs fixes and improvements (#29) 2022-12-28 02:19:43 +03:00
Alexander Batalov
4b137dac5f Improve graphlib accuracy 2022-12-27 18:59:24 +03:00
Alexander Batalov
1f6d5ebbc9 Improve some color functions readability 2022-12-27 16:27:14 +03:00
Alexander Batalov
462baa0a04 Improve color tables accuracy 2022-12-27 15:51:43 +03:00
Alexander Batalov
b74f3c368b Fix warning 2022-12-26 12:03:14 +03:00
Alexander Batalov
a05ab09e1b Cleanup audio IO 2022-12-26 11:48:47 +03:00
Alexander Batalov
1b0cd6d757 Improve SoundDecoder readability 2022-12-25 22:30:31 +03:00
Alexander Batalov
b8dea116ee Improve Sound readability 2022-12-25 21:56:23 +03:00
Alexander Batalov
e11f1af9ae Improve combat_ai.cc readability 2022-12-24 14:30:03 +03:00
Alexander Batalov
9c34b26fec Improve animation.cc readability 2022-12-23 15:49:56 +03:00
Alexander Batalov
9a6d45541f Rename animationRegisterPing 2022-12-23 15:22:53 +03:00
Alexander Batalov
f4775775b8 Review dude move/run functions 2022-12-23 15:13:05 +03:00
Alexander Batalov
a48d744cde Improve actions.cc readability 2022-12-23 12:44:52 +03:00
Alexander Batalov
16dbe1122e Normalize gradlew.bat 2022-12-23 10:55:15 +03:00
Alexander Batalov
b0cb70ffa6 Fix file existence check 2022-12-23 10:34:20 +03:00
Alexander Batalov
512530c653 Fix building file list on non-Windows platforms 2022-12-23 10:33:16 +03:00
Alexander Batalov
982d4cc0f9 Truncate long names in file picker dialog 2022-12-23 10:30:31 +03:00
Alexander Batalov
3b951b8578 Add opSetWeaponAmmoPid 2022-12-22 20:36:31 +03:00
Alexander Batalov
17a4ee8a68 Add Sfall version opcodes (#200) 2022-12-22 20:18:07 +03:00
Alexander Batalov
c85cda4b48 Fix rare crash when using grenades
Closes #208
2022-12-22 19:54:04 +03:00
Alexander Batalov
c7ca358295 Rename OBJECT_NO_SAVE flag 2022-12-22 16:49:17 +03:00
Alexander Batalov
96296417c6 Rename OBJECT_NO_REMOVE flag 2022-12-22 16:47:11 +03:00
Alexander Batalov
737076a126 Fix getting elevation of freed object 2022-12-22 14:20:37 +03:00
Alexander Batalov
c0908cf14f Fix visual artifacts caused by null tiles 2022-12-21 09:18:29 +03:00
Alexander Batalov
343911f736 Fix floor lighting 2022-12-20 18:44:21 +03:00
Alexander Batalov
281c1aa82a Review tileRenderFloorsInRect 2022-12-20 16:22:56 +03:00
Alexander Batalov
18afce71b0 Review tile_fill_roof 2022-12-20 15:10:42 +03:00
Alexander Batalov
9599d63f45 Review roof_fill_off 2022-12-20 15:09:01 +03:00
Alexander Batalov
ce351988b5 Review roof_fill_on 2022-12-20 15:01:57 +03:00
Alexander Batalov
feb3821565 Fix multiple knockout effects 2022-12-13 23:59:45 +03:00
Alexander Batalov
54eaff26ea Fix partyMemberIncLevels 2022-12-13 23:35:15 +03:00
Alexander Batalov
5a7ffde45e Fix max party member level 2022-12-13 23:29:34 +03:00
Alexander Batalov
1c0385ad11 Remove _obj_fix_combat_cid_for_dude
The same cid-to-object conversion performed by `combatLoad`.
2022-12-13 23:06:52 +03:00
Alexander Batalov
0fe6d88a3a Review combatLoad 2022-12-13 23:03:42 +03:00
Alexander Batalov
7596d4d721 Fix combat_is_shot_blocked 2022-12-13 22:18:12 +03:00
Alexander Batalov
8333e553e2 Add SubtileFill 2022-12-13 22:11:37 +03:00
Alexander Batalov
6f62cfd466 Fix critters dying on exit grids 2022-12-13 16:33:19 +03:00
Alexander Batalov
280b55ab05 Add isExitGridPid 2022-12-13 14:34:49 +03:00
Alexander Batalov
e02dcdf69d Fix car disappearing when using town interface 2022-12-13 13:20:55 +03:00
Alexander Batalov
b5701fb9b9 Fix worldmap scroll down button 2022-12-13 12:45:39 +03:00
Alexander Batalov
c569b879f6 Fix opPrint 2022-12-13 11:35:07 +03:00
Alexander Batalov
b36ac50425 Review windowShow 2022-12-13 10:37:52 +03:00
Alexander Batalov
029a84a113 Review gWindowIndexes 2022-12-13 10:18:02 +03:00
Alexander Batalov
e6fddd6905 Rename window flags 2022-12-13 10:04:05 +03:00
Alexander Batalov
b0b69bd780 Fix wmMapIdxToName 2022-12-09 00:52:03 +03:00
Alexander Batalov
6de4ac87df Use snprintf 2022-12-08 23:05:50 +03:00
Alexander Batalov
502c920656 Add reportOverloaded 2022-12-08 22:05:54 +03:00
Alexander Batalov
c383fc6b95 Add mobile keyboard support (#206) 2022-12-08 20:41:59 +03:00
Alexander Batalov
4b03c714b4 Bump libsdl to 2.26.1 2022-12-08 19:48:53 +03:00
Alexander Batalov
0d93279a10 Fix missing include 2022-12-08 19:38:55 +03:00
Alexander Batalov
510d7bfe95 Reorganize includes 2022-12-08 19:18:39 +03:00
Alexander Batalov
19d0d2919f Remove unnecessary includes 2022-12-08 17:29:30 +03:00
Alexander Batalov
42f01c8d45 Remove unused 16bpp code 2022-12-08 17:26:28 +03:00
Alexander Batalov
0474199b5c Remove unnecessary include 2022-12-08 17:13:06 +03:00
Alexander Batalov
8a4ce6c658 Improve splash screen handling (#3)
* Fixes splash screens in Olympus
2022-12-08 16:41:55 +03:00
Alexander Batalov
884034f0bf Refactor stretching blitters
* Fixes skipped pixels
* Improves readability
2022-12-08 16:34:38 +03:00
Alexander Batalov
602ef7c1e1 Increase maps ambient sfx capacity 2022-12-08 14:10:31 +03:00
Alexander Batalov
897ff2bbd2 Add dialog options numbering 2022-12-08 10:58:54 +03:00
Alexander Batalov
9ade107e95 Rename Button fields 2022-12-07 16:42:37 +03:00
Alexander Batalov
1698cfa04b Rename Window fields 2022-12-07 16:39:30 +03:00
Alexander Batalov
59639c60a3 Cleanup 2022-12-07 10:17:48 +03:00
Alexander Batalov
f056f0e131 Add opGetGameMode (#200) 2022-12-06 17:46:52 +03:00
Alexander Batalov
dc90beb696 Add game mode tracking 2022-12-06 17:22:30 +03:00
Alexander Batalov
90da2164f2 Fix assignment 2022-12-06 17:13:48 +03:00
Alexander Batalov
9d9e09e80f Use MESSAGE_LIST_ITEM_FIELD_MAX_SIZE 2022-12-06 12:37:59 +03:00
sonilyan
cd51e0cdd7 Fix crash for msg limit (#203) 2022-12-06 12:32:21 +03:00
drjfaust
d49869f45c Fix several combat bugs (#204) 2022-12-06 12:17:11 +03:00
Alexander Batalov
3a541d4b67 Fix endurance label
Closes #201
2022-11-10 23:48:09 +03:00
Alexander Batalov
7471f9cdbe Fix palette fading 2022-11-10 23:43:18 +03:00
Alexander Batalov
fe9ba9171e Add custom message lists support
Closes #130
See #200
2022-11-10 18:07:23 +03:00
Alexander Batalov
6c03e4e293 Fix storing pointers in map global variables 2022-11-09 14:35:07 +03:00
Alexander Batalov
3f25c9265b Fix arguments order 2022-11-09 13:54:32 +03:00
Alexander Batalov
fa475c754e Add opReadByte
See #200
2022-11-09 13:09:56 +03:00
Alexander Batalov
5e9cf96106 Rename state structs
Fixes UB leading to crash when exiting the game
2022-11-09 11:41:53 +03:00
Alexander Batalov
a70912d311 Fix ini section parsing 2022-11-09 11:11:19 +03:00
Alexander Batalov
67351b8b09 Fix player model customization 2022-11-09 10:21:12 +03:00
Alexander Batalov
e0e0a1ee7d Add Sfall list functions
See #200
2022-11-08 22:55:25 +03:00
Alexander Batalov
fa11122f87 Make spatial script finders public 2022-11-08 22:45:30 +03:00
Alexander Batalov
ef54463fcc Add pc bonus stats opcodes
See #200
2022-11-08 18:59:14 +03:00
Alexander Batalov
2255481d50 Add missing header 2022-11-08 18:07:11 +03:00
Alexander Batalov
03a7e2e1b1 Add some Sfall global vars functions
See #200
2022-11-08 18:01:00 +03:00
Alexander Batalov
19bcfebe0d Add ammo/charges opcodes
See #200
2022-11-07 20:41:33 +03:00
Alexander Batalov
393d89f8c4 Add some Sfall opcodes
See #200
2022-11-07 20:03:04 +03:00
Alexander Batalov
9c6286f94a Increase number of opcodes 2022-11-07 19:06:37 +03:00
Alexander Batalov
fa058f24b4 Add interface bar side panels
See #3
2022-11-07 16:05:34 +03:00
Alexander Batalov
b2420bdc80 Refactor artLock with FrmImage 2022-11-07 13:55:05 +03:00
sonilyan
23bee9fd62 Add IFACE_BAR_WIDTH support (#188) 2022-11-07 13:35:22 +03:00
Alexander Batalov
3ec827d5c1 Fix worldmap position calculation
Closes #192
2022-11-07 10:59:16 +03:00
Alexander Batalov
c692fc91af Fix error check
Closes #196
2022-11-03 18:16:50 +03:00
Alexander Batalov
a38151bf2c Reorganize control flow in gdCustomSelect
Fixes #198
2022-11-03 18:08:49 +03:00
Alexander Batalov
b689b3a757 Fix combat control fields not being cleared
Closes #194
2022-11-03 17:48:34 +03:00
Alexander Batalov
e7d68c4f72 Get rid of PreferenceDescription packing
Fixes pointer alignment warnings.
2022-10-30 13:00:43 +03:00
Alexander Batalov
bc43bdc99c Rename some variables in objectSetLocation 2022-10-30 09:39:43 +03:00
Alexander Batalov
e5aa4a3518 Fix square tiles initial lookup 2022-10-30 09:31:09 +03:00
Alexander Batalov
4e98093d6c Fix crash when removing all scripts 2022-10-30 09:03:24 +03:00
Alexander Batalov
1342fd3ecf Fix warning 2022-10-30 08:45:04 +03:00
Alexander Batalov
3015f39368 Review interfaceFontGetStringWidthImpl 2022-10-30 08:41:00 +03:00
Alexander Batalov
37f7ecc1c3 Review SlotMap2Game 2022-10-29 21:27:08 +03:00
Alexander Batalov
bdf2c219ab Fix error check 2022-10-29 21:09:53 +03:00
Alexander Batalov
3488833c2e Use SDL for logging 2022-10-29 20:57:11 +03:00
Alexander Batalov
4821cab724 Review tileSetCenter flags 2022-10-29 18:52:22 +03:00
Alexander Batalov
0851354c8a Rename _square_y 2022-10-29 18:25:28 +03:00
Alexander Batalov
2f42818a33 Review square coords math 2022-10-29 18:23:53 +03:00
Alexander Batalov
12367acb33 Fix byte swapping warnings 2022-10-29 18:17:57 +03:00
Alexander Batalov
caa8b06d4f Fix UB when parsing encounter table 2022-10-29 18:14:49 +03:00
Alexander Batalov
2b63360b95 Fix memory alignment 2022-10-29 17:26:46 +03:00
Alexander Batalov
8dd8d1c312 Replace random generator 2022-10-29 17:21:54 +03:00
Alexander Batalov
8ee0f478ad Fix setting managed button procs 2022-10-28 11:02:53 +03:00
Alexander Batalov
9c6daa4abe Fix selecting managed window 2022-10-28 10:59:38 +03:00
Alexander Batalov
01f264e0c2 Fix color setters 2022-10-28 10:35:22 +03:00
Alexander Batalov
1bbd586cbb Fix trimming line ending in .lst files 2022-10-26 10:17:39 +03:00
Alexander Batalov
223930c464 Get rid of goto 2022-10-25 17:37:45 +03:00
Alexander Batalov
40510539ee Fix interface font loading 2022-10-25 15:57:31 +03:00
Alexander Batalov
c47113ca29 Force screen redraw after playing movies
Fixes #180
2022-10-25 13:05:09 +03:00
Alexander Batalov
ad9b8586e9 Fix testing pointers for nulls in Nevada
Closes #178
2022-10-25 09:57:13 +03:00
sonilyan
5b151634a5 Fix memory alignment (#187) 2022-10-25 09:12:54 +03:00
Martin Janiczek
7627092478 Make sfall key naming consistent (#172) 2022-10-25 09:05:09 +03:00
Martin Janiczek
b60fe43b06 Refactor: use enum instead of magic numbers (#176) 2022-10-25 09:04:42 +03:00
Alexander Batalov
21aec548af Add iOS support (#167) 2022-10-14 11:40:11 +03:00
Alexander Batalov
beb010cd0f Normalize fade duration
See #165
2022-10-11 17:58:02 +03:00
Alexander Batalov
b23d05f850 Fix called shot window freeze
See #165
2022-10-10 11:42:10 +03:00
Alexander Batalov
e16659d96d Fix key processing
Fixes buffer-underwrite crash on macOS when scancode is remapped to -1.
2022-10-08 12:32:50 +03:00
Alexander Batalov
4c1020af5f Fix path handling in _gsound_get_music_path 2022-10-08 10:56:46 +03:00
Alexander Batalov
a4105d5826 Decouple drawing and rendering (#165) 2022-10-08 00:54:27 +03:00
Alexander Batalov
ddae4df4ab Fix red dialog buttons 2022-10-07 23:31:17 +03:00
Alexander Batalov
74a9a9e530 Add audio engine initialization check
Fixes various bugs when movie system attemps to create audio buffer with
sound system being disabled via fallout2.cfg.
2022-10-07 15:17:07 +03:00
Alexander Batalov
223d214f57 Review lsgSaveGame
* Add scrolling
* Rename variables
2022-10-07 12:04:30 +03:00
Alexander Batalov
a52fd4e70a Review lsgLoadGame
* Add missing cleanup
* Fixes double click handling
* Rename variables
2022-10-07 11:28:42 +03:00
Alexander Batalov
266c727870 Extract renderPresent 2022-10-06 19:10:01 +03:00
Alexander Batalov
54d230432b Refactor game config with Settings (#164) 2022-10-06 16:32:46 +03:00
Alexander Batalov
9663532e44 Add missing elevator ids 2022-10-05 18:29:21 +03:00
Alexander Batalov
a4e9123bcc Fix crash in obj_use_item_on 2022-10-05 17:55:54 +03:00
Alexander Batalov
08691ce319 Fix incorrect death animation in opKillCritterType 2022-10-05 16:58:57 +03:00
Alexander Batalov
d03fd5e43f Review opGetMessageString 2022-10-05 16:07:55 +03:00
Alexander Batalov
7ddd8f2894 Fix itemGetQuantity 2022-10-05 15:44:00 +03:00
Alexander Batalov
93a341bbaa Fix arguments order 2022-10-05 14:59:00 +03:00
Alexander Batalov
d2c4538656 Fix obj_can_see_obj elevation check 2022-10-05 14:56:29 +03:00
Alexander Batalov
8a5c3bc97b Fix obj_can_hear_obj implementation 2022-10-05 14:38:19 +03:00
Alexander Batalov
db57fe6a5e Review isWithinPerception 2022-10-05 14:25:36 +03:00
Alexander Batalov
56d798dd12 Display encounter description in one line 2022-10-05 14:20:32 +03:00
Alexander Batalov
90942c46b2 Uninline obj_set_seen 2022-10-05 13:49:18 +03:00
Alexander Batalov
1cda2beef7 Fix looting corpses with NoSteal flag 2022-10-05 13:26:13 +03:00
Alexander Batalov
8754a5d6c9 Rename critter flags 2022-10-05 13:06:49 +03:00
Alexander Batalov
65f43804b3 Rename core 2022-10-05 10:35:05 +03:00
Alexander Batalov
9b150257fb Rename some input functions 2022-10-05 10:11:47 +03:00
Alexander Batalov
32c7883f82 Cleanup input.h 2022-10-05 09:54:46 +03:00
Alexander Batalov
1c73fb7240 Extract input 2022-10-05 09:23:27 +03:00
Alexander Batalov
cce1bb223e Handle window size change (#163) 2022-10-04 13:15:54 +03:00
Alexander Batalov
624076ae9b Fix Doctor skill usage 2022-10-04 09:23:17 +03:00
Alexander Batalov
8d61a19dbe Remove declarations 2022-10-03 18:11:55 +03:00
Alexander Batalov
0264c00ab8 Extract keyboard 2022-10-03 16:42:34 +03:00
Alexander Batalov
e1f22b8218 Review game state functions 2022-10-03 15:54:18 +03:00
Alexander Batalov
fe4c125474 Fix crash when dialog speaker destroys itself
See #162
2022-10-03 15:04:30 +03:00
Alexander Batalov
56d27400ac Add missing include 2022-10-03 14:52:47 +03:00
Alexander Batalov
b5681984cd Extract mouse 2022-10-03 12:41:33 +03:00
Alexander Batalov
a541d0f24a Fix type warnings 2022-10-03 10:19:22 +03:00
Alexander Batalov
20a41088c6 Remove trap.cc 2022-10-03 10:01:42 +03:00
Alexander Batalov
16ab9cb958 Fix lock check 2022-10-03 09:38:43 +03:00
Alexander Batalov
5f9ceb7f5d Extract vcr 2022-10-03 09:37:05 +03:00
Alexander Batalov
a1c1e03da0 Refactor artLockFrameData with FrmImage 2022-09-26 16:09:22 +03:00
Alexander Batalov
ac0a044a32 Refactor artLockFrameData with FrmImage 2022-09-26 15:17:22 +03:00
Alexander Batalov
4aae167bd8 Refactor artLockFrameData with FrmImage 2022-09-26 11:55:06 +03:00
Alexander Batalov
2e5be31ed4 Refactor artLockFrameData with FrmImage 2022-09-26 11:14:11 +03:00
Alexander Batalov
45278f66a5 Refactor artLockFrameData with FrmImage 2022-09-26 10:36:54 +03:00
Alexander Batalov
2205077d36 Refactor artLockFrameData with FrmImage 2022-09-26 10:28:44 +03:00
Alexander Batalov
67f966f7a9 Refactor artLockFrameData with FrmImage 2022-09-24 22:53:25 +03:00
Alexander Batalov
463968d798 Refactor artLockFrameData with FrmImage 2022-09-24 21:29:50 +03:00
Alexander Batalov
109dc6680c Refactor artLockFrameData with FrmImage 2022-09-24 21:22:40 +03:00
Alexander Batalov
23c5f070fa Refactor artLockFrameData with FrmImage 2022-09-24 21:04:32 +03:00
Alexander Batalov
fd9843f8dc Fix type warning 2022-09-24 19:43:27 +03:00
Alexander Batalov
443070226f Refactor artLockFrameData with FrmImage 2022-09-24 19:41:25 +03:00
Alexander Batalov
db63d8a085 Refactor artLockFrameData with FrmImage 2022-09-24 18:15:52 +03:00
Alexander Batalov
f203cfcc83 Refactor artLockFrameData with FrmImage 2022-09-24 17:31:13 +03:00
Alexander Batalov
9ceb490f72 Refactor artLockFrameData with FrmImage 2022-09-24 17:03:50 +03:00
Alexander Batalov
5b7a676b35 Refactor artLockFrameData with FrmImage 2022-09-24 16:51:16 +03:00
Alexander Batalov
e50e26cf87 Refactor artLockFrameData with FrmImage 2022-09-24 16:39:15 +03:00
Alexander Batalov
f59c072d68 Add FrmImage 2022-09-24 16:14:54 +03:00
Alexander Batalov
79c396c1a0 Uninline characterSelectorWindowFatalError 2022-09-24 10:28:30 +03:00
Alexander Batalov
9bb053b3ba Add namespace (#155) 2022-09-23 15:43:44 +03:00
Alexander Batalov
3168c2ec09 Improve some combat functions readability 2022-09-23 14:01:39 +03:00
Alexander Batalov
cb9c72d1db Bump version to 1.1.0 2022-09-23 09:42:47 +03:00
Edgar Miró
7a455dcad5 Update readme (#143) 2022-09-23 09:33:03 +03:00
Alexander Batalov
cacbecb324 Update license 2022-09-22 13:23:09 +03:00
Alexander Batalov
c521dcaf57 Fix storing pointers in map local variables
Closes #152
2022-09-22 12:45:58 +03:00
Alexander Batalov
b89c06008b Add Windows icon 2022-09-22 10:13:28 +03:00
Alexander Batalov
c20e40652d Add Android icon 2022-09-15 22:06:36 +03:00
Alexander Batalov
2fe74627bd Add macOS icon 2022-09-15 20:59:02 +03:00
Alexander Batalov
9864a2551d Uninline mapSetEnteringLocation 2022-09-15 19:59:16 +03:00
Alexander Batalov
dea8c98399 Decompile square_init 2022-09-15 18:25:48 +03:00
Alexander Batalov
108a20ef27 Decompile mapLocalVariablesLoad 2022-09-15 18:22:27 +03:00
Alexander Batalov
0adc65054c Decompile mapLocalVariablesInit 2022-09-15 18:19:50 +03:00
Alexander Batalov
d72a74f6c3 Decompile mapGlobalVariablesLoad 2022-09-15 18:17:16 +03:00
Alexander Batalov
1d05bac7a1 Decompile mapGlobalVariablesInit 2022-09-15 18:16:46 +03:00
Alexander Batalov
980b40dcdd Review mapNewMap 2022-09-15 17:49:40 +03:00
Alexander Batalov
b1a8707fb2 Review isoExit 2022-09-15 17:45:35 +03:00
Alexander Batalov
382de999cc Review isoReset 2022-09-15 17:43:33 +03:00
Alexander Batalov
06618d9e21 Uninline wmAreaSetVisibleState 2022-09-15 14:25:21 +03:00
Alexander Batalov
1b6e29acc0 Reorder includes 2022-09-15 12:38:23 +03:00
Alexander Batalov
e5992779f3 Reconcile worldmap.cc 2022-09-15 11:42:02 +03:00
Alexander Batalov
d7bc8e4176 Rename worldmap.cc 2022-09-15 09:00:11 +03:00
Alexander Batalov
629978d7a6 Reconcile with reference edition 2022-09-01 18:41:37 +03:00
Alexander Batalov
039ad65557 Add getFileSize 2022-09-01 07:37:00 +03:00
Alexander Batalov
26e5104a96 Add compat_strdup 2022-09-01 06:48:26 +03:00
Alexander Batalov
d39276fe3a Update fpattern config 2022-09-01 06:24:01 +03:00
Alexander Batalov
2a17a07784 Fix AI behaviour for Snipe distance preference 2022-08-31 22:02:08 +03:00
Alexander Batalov
56d35bef0d Add more checks for AI to pick secondary hit mode 2022-08-31 20:25:40 +03:00
Alexander Batalov
676098dc5f Fix weapon safety check 2022-08-31 19:56:09 +03:00
Alexander Batalov
857d3902bb Fix AI weapon switching 2022-08-31 19:53:12 +03:00
Alexander Batalov
7803378d82 Fix some warnings 2022-08-31 18:52:01 +03:00
Alexander Batalov
893a116fb4 Fix getting random target 2022-08-31 18:31:23 +03:00
Alexander Batalov
916bf40602 Add CombatBadShot enum 2022-08-31 18:29:46 +03:00
Alexander Batalov
40d6348b09 Fix incorrect AP cost when AI reloads a weapon 2022-08-31 17:41:50 +03:00
Alexander Batalov
1e5047cd48 Improve aiHaveAmmo 2022-08-31 12:05:56 +03:00
Alexander Batalov
7750006127 Fix warnings 2022-08-31 10:11:01 +03:00
Alexander Batalov
1b69c97ce4 Improve aiAttemptWeaponReload 2022-08-31 10:05:00 +03:00
Alexander Batalov
b3431fadf7 Fix some warnings 2022-08-29 23:58:03 +03:00
Alexander Batalov
2a0d0633f1 Fix images for scroll buttons 2022-08-29 23:42:36 +03:00
Alexander Batalov
4ed86be78e Add missing worldmap button sounds 2022-08-29 23:35:58 +03:00
Alexander Batalov
db13317613 Fix default worldmap font 2022-08-29 23:33:03 +03:00
Alexander Batalov
8deb855b12 Fix Pathfinder perk 2022-08-29 17:37:58 +03:00
Alexander Batalov
24277424d2 Fix typo 2022-08-29 16:08:23 +03:00
Alexander Batalov
c593eceaaf Add pip-boy automaps patch 2022-08-29 16:07:34 +03:00
Alexander Batalov
e3a811c83d Add a flashing icon to the Horrigan encounter 2022-08-29 15:14:16 +03:00
Alexander Batalov
89fd83012a Fix quick destinations positioning 2022-08-24 13:11:46 +03:00
Alexander Batalov
e71447a3d3 Use pop-up message box about death from radiation 2022-08-19 20:25:14 +03:00
Alexander Batalov
b89d6dfd72 Fix crash when opening bag on the bartering table 2022-08-19 19:54:39 +03:00
Alexander Batalov
3878be0d09 Fix items disappearing when using bag 2022-08-19 19:42:21 +03:00
Alexander Batalov
21d550ad1e Fix attackDetermineToHit 2022-08-19 18:58:18 +03:00
Alexander Batalov
134ab451e3 Fix item name separator position 2022-08-19 18:07:12 +03:00
Alexander Batalov
25fc67fec1 Add mouse support on Android
Closes #116
2022-08-19 13:36:14 +03:00
Alexander Batalov
a41780caa2 Rename some item functions 2022-08-18 08:41:15 +03:00
Edgar Miró
4eb5e39946 Add loading dialog (#125)
Co-authored-by: Alexander Batalov <alex.batalov@gmail.com>
2022-08-17 09:32:08 +03:00
Alexander Batalov
354b0812c9 Fix missing Android signing configs in forks/PRs 2022-08-17 00:01:20 +03:00
Alexander Batalov
bee34bfdf4 Add items weight summary 2022-08-16 23:06:40 +03:00
Alexander Batalov
d31e367870 Tweak inventory scroller positions 2022-08-16 22:19:23 +03:00
Alexander Batalov
16f0be1a45 Add more inventory UI constants 2022-08-16 21:08:04 +03:00
Alexander Batalov
ebba548206 Use inventory slot size constants 2022-08-16 16:26:21 +03:00
Alexander Batalov
c9fa9a4765 Fix inventory defines misuse 2022-08-16 16:09:13 +03:00
Alexander Batalov
cfca07f7f2 Add idle func 2022-08-16 14:24:57 +03:00
Alexander Batalov
0f60556b73 Decompile some input functions 2022-08-16 14:14:53 +03:00
Nikola Đurinec
a1521049b5 Fix interface button positions (#131) 2022-08-16 11:52:37 +03:00
Alexander Batalov
1128813bf0 Fix testing pointers for nulls in Sonora 2022-08-16 11:35:21 +03:00
Alexander Batalov
1f6339b3b3 Uncollapse _db_select 2022-08-16 11:25:56 +03:00
Alexander Batalov
79f5e00f6e Fix town map hotkeys 2022-08-15 00:16:36 +03:00
Alexander Batalov
957c5af66b Fix pcx extension check 2022-08-14 23:22:37 +03:00
Alexander Batalov
c5c5ecd499 Fix for Jet antidote not being removed 2022-08-14 23:06:11 +03:00
Alexander Batalov
df4382f2e0 Fix Silent Death bonus for critical hits 2022-08-14 22:46:28 +03:00
Alexander Batalov
8700d0c601 Remove unreferenced label 2022-08-14 22:16:48 +03:00
Alexander Batalov
27c5beea01 Fix max items quantity added via scripting 2022-08-14 22:15:13 +03:00
Alexander Batalov
52af5cfc1f Fix object search when loading game in combat mode 2022-08-14 22:07:22 +03:00
Alexander Batalov
5a47f74023 Fix base EMP damage resistance 2022-08-14 21:45:58 +03:00
Alexander Batalov
c35ea77c59 Fix ammo details when examining in barter screen 2022-08-14 20:45:26 +03:00
Alexander Batalov
b10c580d6f Fix displaying xp gained with Swift Learner bonus 2022-08-14 18:46:02 +03:00
Alexander Batalov
7096116296 Fix best weapon calculations 2022-08-14 17:48:59 +03:00
Alexander Batalov
330edde003 Fix radiation bugs 2022-08-14 17:30:45 +03:00
Alexander Batalov
cac96bfc13 Add gender-specific text improvements (#130) 2022-08-14 13:06:51 +03:00
Alexander Batalov
31edb19379 Use English as fallback language 2022-08-14 12:52:13 +03:00
Alexander Batalov
d482f0e610 Add healing items improvements (#29) 2022-08-14 12:42:18 +03:00
Alexander Batalov
c3bffa6777 Increase script message lists capacity 2022-08-12 15:49:43 +03:00
Alexander Batalov
140234f40e Add game dialog fix 2022-08-12 15:03:06 +03:00
Alexander Batalov
13b76287f8 Add science/repair on critters patch 2022-08-12 14:55:36 +03:00
Alexander Batalov
a55feb9301 Add custom mouse mode frms 2022-08-12 14:19:16 +03:00
Alexander Batalov
d86a887cf9 Add damage mod (#29) 2022-08-12 13:54:18 +03:00
Alexander Batalov
5b2a1d13a1 Fix displaying secondary weapon mode stats 2022-08-11 14:30:06 +03:00
Alexander Batalov
1ca08cb97a Fix unarmed attack mode reset when using inventory 2022-08-11 14:12:29 +03:00
Alexander Batalov
ca78b94a7d Fix sprintf warning 2022-08-11 13:41:06 +03:00
Alexander Batalov
dcb53393c1 FIx opFloatMessage string arg handling
Closes #129
2022-08-11 13:34:37 +03:00
Alexander Batalov
4ece7d1188 Workaround for wrong animate_rotation usage
Closes #128
2022-08-11 13:25:43 +03:00
Alexander Batalov
87289a34c0 Add unarmed hits improvements (#29) 2022-08-10 20:46:09 +03:00
Alexander Batalov
2859410d4b Fix character selector not being reset 2022-08-08 12:04:03 +03:00
Alexander Batalov
9d53496521 Fix premade characters data freed too early 2022-08-08 11:34:36 +03:00
Alexander Batalov
4a8d5b13a8 Fix queue clearing 2022-08-08 11:01:52 +03:00
Alexander Batalov
8ba4fa309d Add shortcuts to close game windows 2022-08-07 21:49:53 +03:00
Alexander Batalov
0cab26227a Fix holodisk highlighting 2022-08-07 21:05:42 +03:00
Alexander Batalov
1694c68ebc Fix rest clickability exploit 2022-08-07 19:54:04 +03:00
Alexander Batalov
6c74c461c2 Review pipboyOpen 2022-08-07 19:03:43 +03:00
Alexander Batalov
0ec5306de5 Fix Barter button 2022-08-07 15:52:28 +03:00
Alexander Batalov
ba2ae3c303 Uninline gameDialogHighlightsExit 2022-08-07 15:38:47 +03:00
Alexander Batalov
019dbbb56e Add mouse wheel support 2022-08-07 14:52:44 +03:00
Alexander Batalov
a47918a83c Fix Pipboy screensaver on non-Windows platforms
Closes #115
2022-08-06 22:46:13 +03:00
Alexander Batalov
25e07fb597 Fix flags on non-door objects 2022-08-06 20:33:53 +03:00
Alexander Batalov
43ad927817 Fix dead critters reload weapons when combat ends 2022-08-06 20:12:23 +03:00
Alexander Batalov
d7966cdab2 Fix updating armor class 2022-08-06 20:05:33 +03:00
Alexander Batalov
33bda1612c Fix withdrawal perk crash 2022-08-06 18:45:44 +03:00
Alexander Batalov
f3eef3fe6c Fix negative Skilldex values 2022-08-06 18:21:47 +03:00
Alexander Batalov
d8ae5fbd32 Fix car charging bugs 2022-08-06 17:49:24 +03:00
Alexander Batalov
02e83a1989 Fix Sharshooter bonus 2022-08-06 17:07:07 +03:00
Alexander Batalov
5c8f7c4b00 Fix negate operator 2022-08-06 16:57:43 +03:00
Alexander Batalov
3592a7232c Add town reputation improvements (#29) 2022-08-06 15:50:33 +03:00
Alexander Batalov
ef067cd954 Reconcile with reference edition 2022-08-06 13:48:16 +03:00
Alexander Batalov
17382d7c7e Fix poping uninitialized pointer
Closes #113
2022-08-06 12:46:05 +03:00
Alexander Batalov
5170948588 Add explosions improvements (#29) 2022-08-05 17:43:00 +03:00
Alexander Batalov
3ccb087d20 Add burst mod (#29) 2022-08-03 17:23:24 +03:00
Alexander Batalov
3f9145b745 Remove varargs.h 2022-08-03 16:13:18 +03:00
Alexander Batalov
5b79d8ca79 Add premade characters improvements (#29) 2022-08-03 16:06:44 +03:00
Alexander Batalov
25c1eeee5c Add display monitor improvements (#29) 2022-08-03 13:54:26 +03:00
Alexander Batalov
f5060c301b Add elevators improvements (#29) 2022-08-03 12:34:13 +03:00
Alexander Batalov
8a2ca4b733 Add books improvements (#29) 2022-08-02 21:52:32 +03:00
Alexander Batalov
9610de0a66 Add criticals config defaults 2022-08-02 21:04:58 +03:00
Alexander Batalov
64627ddde9 Fix sprintf_s 2022-08-02 19:46:44 +03:00
Alexander Batalov
63bcb2d009 Add critical hits improvements (#29) 2022-08-02 19:37:31 +03:00
Alexander Batalov
df5bceaf2a Reconcile with reference edition 2022-08-02 13:31:26 +03:00
Alexander Batalov
66d46bd8a5 Fix skills usage slots
Closes #110
2022-08-02 12:09:33 +03:00
Alexander Batalov
28a433ea76 Fix parentesis warnings 2022-08-01 19:47:09 +03:00
Alexander Batalov
2c69834a12 Fix unequipped armor flags
Closes #108
2022-08-01 07:17:01 +03:00
Alexander Batalov
00ee53efca Fix CI branch 2022-07-31 23:06:27 +03:00
Alexander Batalov
4989cf6d5a Update CI workflow trigger events 2022-07-31 23:05:30 +03:00
Alexander Batalov
0d5aa705f0 Add activity to import game assets on Android
Fixes #105
2022-07-31 22:52:26 +03:00
Alexander Batalov
307b032118 Fix movie decoding on x64
Closes #107
2022-07-31 22:32:50 +03:00
Alexander Batalov
ab8279078e Fix party member level up message
Closes #104
2022-07-31 12:09:39 +03:00
Alexander Batalov
b7ac80e684 Fix floating point division
Closes #103
2022-07-31 12:03:43 +03:00
Alexander Batalov
6064a4bc79 Add Android controls (#101) 2022-07-29 20:04:37 +03:00
Alexander Batalov
e41a4b8e16 Reconcile with reference edition (#100) 2022-07-29 16:04:05 +03:00
Alexander Batalov
39057fd1fb Fix missing result on stack
Closes #95
Closes #96
2022-07-27 23:22:06 +03:00
296 changed files with 36883 additions and 22549 deletions

11
.gitattributes vendored
View File

@@ -2,14 +2,23 @@
*.c text eol=lf
*.cc text eol=lf
*.cmake text eol=lf
*.gradle text eol=lf
*.h text eol=lf
*.md text eol=lf
*.java text eol=lf
*.json text eol=lf
*.md text eol=lf
*.plist text eol=lf
*.pro text eol=lf
*.properties text eol=lf
*.xml text eol=lf
*.yml text eol=lf
.clang-format text eol=lf
.editorconfig text eol=lf
.gitattributes text eol=lf
.gitignore text eol=lf
gradlew text eol=lf
CMakeLists.txt text eol=lf
LICENSE text eol=lf
# Force CRLF
*.bat text eol=crlf

View File

@@ -2,19 +2,12 @@ name: Build
on:
push:
paths:
- '.github/workflows/ci-build.yml'
- 'src/**.cc'
- 'src/**.h'
- '**/CMakeLists.txt'
- '**/*.cmake'
branches:
- main
pull_request:
paths:
- '.github/workflows/ci-build.yml'
- 'src/**.cc'
- 'src/**.h'
- '**/CMakeLists.txt'
- '**/*.cmake'
types:
- opened
- synchronize
defaults:
run:
@@ -38,6 +31,95 @@ jobs:
- name: cppcheck
run: cppcheck --std=c++17 src/
android:
name: Android
runs-on: ubuntu-latest
steps:
- name: Clone
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v2
with:
distribution: temurin
java-version: 11
cache: gradle
- name: Cache cmake build
uses: actions/cache@v3
with:
path: os/android/app/.cxx
key: android-cmake-v1
- name: Setup signing config
if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != ''
run: |
cd os/android
echo "$KEYSTORE_FILE_BASE64" | base64 --decode > debug.keystore
echo "$KEYSTORE_PROPERTIES_FILE_BASE64" | base64 --decode > debug-keystore.properties
env:
KEYSTORE_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_FILE_BASE64 }}
KEYSTORE_PROPERTIES_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_PROPERTIES_FILE_BASE64 }}
- name: Build
run: |
cd os/android
./gradlew assembleDebug
- name: Upload
uses: actions/upload-artifact@v3
with:
name: fallout2-ce-debug.apk
path: os/android/app/build/outputs/apk/debug/app-debug.apk
retention-days: 7
ios:
name: iOS
runs-on: macos-11
steps:
- name: Clone
uses: actions/checkout@v3
- name: Cache cmake build
uses: actions/cache@v3
with:
path: build
key: ios-cmake-v1
- name: Configure
run: |
cmake \
-B build \
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
-D CMAKE_TOOLCHAIN_FILE=cmake/toolchain/ios.toolchain.cmake \
-D ENABLE_BITCODE=0 \
-D PLATFORM=OS64 \
# EOL
- name: Build
run: |
cmake \
--build build \
-j $(sysctl -n hw.physicalcpu) \
--target package \
# EOL
# TODO: Should be a part of packaging.
- name: Prepare for uploading
run: |
cp build/fallout2-ce.zip build/fallout2-ce.ipa
- name: Upload
uses: actions/upload-artifact@v3
with:
name: fallout2-ce.ipa
path: build/fallout2-ce.ipa
retention-days: 7
linux:
name: Linux (${{ matrix.arch }})

View File

@@ -10,6 +10,94 @@ defaults:
shell: bash
jobs:
android:
name: Android
runs-on: ubuntu-latest
steps:
- name: Clone
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v2
with:
distribution: temurin
java-version: 11
cache: gradle
- name: Cache cmake build
uses: actions/cache@v3
with:
path: os/android/app/.cxx
key: android-cmake-v1
- name: Setup signing config
if: env.KEYSTORE_FILE_BASE64 != '' && env.KEYSTORE_PROPERTIES_FILE_BASE64 != ''
run: |
cd os/android
echo "$KEYSTORE_FILE_BASE64" | base64 --decode > release.keystore
echo "$KEYSTORE_PROPERTIES_FILE_BASE64" | base64 --decode > release-keystore.properties
env:
KEYSTORE_FILE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_FILE_BASE64 }}
KEYSTORE_PROPERTIES_FILE_BASE64: ${{ secrets.ANDROID_RELEASE_KEYSTORE_PROPERTIES_FILE_BASE64 }}
- name: Build
run: |
cd os/android
./gradlew assembleRelease
- name: Upload
run: |
cd os/android/app/build/outputs/apk/release
cp app-release.apk fallout2-ce-android.apk
gh release upload ${{ github.ref_name }} fallout2-ce-android.apk
rm fallout2-ce-android.apk
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ios:
name: iOS
runs-on: macos-11
steps:
- name: Clone
uses: actions/checkout@v3
- name: Cache cmake build
uses: actions/cache@v3
with:
path: build
key: ios-cmake-v1
- name: Configure
run: |
cmake \
-B build \
-D CMAKE_BUILD_TYPE=RelWithDebInfo \
-D CMAKE_TOOLCHAIN_FILE=cmake/toolchain/ios.toolchain.cmake \
-D ENABLE_BITCODE=0 \
-D PLATFORM=OS64 \
# EOL
- name: Build
run: |
cmake \
--build build \
-j $(sysctl -n hw.physicalcpu) \
--target package \
# EOL
- name: Upload
run: |
cd build
cp fallout2-ce.zip fallout2-ce-ios.ipa
gh release upload ${{ github.ref_name }} fallout2-ce-ios.ipa
rm fallout2-ce-ios.ipa
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
linux:
name: Linux (${{ matrix.arch }})

View File

@@ -5,8 +5,13 @@ set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
set(EXECUTABLE_NAME fallout2-ce)
if (APPLE)
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "")
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
if(IOS)
set(CMAKE_OSX_DEPLOYMENT_TARGET "11" CACHE STRING "")
set(CMAKE_OSX_ARCHITECTURES "arm64" CACHE STRING "")
else()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.11" CACHE STRING "")
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "")
endif()
endif()
project(${EXECUTABLE_NAME})
@@ -52,8 +57,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/combat.h"
"src/config.cc"
"src/config.h"
"src/core.cc"
"src/core.h"
"src/credits.cc"
"src/credits.h"
"src/critter.cc"
@@ -80,8 +83,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/display_monitor.h"
"src/draw.cc"
"src/draw.h"
"src/electronic_registration.cc"
"src/electronic_registration.h"
"src/elevator.cc"
"src/elevator.h"
"src/endgame.cc"
@@ -104,8 +105,6 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/game_mouse.h"
"src/game_movie.cc"
"src/game_movie.h"
"src/game_palette.cc"
"src/game_palette.h"
"src/game_sound.cc"
"src/game_sound.h"
"src/game_vars.h"
@@ -115,10 +114,10 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/geometry.h"
"src/graph_lib.cc"
"src/graph_lib.h"
"src/grayscale.cc"
"src/grayscale.h"
"src/heap.cc"
"src/heap.h"
"src/input.cc"
"src/input.h"
"src/interface.cc"
"src/interface.h"
"src/interpreter_extra.cc"
@@ -131,6 +130,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/inventory.h"
"src/item.cc"
"src/item.h"
"src/kb.cc"
"src/kb.h"
"src/light.cc"
"src/light.h"
"src/lips.cc"
@@ -153,6 +154,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/mmx.h"
"src/mouse_manager.cc"
"src/mouse_manager.h"
"src/mouse.cc"
"src/mouse.h"
"src/movie_effect.cc"
"src/movie_effect.h"
"src/movie_lib.cc"
@@ -170,6 +173,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/palette.h"
"src/party_member.cc"
"src/party_member.h"
"src/pcx.cc"
"src/pcx.h"
"src/perk_defs.h"
"src/perk.cc"
"src/perk.h"
@@ -212,6 +217,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/stat.h"
"src/string_parsers.cc"
"src/string_parsers.h"
"src/svga.cc"
"src/svga.h"
"src/text_font.cc"
"src/text_font.h"
"src/text_object.cc"
@@ -221,8 +228,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/trait_defs.h"
"src/trait.cc"
"src/trait.h"
"src/trap.cc"
"src/trap.h"
"src/vcr.cc"
"src/vcr.h"
"src/version.cc"
"src/version.h"
"src/widget.cc"
@@ -237,8 +244,8 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/window.h"
"src/word_wrap.cc"
"src/word_wrap.h"
"src/world_map.cc"
"src/world_map.h"
"src/worldmap.cc"
"src/worldmap.h"
"src/xfile.cc"
"src/xfile.h"
)
@@ -252,10 +259,25 @@ target_sources(${EXECUTABLE_NAME} PUBLIC
"src/platform_compat.h"
"src/pointer_registry.cc"
"src/pointer_registry.h"
"src/settings.cc"
"src/settings.h"
"src/sfall_config.cc"
"src/sfall_config.h"
"src/sfall_global_vars.cc"
"src/sfall_global_vars.h"
"src/sfall_lists.cc"
"src/sfall_lists.h"
"src/sfall_opcodes.cc"
"src/sfall_opcodes.h"
)
if(IOS)
target_sources(${EXECUTABLE_NAME} PUBLIC
"src/platform/ios/paths.h"
"src/platform/ios/paths.mm"
)
endif()
if(WIN32)
target_compile_definitions(${EXECUTABLE_NAME} PUBLIC
_CRT_SECURE_NO_WARNINGS
@@ -274,11 +296,32 @@ if(WIN32)
)
endif()
if (WIN32)
target_sources(${EXECUTABLE_NAME} PUBLIC
"os/windows/fallout2-ce.ico"
"os/windows/fallout2-ce.rc"
)
endif()
if(APPLE)
set_target_properties(${EXECUTABLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/macos/Info.plist")
target_sources(${EXECUTABLE_NAME} PUBLIC "os/macos/fallout2-ce.icns")
set_source_files_properties("os/macos/fallout2-ce.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
if(IOS)
target_sources(${EXECUTABLE_NAME} PUBLIC "os/ios/LaunchScreen.storyboard")
set_source_files_properties("os/ios/LaunchScreen.storyboard" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
set_target_properties(${EXECUTABLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/ios/Info.plist")
set_target_properties(${EXECUTABLE_NAME} PROPERTIES XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2")
else()
set_target_properties(${EXECUTABLE_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/os/macos/Info.plist")
endif()
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.alexbatalov.fallout2-ce")
set(MACOSX_BUNDLE_BUNDLE_NAME "${EXECUTABLE_NAME}")
set(MACOSX_BUNDLE_ICON_FILE "fallout2-ce.icns")
set(MACOSX_BUNDLE_DISPLAY_NAME "Fallout 2")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "1.2.0")
set(MACOSX_BUNDLE_BUNDLE_VERSION "1.2.0")
endif()
add_subdirectory("third_party/fpattern")
@@ -300,23 +343,31 @@ target_link_libraries(${EXECUTABLE_NAME} ${SDL2_LIBRARIES})
target_include_directories(${EXECUTABLE_NAME} PRIVATE ${SDL2_INCLUDE_DIRS})
if(APPLE)
install(TARGETS ${EXECUTABLE_NAME} DESTINATION .)
install(CODE "
include(BundleUtilities)
fixup_bundle(${CMAKE_BINARY_DIR}/${MACOSX_BUNDLE_BUNDLE_NAME}.app \"\" \"\")
"
COMPONENT Runtime)
if(IOS)
install(TARGETS ${EXECUTABLE_NAME} DESTINATION "Payload")
if (CPACK_BUNDLE_APPLE_CERT_APP)
set(CPACK_GENERATOR "ZIP")
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF)
set(CPACK_PACKAGE_FILE_NAME "fallout2-ce")
else()
install(TARGETS ${EXECUTABLE_NAME} DESTINATION .)
install(CODE "
execute_process(COMMAND codesign --deep --force --options runtime --sign \"${CPACK_BUNDLE_APPLE_CERT_APP}\" ${CMAKE_BINARY_DIR}/${MACOSX_BUNDLE_BUNDLE_NAME}.app)
include(BundleUtilities)
fixup_bundle(${CMAKE_BINARY_DIR}/${MACOSX_BUNDLE_BUNDLE_NAME}.app \"\" \"\")
"
COMPONENT Runtime)
endif()
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK ON)
set(CPACK_PACKAGE_FILE_NAME "fallout2-ce")
if (CPACK_BUNDLE_APPLE_CERT_APP)
install(CODE "
execute_process(COMMAND codesign --deep --force --options runtime --sign \"${CPACK_BUNDLE_APPLE_CERT_APP}\" ${CMAKE_BINARY_DIR}/${MACOSX_BUNDLE_BUNDLE_NAME}.app)
"
COMPONENT Runtime)
endif()
set(CPACK_GENERATOR "DragNDrop")
set(CPACK_DMG_DISABLE_APPLICATIONS_SYMLINK ON)
set(CPACK_PACKAGE_FILE_NAME "fallout2-ce")
endif()
include(CPack)
endif()

51
LICENSE.md Normal file
View File

@@ -0,0 +1,51 @@
# Sustainable Use License
Version 1.0
## Acceptance
By using the software, you agree to all of the terms and conditions below.
## Copyright License
The licensor grants you a non-exclusive, royalty-free, worldwide, non-sublicensable, non-transferable license to use, copy, distribute, make available, and prepare derivative works of the software, in each case subject to the limitations below.
## Limitations
You may use or modify the software only for your own internal business purposes or for non-commercial or personal use. You may distribute the software or provide it to others only if you do so free of charge for non-commercial purposes. You may not alter, remove, or obscure any licensing, copyright, or other notices of the licensor in the software. Any use of the licensors trademarks is subject to applicable law.
## Patents
The licensor grants you a license, under any patent claims the licensor can license, or becomes able to license, to make, have made, use, sell, offer for sale, import and have imported the software, in each case subject to the limitations and conditions in this license. This license does not cover any patent claims that you cause to be infringed by modifications or additions to the software. If you or your company make any written claim that the software infringes or contributes to infringement of any patent, your patent license for the software granted under these terms ends immediately. If your company makes such a claim, your patent license ends immediately for work on behalf of your company.
## Notices
You must ensure that anyone who gets a copy of any part of the software from you also gets a copy of these terms. If you modify the software, you must include in any modified copies of the software a prominent notice stating that you have modified the software.
## No Other Rights
These terms do not imply any licenses other than those expressly granted in these terms.
## Termination
If you use the software in violation of these terms, such use is not licensed, and your license will automatically terminate. If the licensor provides you with a notice of your violation, and you cease all violation of this license no later than 30 days after you receive that notice, your license will be reinstated retroactively. However, if you violate these terms after such reinstatement, any additional violation of these terms will cause your license to terminate automatically and permanently.
## No Liability
As far as the law allows, the software comes as is, without any warranty or condition, and the licensor will not be liable to you for any damages arising out of these terms or the use or nature of the software, under any kind of legal claim.
## Definitions
The "licensor" is the entity offering these terms.
The "software" is the software the licensor makes available under these terms, including any portion of it.
"You" refers to the individual or entity agreeing to these terms.
"Your company" is any legal entity, sole proprietorship, or other kind of organization that you work for, plus all organizations that have control over, are under the control of, or are under common control with that organization. Control means ownership of substantially all the assets of an entity, or the power to direct its management and policies by vote, contract, or otherwise. Control can be direct or indirect.
"Your license" is the license granted to you for the software under these terms.
"Use" means anything you do with the software requiring your license.
"Trademark" means trademarks, service marks, and similar rights.

View File

@@ -34,6 +34,26 @@ $ sudo apt install libsdl2-2.0-0
- Run `fallout2-ce.app`.
### Android
> **NOTE**: Fallout 2 was designed with mouse in mind. There are many controls that require precise cursor positioning, which is not possible with fingers. When playing on Android you'll use fingers to move mouse cursor, not a character, or a map. Double tap to "click" left mouse button in the current cursor position, triple tap to "click" right mouse button. It might feel awkward at first, but it's super handy - you can play with just a thumb. This is not set in stone and might change in the future.
- Use Windows installation as a base - it contains data assets needed to play. Copy `Fallout2` folder to your device, for example to `Downloads`. You need `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder.
- Download `fallout2-ce.apk` and copy it to your device. Open it with file explorer, follow instructions (install from unknown source).
- When you run the game for the first time it will immediately present file picker. Select the folder from the first step. Wait until this data is copied. A loading dialog will appear, just wait for about 30 seconds. The game will start automatically.
### iOS
> **NOTE**: See Android note on controls.
- Download `fallout2-ce.ipa`. Use sideloading applications ([AltStore](https://altstore.io/) or [Sideloadly](https://sideloadly.io/)) to install it to your device. Alternatively you can always build from source with your own signing certificate.
- Run the game once. You'll see error message saying "Couldn't find/load text fonts". This step is needed for iOS to expose the game via File Sharing feature.
- Use Finder (macOS Catalina and later) or iTunes (Windows and macOS Mojave or earlier) to copy `master.dat`, `critter.dat`, `patch000.dat`, and `data` folder to "Fallout 2" app ([how-to](https://support.apple.com/HT210598)).
## Contributing
Integrating Sfall goodies is the top priority. Quality of life updates are OK too. Please no large scale refactorings at this time as we need to reconcile changes from Reference Edition, which will make this process slow and error-prone. In any case open up an issue with your suggestion or to notify other people that something is being worked on.
@@ -42,6 +62,6 @@ Integrating Sfall goodies is the top priority. Quality of life updates are OK to
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.
## Legal & License
## 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.
The source code is this repository is available under the [Sustainable Use License](LICENSE.md).

File diff suppressed because it is too large Load Diff

View File

@@ -7,3 +7,7 @@
/.idea/navEditor.xml
/.idea/workspace.xml
/local.properties
/debug-keystore.properties
/debug.keystore
/release-keystore.properties
/release.keystore

View File

@@ -1,2 +1,5 @@
/.cxx
/build
# TODO: Cleanup root .gitignore
!/src/debug

View File

@@ -9,8 +9,8 @@ android {
applicationId 'com.alexbatalov.fallout2ce'
minSdk 21
targetSdk 32
versionCode 1
versionName '1.0'
versionCode 3
versionName '1.2.0'
externalNativeBuild {
cmake {
arguments '-DANDROID_STL=c++_static'
@@ -26,10 +26,49 @@ android {
}
}
signingConfigs {
// Override default debug signing config to make sure every CI runner
// uses the same key for painless updates.
def debugKeystorePropertiesFile = rootProject.file('debug-keystore.properties')
if (debugKeystorePropertiesFile.exists()) {
def debugKeystoreProperties = new Properties()
debugKeystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile))
debug {
storeFile rootProject.file(debugKeystoreProperties.getProperty('storeFile'))
storePassword debugKeystoreProperties.getProperty('storePassword')
keyAlias debugKeystoreProperties.getProperty('keyAlias')
keyPassword debugKeystoreProperties.getProperty('keyPassword')
}
}
def releaseKeystoreProperties = new Properties()
def releaseKeystorePropertiesFile = rootProject.file('release-keystore.properties')
if (releaseKeystorePropertiesFile.exists()) {
releaseKeystoreProperties.load(new FileInputStream(releaseKeystorePropertiesFile))
release {
storeFile rootProject.file(releaseKeystoreProperties.getProperty('storeFile'))
storePassword releaseKeystoreProperties.getProperty('storePassword')
keyAlias releaseKeystoreProperties.getProperty('keyAlias')
keyPassword releaseKeystoreProperties.getProperty('keyPassword')
}
}
}
buildTypes {
debug {
// Prevents signing keys clashes between debug and release versions
// for painless development.
applicationIdSuffix '.debug'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
// Release signing config is optional and might not be present in CI
// builds, hence `findByName`.
signingConfig signingConfigs.findByName('release')
}
}
@@ -57,4 +96,5 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.documentfile:documentfile:1.0.1'
}

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Fallout 2 (Debug)</string>
</resources>

View File

@@ -58,6 +58,7 @@
An example Java class can be found in README-android.md
-->
<application android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:hardwareAccelerated="true" >
@@ -92,6 +93,10 @@
</intent-filter>
-->
</activity>
<activity android:name=".ImportActivity"
android:theme="@style/AppTheme">
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,56 @@
package com.alexbatalov.fallout2ce;
import android.content.ContentResolver;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class FileUtils {
static boolean copyRecursively(ContentResolver contentResolver, DocumentFile src, File dest) {
final DocumentFile[] documentFiles = src.listFiles();
for (final DocumentFile documentFile : documentFiles) {
if (documentFile.isFile()) {
if (!copyFile(contentResolver, documentFile, new File(dest, documentFile.getName()))) {
return false;
}
} else if (documentFile.isDirectory()) {
final File subdirectory = new File(dest, documentFile.getName());
if (!subdirectory.exists()) {
subdirectory.mkdir();
}
if (!copyRecursively(contentResolver, documentFile, subdirectory)) {
return false;
}
}
}
return true;
}
private static boolean copyFile(ContentResolver contentResolver, DocumentFile src, File dest) {
try {
final InputStream inputStream = contentResolver.openInputStream(src.getUri());
final OutputStream outputStream = new FileOutputStream(dest);
final byte[] buffer = new byte[16384];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}

View File

@@ -0,0 +1,74 @@
package com.alexbatalov.fallout2ce;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import androidx.documentfile.provider.DocumentFile;
import java.io.File;
public class ImportActivity extends Activity {
private static final int IMPORT_REQUEST_CODE = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, IMPORT_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == IMPORT_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
final Uri treeUri = resultData.getData();
if (treeUri != null) {
final DocumentFile treeDocument = DocumentFile.fromTreeUri(this, treeUri);
if (treeDocument != null) {
copyFiles(treeDocument);
return;
}
}
}
finish();
} else {
super.onActivityResult(requestCode, resultCode, resultData);
}
}
private void copyFiles(DocumentFile treeDocument) {
ProgressDialog dialog = createProgressDialog();
dialog.show();
new Thread(() -> {
ContentResolver contentResolver = getContentResolver();
File externalFilesDir = getExternalFilesDir(null);
FileUtils.copyRecursively(contentResolver, treeDocument, externalFilesDir);
startMainActivity();
dialog.dismiss();
finish();
}).start();
}
private void startMainActivity() {
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
}
private ProgressDialog createProgressDialog() {
ProgressDialog progressDialog = new ProgressDialog(this,
android.R.style.Theme_Material_Light_Dialog);
progressDialog.setTitle(R.string.loading_dialog_title);
progressDialog.setMessage(getString(R.string.loading_dialog_message));
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.setCancelable(false);
return progressDialog;
}
}

View File

@@ -1,18 +1,47 @@
package com.alexbatalov.fallout2ce;
import android.content.Intent;
import android.os.Bundle;
import org.libsdl.app.SDLActivity;
public class MainActivity extends SDLActivity {
protected void onCreate(Bundle savedInstanceState) {
// Needed to initialize `files` folder (and make it publicly accessible
// for file managers) for user to upload assets.
getExternalFilesDir(null);
import java.io.File;
public class MainActivity extends SDLActivity {
private boolean noExit = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final File externalFilesDir = getExternalFilesDir(null);
final File configFile = new File(externalFilesDir, "fallout2.cfg");
if (!configFile.exists()) {
final File masterDatFile = new File(externalFilesDir, "master.dat");
final File critterDatFile = new File(externalFilesDir, "critter.dat");
if (!masterDatFile.exists() || !critterDatFile.exists()) {
final Intent intent = new Intent(this, ImportActivity.class);
startActivity(intent);
noExit = true;
finish();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (!noExit) {
// Needed to make sure libc calls exit handlers, which releases
// in-game resources.
System.exit(0);
}
}
@Override
protected String[] getLibraries() {
return new String[]{
"fallout2-ce",

View File

@@ -15,13 +15,9 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -37,11 +33,8 @@ import android.view.Display;
import android.view.Gravity;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
@@ -51,6 +44,7 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -65,6 +59,9 @@ import java.util.Locale;
*/
public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
private static final String TAG = "SDL";
private static final int SDL_MAJOR_VERSION = 2;
private static final int SDL_MINOR_VERSION = 26;
private static final int SDL_MICRO_VERSION = 1;
/*
// Display InputType.SOURCE/CLASS of events and devices
//
@@ -213,7 +210,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
// Main components
protected static SDLActivity mSingleton;
protected static SDLSurface mSurface;
protected static View mTextEdit;
protected static DummyEdit mTextEdit;
protected static boolean mScreenKeyboardShown;
protected static ViewGroup mLayout;
protected static SDLClipboardHandler mClipboardHandler;
@@ -314,6 +311,10 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
mNextNativeState = NativeState.INIT;
mCurrentNativeState = NativeState.INIT;
}
protected SDLSurface createSDLSurface(Context context) {
return new SDLSurface(context);
}
// Setup
@Override
@@ -344,8 +345,18 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
errorMsgBrokenLib = e.getMessage();
}
if (mBrokenLibraries)
{
if (!mBrokenLibraries) {
String expected_version = String.valueOf(SDL_MAJOR_VERSION) + "." +
String.valueOf(SDL_MINOR_VERSION) + "." +
String.valueOf(SDL_MICRO_VERSION);
String version = nativeGetVersion();
if (!version.equals(expected_version)) {
mBrokenLibraries = true;
errorMsgBrokenLib = "SDL C/Java version mismatch (expected " + expected_version + ", got " + version + ")";
}
}
if (mBrokenLibraries) {
mSingleton = this;
AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
@@ -382,7 +393,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
mHIDDeviceManager = HIDDeviceManager.acquire(this);
// Set up the surface
mSurface = new SDLSurface(getApplication());
mSurface = createSDLSurface(getApplication());
mLayout = new RelativeLayout(this);
mLayout.addView(mSurface);
@@ -886,6 +897,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
}
// C functions we call
public static native String nativeGetVersion();
public static native int nativeSetupJNI();
public static native int nativeRunMain(String library, String function, Object arguments);
public static native void nativeLowMemory();
@@ -1220,8 +1232,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
}
// This method is called by SDLControllerManager's API 26 Generic Motion Handler.
public static View getContentView()
{
public static View getContentView() {
return mLayout;
}
@@ -1292,6 +1303,77 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh
return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
}
public static boolean handleKeyEvent(View v, int keyCode, KeyEvent event, InputConnection ic) {
int deviceId = event.getDeviceId();
int source = event.getSource();
if (source == InputDevice.SOURCE_UNKNOWN) {
InputDevice device = InputDevice.getDevice(deviceId);
if (device != null) {
source = device.getSources();
}
}
// if (event.getAction() == KeyEvent.ACTION_DOWN) {
// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
// } else if (event.getAction() == KeyEvent.ACTION_UP) {
// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
// }
// Dispatch the different events depending on where they come from
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
//
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
// So, retrieve the device itself and check all of its sources
if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
// Note that we process events with specific key codes here
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
return true;
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
return true;
}
}
}
if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (isTextInputEvent(event)) {
if (ic != null) {
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
} else {
SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
}
}
onNativeKeyDown(keyCode);
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
onNativeKeyUp(keyCode);
return true;
}
}
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
// they are ignored here because sending them as mouse input to SDL is messy
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
case KeyEvent.ACTION_UP:
// mark the event as handled or it will be handled by system
// handling KEYCODE_BACK by system will call onBackPressed()
return true;
}
}
}
return false;
}
/**
* This method is called by SDL using JNI.
*/
@@ -1809,455 +1891,6 @@ class SDLMain implements Runnable {
}
}
/**
SDLSurface. This is what we draw on, so we need to know when it's created
in order to do anything useful.
Because of this, that's where we set up the SDL thread
*/
class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
protected Display mDisplay;
// Keep track of the surface size to normalize touch events
protected float mWidth, mHeight;
// Is SurfaceView ready for rendering
public boolean mIsSurfaceReady;
// Startup
public SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
setOnGenericMotionListener(SDLActivity.getMotionListener());
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
mHeight = 1.0f;
mIsSurfaceReady = false;
}
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
public Surface getNativeSurface() {
return getHolder().getSurface();
}
// Called when we have a valid drawing surface
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v("SDL", "surfaceCreated()");
SDLActivity.onNativeSurfaceCreated();
}
// Called when we lose the surface
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("SDL", "surfaceDestroyed()");
// Transition to pause, if needed
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
SDLActivity.handleNativeState();
mIsSurfaceReady = false;
SDLActivity.onNativeSurfaceDestroyed();
}
// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
Log.v("SDL", "surfaceChanged()");
if (SDLActivity.mSingleton == null) {
return;
}
mWidth = width;
mHeight = height;
int nDeviceWidth = width;
int nDeviceHeight = height;
try
{
if (Build.VERSION.SDK_INT >= 17) {
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
}
} catch(Exception ignored) {
}
synchronized(SDLActivity.getContext()) {
// In case we're waiting on a size change after going fullscreen, send a notification.
SDLActivity.getContext().notifyAll();
}
Log.v("SDL", "Window size: " + width + "x" + height);
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
SDLActivity.onNativeResize();
// Prevent a screen distortion glitch,
// for instance when the device is in Landscape and a Portrait App is resumed.
boolean skip = false;
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
if (mWidth > mHeight) {
skip = true;
}
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
if (mWidth < mHeight) {
skip = true;
}
}
// Special Patch for Square Resolution: Black Berry Passport
if (skip) {
double min = Math.min(mWidth, mHeight);
double max = Math.max(mWidth, mHeight);
if (max / min < 1.20) {
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
skip = false;
}
}
// Don't skip in MultiWindow.
if (skip) {
if (Build.VERSION.SDK_INT >= 24) {
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
Log.v("SDL", "Don't skip in Multi-Window");
skip = false;
}
}
}
if (skip) {
Log.v("SDL", "Skip .. Surface is not ready.");
mIsSurfaceReady = false;
return;
}
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
SDLActivity.onNativeSurfaceChanged();
/* Surface is ready */
mIsSurfaceReady = true;
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
SDLActivity.handleNativeState();
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
int deviceId = event.getDeviceId();
int source = event.getSource();
if (source == InputDevice.SOURCE_UNKNOWN) {
InputDevice device = InputDevice.getDevice(deviceId);
if (device != null) {
source = device.getSources();
}
}
// if (event.getAction() == KeyEvent.ACTION_DOWN) {
// Log.v("SDL", "key down: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
// } else if (event.getAction() == KeyEvent.ACTION_UP) {
// Log.v("SDL", "key up: " + keyCode + ", deviceId = " + deviceId + ", source = " + source);
// }
// Dispatch the different events depending on where they come from
// Some SOURCE_JOYSTICK, SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
// So, we try to process them as JOYSTICK/DPAD/GAMEPAD events first, if that fails we try them as KEYBOARD
//
// Furthermore, it's possible a game controller has SOURCE_KEYBOARD and
// SOURCE_JOYSTICK, while its key events arrive from the keyboard source
// So, retrieve the device itself and check all of its sources
if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) {
// Note that we process events with specific key codes here
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLControllerManager.onNativePadDown(deviceId, keyCode) == 0) {
return true;
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
if (SDLControllerManager.onNativePadUp(deviceId, keyCode) == 0) {
return true;
}
}
}
if ((source & InputDevice.SOURCE_KEYBOARD) == InputDevice.SOURCE_KEYBOARD) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLActivity.isTextInputEvent(event)) {
SDLInputConnection.nativeCommitText(String.valueOf((char) event.getUnicodeChar()), 1);
}
SDLActivity.onNativeKeyDown(keyCode);
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
SDLActivity.onNativeKeyUp(keyCode);
return true;
}
}
if ((source & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) {
// on some devices key events are sent for mouse BUTTON_BACK/FORWARD presses
// they are ignored here because sending them as mouse input to SDL is messy
if ((keyCode == KeyEvent.KEYCODE_BACK) || (keyCode == KeyEvent.KEYCODE_FORWARD)) {
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
case KeyEvent.ACTION_UP:
// mark the event as handled or it will be handled by system
// handling KEYCODE_BACK by system will call onBackPressed()
return true;
}
}
}
return false;
}
// Touch events
@Override
public boolean onTouch(View v, MotionEvent event) {
/* Ref: http://developer.android.com/training/gestures/multi.html */
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int i = -1;
float x,y,p;
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
// 12290 = Samsung DeX mode desktop mouse
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
// 0x2 = SOURCE_CLASS_POINTER
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
int mouseButton = 1;
try {
Object object = event.getClass().getMethod("getButtonState").invoke(event);
if (object != null) {
mouseButton = (Integer) object;
}
} catch(Exception ignored) {
}
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event);
y = motionListener.getEventY(event);
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
} else {
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
/* fallthrough */
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
// We thus should check here.
int newOrientation;
float x, y;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
break;
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
break;
}
if (newOrientation != SDLActivity.mCurrentOrientation) {
SDLActivity.mCurrentOrientation = newOrientation;
SDLActivity.onNativeOrientationChanged(newOrientation);
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
// Captured pointer events for API 26.
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(0);
y = event.getY(0);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
return false;
}
}
/* This is a fake invisible editor view that receives the input and defines the
* pan&scan region
*/
@@ -2278,21 +1911,7 @@ class DummyEdit extends View implements View.OnKeyListener {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
/*
* This handles the hardware keyboard input
*/
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (SDLActivity.isTextInputEvent(event)) {
ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
return true;
}
SDLActivity.onNativeKeyDown(keyCode);
return true;
} else if (event.getAction() == KeyEvent.ACTION_UP) {
SDLActivity.onNativeKeyUp(keyCode);
return true;
}
return false;
return SDLActivity.handleKeyEvent(v, keyCode, event, ic);
}
//
@@ -2316,9 +1935,10 @@ class DummyEdit extends View implements View.OnKeyListener {
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
ic = new SDLInputConnection(this, true);
outAttrs.inputType = InputType.TYPE_CLASS_TEXT;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
| EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
outAttrs.inputType = InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_FLAG_MULTI_LINE;
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI |
EditorInfo.IME_FLAG_NO_FULLSCREEN /* API 11 */;
return ic;
}
@@ -2326,9 +1946,17 @@ class DummyEdit extends View implements View.OnKeyListener {
class SDLInputConnection extends BaseInputConnection {
protected EditText mEditText;
protected String mCommittedText = "";
public SDLInputConnection(View targetView, boolean fullEditor) {
super(targetView, fullEditor);
mEditText = new EditText(SDL.getContext());
}
@Override
public Editable getEditable() {
return mEditText.getEditableText();
}
@Override
@@ -2351,79 +1979,84 @@ class SDLInputConnection extends BaseInputConnection {
}
}
return super.sendKeyEvent(event);
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
/* Generate backspaces for the text we're going to replace */
final Editable content = getEditable();
if (content != null) {
int a = getComposingSpanStart(content);
int b = getComposingSpanEnd(content);
if (a == -1 || b == -1) {
a = Selection.getSelectionStart(content);
b = Selection.getSelectionEnd(content);
}
if (a < 0) a = 0;
if (b < 0) b = 0;
if (b < a) {
int tmp = a;
a = b;
b = tmp;
}
int backspaces = (b - a);
for (int i = 0; i < backspaces; i++) {
nativeGenerateScancodeForUnichar('\b');
}
if (!super.commitText(text, newCursorPosition)) {
return false;
}
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '\n') {
if (SDLActivity.onNativeSoftReturnKey()) {
return true;
}
}
nativeGenerateScancodeForUnichar(c);
}
SDLInputConnection.nativeCommitText(text.toString(), newCursorPosition);
return super.commitText(text, newCursorPosition);
updateText();
return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (!super.setComposingText(text, newCursorPosition)) {
return false;
}
updateText();
return true;
}
nativeSetComposingText(text.toString(), newCursorPosition);
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (!super.deleteSurroundingText(beforeLength, afterLength)) {
return false;
}
updateText();
return true;
}
return super.setComposingText(text, newCursorPosition);
protected void updateText() {
final Editable content = getEditable();
if (content == null) {
return;
}
String text = content.toString();
int compareLength = Math.min(text.length(), mCommittedText.length());
int matchLength, offset;
/* Backspace over characters that are no longer in the string */
for (matchLength = 0; matchLength < compareLength; ) {
int codePoint = mCommittedText.codePointAt(matchLength);
if (codePoint != text.codePointAt(matchLength)) {
break;
}
matchLength += Character.charCount(codePoint);
}
/* FIXME: This doesn't handle graphemes, like '🌬️' */
for (offset = matchLength; offset < mCommittedText.length(); ) {
int codePoint = mCommittedText.codePointAt(offset);
nativeGenerateScancodeForUnichar('\b');
offset += Character.charCount(codePoint);
}
if (matchLength < text.length()) {
String pendingText = text.subSequence(matchLength, text.length()).toString();
for (offset = 0; offset < pendingText.length(); ) {
int codePoint = pendingText.codePointAt(offset);
if (codePoint == '\n') {
if (SDLActivity.onNativeSoftReturnKey()) {
return;
}
}
/* Higher code points don't generate simulated scancodes */
if (codePoint < 128) {
nativeGenerateScancodeForUnichar((char)codePoint);
}
offset += Character.charCount(codePoint);
}
SDLInputConnection.nativeCommitText(pendingText, 0);
}
mCommittedText = text;
}
public static native void nativeCommitText(String text, int newCursorPosition);
public native void nativeGenerateScancodeForUnichar(char c);
public native void nativeSetComposingText(String text, int newCursorPosition);
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
// Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
// and https://bugzilla.libsdl.org/show_bug.cgi?id=2265
if (beforeLength > 0 && afterLength == 0) {
// backspace(s)
while (beforeLength-- > 0) {
nativeGenerateScancodeForUnichar('\b');
}
return true;
}
return super.deleteSurroundingText(beforeLength, afterLength);
}
public static native void nativeGenerateScancodeForUnichar(char c);
}
class SDLClipboardHandler implements

View File

@@ -0,0 +1,405 @@
package org.libsdl.app;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
/**
SDLSurface. This is what we draw on, so we need to know when it's created
in order to do anything useful.
Because of this, that's where we set up the SDL thread
*/
public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
View.OnKeyListener, View.OnTouchListener, SensorEventListener {
// Sensors
protected SensorManager mSensorManager;
protected Display mDisplay;
// Keep track of the surface size to normalize touch events
protected float mWidth, mHeight;
// Is SurfaceView ready for rendering
public boolean mIsSurfaceReady;
// Startup
public SDLSurface(Context context) {
super(context);
getHolder().addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
setOnGenericMotionListener(SDLActivity.getMotionListener());
// Some arbitrary defaults to avoid a potential division by zero
mWidth = 1.0f;
mHeight = 1.0f;
mIsSurfaceReady = false;
}
public void handlePause() {
enableSensor(Sensor.TYPE_ACCELEROMETER, false);
}
public void handleResume() {
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(this);
setOnTouchListener(this);
enableSensor(Sensor.TYPE_ACCELEROMETER, true);
}
public Surface getNativeSurface() {
return getHolder().getSurface();
}
// Called when we have a valid drawing surface
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.v("SDL", "surfaceCreated()");
SDLActivity.onNativeSurfaceCreated();
}
// Called when we lose the surface
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.v("SDL", "surfaceDestroyed()");
// Transition to pause, if needed
SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED;
SDLActivity.handleNativeState();
mIsSurfaceReady = false;
SDLActivity.onNativeSurfaceDestroyed();
}
// Called when the surface is resized
@Override
public void surfaceChanged(SurfaceHolder holder,
int format, int width, int height) {
Log.v("SDL", "surfaceChanged()");
if (SDLActivity.mSingleton == null) {
return;
}
mWidth = width;
mHeight = height;
int nDeviceWidth = width;
int nDeviceHeight = height;
try
{
if (Build.VERSION.SDK_INT >= 17) {
DisplayMetrics realMetrics = new DisplayMetrics();
mDisplay.getRealMetrics( realMetrics );
nDeviceWidth = realMetrics.widthPixels;
nDeviceHeight = realMetrics.heightPixels;
}
} catch(Exception ignored) {
}
synchronized(SDLActivity.getContext()) {
// In case we're waiting on a size change after going fullscreen, send a notification.
SDLActivity.getContext().notifyAll();
}
Log.v("SDL", "Window size: " + width + "x" + height);
Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight);
SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate());
SDLActivity.onNativeResize();
// Prevent a screen distortion glitch,
// for instance when the device is in Landscape and a Portrait App is resumed.
boolean skip = false;
int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
if (mWidth > mHeight) {
skip = true;
}
} else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) {
if (mWidth < mHeight) {
skip = true;
}
}
// Special Patch for Square Resolution: Black Berry Passport
if (skip) {
double min = Math.min(mWidth, mHeight);
double max = Math.max(mWidth, mHeight);
if (max / min < 1.20) {
Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
skip = false;
}
}
// Don't skip in MultiWindow.
if (skip) {
if (Build.VERSION.SDK_INT >= 24) {
if (SDLActivity.mSingleton.isInMultiWindowMode()) {
Log.v("SDL", "Don't skip in Multi-Window");
skip = false;
}
}
}
if (skip) {
Log.v("SDL", "Skip .. Surface is not ready.");
mIsSurfaceReady = false;
return;
}
/* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */
SDLActivity.onNativeSurfaceChanged();
/* Surface is ready */
mIsSurfaceReady = true;
SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED;
SDLActivity.handleNativeState();
}
// Key events
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
return SDLActivity.handleKeyEvent(v, keyCode, event, null);
}
// Touch events
@Override
public boolean onTouch(View v, MotionEvent event) {
/* Ref: http://developer.android.com/training/gestures/multi.html */
int touchDevId = event.getDeviceId();
final int pointerCount = event.getPointerCount();
int action = event.getActionMasked();
int pointerFingerId;
int i = -1;
float x,y,p;
/*
* Prevent id to be -1, since it's used in SDL internal for synthetic events
* Appears when using Android emulator, eg:
* adb shell input mouse tap 100 100
* adb shell input touchscreen tap 100 100
*/
if (touchDevId < 0) {
touchDevId -= 1;
}
// 12290 = Samsung DeX mode desktop mouse
// 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN
// 0x2 = SOURCE_CLASS_POINTER
if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) {
int mouseButton = 1;
try {
Object object = event.getClass().getMethod("getButtonState").invoke(event);
if (object != null) {
mouseButton = (Integer) object;
}
} catch(Exception ignored) {
}
// We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values
// if we are. We'll leverage our existing mouse motion listener
SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener();
x = motionListener.getEventX(event);
y = motionListener.getEventY(event);
SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode());
} else {
switch(action) {
case MotionEvent.ACTION_MOVE:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_DOWN:
// Primary pointer up/down, the index is always zero
i = 0;
/* fallthrough */
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN:
// Non primary pointer up/down
if (i == -1) {
i = event.getActionIndex();
}
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
break;
case MotionEvent.ACTION_CANCEL:
for (i = 0; i < pointerCount; i++) {
pointerFingerId = event.getPointerId(i);
x = event.getX(i) / mWidth;
y = event.getY(i) / mHeight;
p = event.getPressure(i);
if (p > 1.0f) {
// may be larger than 1.0f on some devices
// see the documentation of getPressure(i)
p = 1.0f;
}
SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
}
break;
default:
break;
}
}
return true;
}
// Sensor events
public void enableSensor(int sensortype, boolean enabled) {
// TODO: This uses getDefaultSensor - what if we have >1 accels?
if (enabled) {
mSensorManager.registerListener(this,
mSensorManager.getDefaultSensor(sensortype),
SensorManager.SENSOR_DELAY_GAME, null);
} else {
mSensorManager.unregisterListener(this,
mSensorManager.getDefaultSensor(sensortype));
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO
}
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
// Since we may have an orientation set, we won't receive onConfigurationChanged events.
// We thus should check here.
int newOrientation;
float x, y;
switch (mDisplay.getRotation()) {
case Surface.ROTATION_90:
x = -event.values[1];
y = event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE;
break;
case Surface.ROTATION_270:
x = event.values[1];
y = -event.values[0];
newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED;
break;
case Surface.ROTATION_180:
x = -event.values[0];
y = -event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED;
break;
case Surface.ROTATION_0:
default:
x = event.values[0];
y = event.values[1];
newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT;
break;
}
if (newOrientation != SDLActivity.mCurrentOrientation) {
SDLActivity.mCurrentOrientation = newOrientation;
SDLActivity.onNativeOrientationChanged(newOrientation);
}
SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
y / SensorManager.GRAVITY_EARTH,
event.values[2] / SensorManager.GRAVITY_EARTH);
}
}
// Captured pointer events for API 26.
public boolean onCapturedPointerEvent(MotionEvent event)
{
int action = event.getActionMasked();
float x, y;
switch (action) {
case MotionEvent.ACTION_SCROLL:
x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
SDLActivity.onNativeMouse(0, action, x, y, false);
return true;
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
x = event.getX(0);
y = event.getY(0);
SDLActivity.onNativeMouse(0, action, x, y, true);
return true;
case MotionEvent.ACTION_BUTTON_PRESS:
case MotionEvent.ACTION_BUTTON_RELEASE:
// Change our action value to what SDL's code expects.
if (action == MotionEvent.ACTION_BUTTON_PRESS) {
action = MotionEvent.ACTION_DOWN;
} else { /* MotionEvent.ACTION_BUTTON_RELEASE */
action = MotionEvent.ACTION_UP;
}
x = event.getX(0);
y = event.getY(0);
int button = event.getButtonState();
SDLActivity.onNativeMouse(button, action, x, y, true);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#000000</color>
</resources>

View File

@@ -1,3 +1,5 @@
<resources>
<string name="app_name">Fallout 2 Community Edition</string>
<string name="app_name">Fallout 2</string>
<string name="loading_dialog_title">PLEASE STAND BY</string>
<string name="loading_dialog_message">Copying files…</string>
</resources>

178
os/android/gradlew.bat vendored
View File

@@ -1,89 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

56
os/ios/Info.plist Normal file
View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>${MACOSX_BUNDLE_DISPLAY_NAME}</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.role-playing-games</string>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSHighResolutionCapable</key>
<string>True</string>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21225" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="834" height="1194"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

BIN
os/macos/fallout2-ce.icns Normal file

Binary file not shown.

BIN
os/windows/fallout2-ce.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

View File

@@ -0,0 +1 @@
IDI_ICON1 ICON DISCARDABLE "fallout2-ce.ico"

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,8 @@
#include "combat_defs.h"
#include "obj_types.h"
namespace fallout {
int _action_attack(Attack* attack);
int _action_use_an_item_on_object(Object* a1, Object* a2, Object* a3);
int _action_use_an_object(Object* a1, Object* a2);
@@ -16,9 +18,11 @@ bool _can_see(Object* a1, Object* a2);
bool _action_explode_running();
int actionExplode(int tile, int elevation, int minDamage, int maxDamage, Object* a5, bool a6);
int actionTalk(Object* a1, Object* a2);
void _action_dmg(int tile, int elevation, int minDamage, int maxDamage, int damageType, bool animated, bool bypassArmor);
void actionDamage(int tile, int elevation, int minDamage, int maxDamage, int damageType, bool animated, bool bypassArmor);
bool actionCheckPush(Object* a1, Object* a2);
int actionPush(Object* a1, Object* a2);
int _action_can_talk_to(Object* a1, Object* a2);
} // namespace fallout
#endif /* ACTIONS_H */

File diff suppressed because it is too large Load Diff

View File

@@ -3,36 +3,16 @@
#include "combat_defs.h"
#include "obj_types.h"
#include "sound.h"
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;
namespace fallout {
typedef enum AnimationRequestOptions {
ANIMATION_REQUEST_UNRESERVED = 0x01,
ANIMATION_REQUEST_RESERVED = 0x02,
ANIMATION_REQUEST_NO_STAND = 0x04,
ANIMATION_REQUEST_PING = 0x100,
ANIMATION_REQUEST_INSIGNIFICANT = 0x200,
} AnimationRequestOptions;
// Basic animations: 0-19
// Knockdown and death: 20-35
@@ -112,16 +92,20 @@ typedef enum AnimationType {
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*);
#define FID_ANIM_TYPE(value) ((value) & 0xFF0000) >> 16
typedef struct STRUCT_530014_28 {
// Signature of animation callback accepting 2 parameters.
typedef int(AnimationCallback)(void* a1, void* a2);
// Signature of animation callback accepting 3 parameters.
typedef int(AnimationCallback3)(void* a1, void* a2, void* a3);
typedef struct StraightPathNode {
int tile;
int elevation;
int x;
int y;
} STRUCT_530014_28;
} StraightPathNode;
typedef Object* PathBuilderCallback(Object* object, int tile, int elevation);
@@ -133,40 +117,47 @@ int _register_priority(int a1);
int reg_anim_clear(Object* a1);
int reg_anim_end();
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 animationRegisterMoveToObject(Object* owner, Object* destination, int actionPoints, int delay);
int animationRegisterRunToObject(Object* owner, Object* destination, int actionPoints, int delay);
int animationRegisterMoveToTile(Object* owner, int tile, int elevation, int actionPoints, int delay);
int animationRegisterRunToTile(Object* owner, int tile, int elevation, int actionPoints, int delay);
int animationRegisterMoveToTileStraight(Object* object, int tile, int elevation, int anim, int delay);
int animationRegisterMoveToTileStraightAndWaitForComplete(Object* owner, int tile, int elev, int anim, int delay);
int animationRegisterAnimate(Object* owner, int anim, int delay);
int animationRegisterAnimateReversed(Object* owner, int anim, int delay);
int animationRegisterAnimateAndHide(Object* owner, int anim, int delay);
int animationRegisterRotateToTile(Object* owner, int tile);
int animationRegisterRotateClockwise(Object* owner);
int animationRegisterRotateCounterClockwise(Object* owner);
int animationRegisterHideObject(Object* object);
int animationRegisterHideObjectForced(Object* object);
int animationRegisterCallback(void* a1, void* a2, AnimationCallback* proc, int delay);
int animationRegisterCallback3(void* a1, void* a2, void* a3, AnimationCallback3* proc, int delay);
int animationRegisterCallbackForced(void* a1, void* a2, AnimationCallback* proc, int delay);
int animationRegisterSetFlag(Object* object, int flag, int delay);
int animationRegisterUnsetFlag(Object* object, int flag, int delay);
int animationRegisterSetFid(Object* owner, int fid, int delay);
int animationRegisterTakeOutWeapon(Object* owner, int weaponAnimationCode, int delay);
int animationRegisterSetLightDistance(Object* owner, int lightDistance, int delay);
int animationRegisterToggleOutline(Object* object, bool outline, int delay);
int animationRegisterPlaySoundEffect(Object* owner, const char* soundEffectName, int delay);
int animationRegisterAnimateForever(Object* owner, int anim, int delay);
int animationRegisterPing(int flags, int delay);
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 _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 _make_straight_path(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6);
int _make_straight_path_func(Object* a1, int from, int to, StraightPathNode* straightPathNodeList, Object** obstaclePtr, int a6, PathBuilderCallback* callback);
void _object_animate();
int _check_move(int* a1);
int _dude_move(int a1);
int _dude_run(int a1);
int _check_move(int* actionPointsPtr);
int _dude_move(int actionPoints);
int _dude_run(int actionPoints);
void _dude_fidget();
void _dude_stand(Object* obj, int rotation, int fid);
void _dude_standup(Object* a1);
void animationStop();
int animationRegisterSetLightIntensity(Object* owner, int lightDistance, int lightIntensity, int delay);
} // namespace fallout
#endif /* ANIMATION_H */

View File

@@ -1,18 +1,21 @@
#include "art.h"
#include "animation.h"
#include "debug.h"
#include "draw.h"
#include "game_config.h"
#include "memory.h"
#include "object.h"
#include "proto.h"
#include "sfall_config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "animation.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "memory.h"
#include "object.h"
#include "proto.h"
#include "settings.h"
#include "sfall_config.h"
namespace fallout {
typedef struct ArtListDescription {
int flags;
char name[16];
@@ -31,8 +34,10 @@ static int artReadList(const char* path, char** out_arr, int* out_count);
static int artCacheGetFileSizeImpl(int a1, int* out_size);
static int artCacheReadDataImpl(int a1, int* a2, unsigned char* data);
static void artCacheFreeImpl(void* ptr);
static int artReadFrameData(unsigned char* data, File* stream, int count);
static int artReadFrameData(unsigned char* data, File* stream, int count, int* paddingPtr);
static int artReadHeader(Art* art, File* stream);
static int artGetDataSize(Art* art);
static int paddingForSize(int size);
// 0x5002D8
static char gDefaultJumpsuitMaleFileName[] = "hmjmps";
@@ -126,41 +131,36 @@ static int* gArtCritterFidShoudRunData;
int artInit()
{
char path[COMPAT_MAX_PATH];
int i;
File* stream;
char str[200];
char *ptr, *curr;
int cacheSize;
if (!configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_ART_CACHE_SIZE_KEY, &cacheSize)) {
cacheSize = 8;
}
char string[200];
int cacheSize = settings.system.art_cache_size;
if (!cacheInit(&gArtCache, artCacheGetFileSizeImpl, artCacheReadDataImpl, artCacheFreeImpl, cacheSize << 20)) {
debugPrint("cache_init failed in art_init\n");
return -1;
}
char* language;
if (configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_LANGUAGE_KEY, &language) && compat_stricmp(language, ENGLISH) != 0) {
const char* language = settings.system.language.c_str();
if (compat_stricmp(language, ENGLISH) != 0) {
strcpy(gArtLanguage, language);
gArtLanguageInitialized = true;
}
for (i = 0; i < 11; i++) {
gArtListDescriptions[i].flags = 0;
sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[i].name, gArtListDescriptions[i].name);
bool critterDbSelected = false;
for (int objectType = 0; objectType < OBJ_TYPE_COUNT; objectType++) {
gArtListDescriptions[objectType].flags = 0;
snprintf(path, sizeof(path), "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[objectType].name, gArtListDescriptions[objectType].name);
if (artReadList(path, &(gArtListDescriptions[i].fileNames), &(gArtListDescriptions[i].fileNamesLength)) != 0) {
if (artReadList(path, &(gArtListDescriptions[objectType].fileNames), &(gArtListDescriptions[objectType].fileNamesLength)) != 0) {
debugPrint("art_read_lst failed in art_init\n");
cacheFree(&gArtCache);
return -1;
}
}
_anon_alias = (int*)internal_malloc(sizeof(*_anon_alias) * gArtListDescriptions[1].fileNamesLength);
_anon_alias = (int*)internal_malloc(sizeof(*_anon_alias) * gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength);
if (_anon_alias == NULL) {
gArtListDescriptions[1].fileNamesLength = 0;
gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength = 0;
debugPrint("Out of memory for anon_alias in art_init\n");
cacheFree(&gArtCache);
return -1;
@@ -168,17 +168,17 @@ int artInit()
gArtCritterFidShoudRunData = (int*)internal_malloc(sizeof(*gArtCritterFidShoudRunData) * gArtListDescriptions[1].fileNamesLength);
if (gArtCritterFidShoudRunData == NULL) {
gArtListDescriptions[1].fileNamesLength = 0;
gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength = 0;
debugPrint("Out of memory for artCritterFidShouldRunData in art_init\n");
cacheFree(&gArtCache);
return -1;
}
for (i = 0; i < gArtListDescriptions[1].fileNamesLength; i++) {
gArtCritterFidShoudRunData[i] = 0;
for (int critterIndex = 0; critterIndex < gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength; critterIndex++) {
gArtCritterFidShoudRunData[critterIndex] = 0;
}
sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].name);
snprintf(path, sizeof(path), "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[OBJ_TYPE_CRITTER].name, gArtListDescriptions[OBJ_TYPE_CRITTER].name);
stream = fileOpen(path, "rt");
if (stream == NULL) {
@@ -206,75 +206,70 @@ int artInit()
tribalMaleFileName = gDefaultTribalMaleFileName;
}
char *tribalFemaleFileName = NULL;
char* tribalFemaleFileName = NULL;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_DUDE_NATIVE_LOOK_TRIBAL_FEMALE_KEY, &tribalFemaleFileName);
if (tribalFemaleFileName == NULL || tribalFemaleFileName[0] == '\0') {
tribalFemaleFileName = gDefaultTribalFemaleFileName;
}
ptr = gArtListDescriptions[1].fileNames;
for (i = 0; i < gArtListDescriptions[1].fileNamesLength; i++) {
if (compat_stricmp(ptr, jumpsuitMaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_JUMPSUIT][GENDER_MALE] = i;
} else if (compat_stricmp(ptr, jumpsuitFemaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_JUMPSUIT][GENDER_FEMALE] = i;
char* critterFileNames = gArtListDescriptions[OBJ_TYPE_CRITTER].fileNames;
for (int critterIndex = 0; critterIndex < gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength; critterIndex++) {
if (compat_stricmp(critterFileNames, jumpsuitMaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_JUMPSUIT][GENDER_MALE] = critterIndex;
} else if (compat_stricmp(critterFileNames, jumpsuitFemaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_JUMPSUIT][GENDER_FEMALE] = critterIndex;
}
if (compat_stricmp(ptr, tribalMaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_TRIBAL][GENDER_MALE] = i;
_art_vault_guy_num = i;
} else if (compat_stricmp(ptr, tribalFemaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_TRIBAL][GENDER_FEMALE] = i;
if (compat_stricmp(critterFileNames, tribalMaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_TRIBAL][GENDER_MALE] = critterIndex;
_art_vault_guy_num = critterIndex;
} else if (compat_stricmp(critterFileNames, tribalFemaleFileName) == 0) {
_art_vault_person_nums[DUDE_NATIVE_LOOK_TRIBAL][GENDER_FEMALE] = critterIndex;
}
ptr += 13;
critterFileNames += 13;
}
for (i = 0; i < gArtListDescriptions[1].fileNamesLength; i++) {
if (!fileReadString(str, sizeof(str), stream)) {
for (int critterIndex = 0; critterIndex < gArtListDescriptions[OBJ_TYPE_CRITTER].fileNamesLength; critterIndex++) {
if (!fileReadString(string, sizeof(string), stream)) {
break;
}
ptr = str;
curr = ptr;
while (*curr != '\0' && *curr != ',') {
curr++;
}
char* sep1 = strchr(string, ',');
if (sep1 != NULL) {
_anon_alias[critterIndex] = atoi(sep1 + 1);
if (*curr != '\0') {
_anon_alias[i] = atoi(curr + 1);
ptr = curr + 1;
curr = ptr;
while (*curr != '\0' && *curr != ',') {
curr++;
char* sep2 = strchr(sep1 + 1, ',');
if (sep2 != NULL) {
gArtCritterFidShoudRunData[critterIndex] = atoi(sep2 + 1);
} else {
gArtCritterFidShoudRunData[critterIndex] = 0;
}
gArtCritterFidShoudRunData[i] = *curr != '\0' ? atoi(ptr) : 0;
} else {
_anon_alias[i] = _art_vault_guy_num;
gArtCritterFidShoudRunData[i] = 1;
_anon_alias[critterIndex] = _art_vault_guy_num;
gArtCritterFidShoudRunData[critterIndex] = 1;
}
}
fileClose(stream);
ptr = gArtListDescriptions[4].fileNames;
for (i = 0; i < gArtListDescriptions[4].fileNamesLength; i++) {
if (compat_stricmp(ptr, "grid001.frm") == 0) {
_art_mapper_blank_tile = i;
char* tileFileNames = gArtListDescriptions[OBJ_TYPE_TILE].fileNames;
for (int tileIndex = 0; tileIndex < gArtListDescriptions[OBJ_TYPE_TILE].fileNamesLength; tileIndex++) {
if (compat_stricmp(tileFileNames, "grid001.frm") == 0) {
_art_mapper_blank_tile = tileIndex;
}
tileFileNames += 13;
}
gHeadDescriptions = (HeadDescription*)internal_malloc(sizeof(HeadDescription) * gArtListDescriptions[8].fileNamesLength);
gHeadDescriptions = (HeadDescription*)internal_malloc(sizeof(*gHeadDescriptions) * gArtListDescriptions[OBJ_TYPE_HEAD].fileNamesLength);
if (gHeadDescriptions == NULL) {
gArtListDescriptions[8].fileNamesLength = 0;
gArtListDescriptions[OBJ_TYPE_HEAD].fileNamesLength = 0;
debugPrint("Out of memory for head_info in art_init\n");
cacheFree(&gArtCache);
return -1;
}
sprintf(path, "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].name);
snprintf(path, sizeof(path), "%s%s%s\\%s.lst", _cd_path_base, "art\\", gArtListDescriptions[OBJ_TYPE_HEAD].name, gArtListDescriptions[OBJ_TYPE_HEAD].name);
stream = fileOpen(path, "rt");
if (stream == NULL) {
@@ -283,46 +278,42 @@ int artInit()
return -1;
}
for (i = 0; i < gArtListDescriptions[8].fileNamesLength; i++) {
if (!fileReadString(str, sizeof(str), stream)) {
for (int headIndex = 0; headIndex < gArtListDescriptions[OBJ_TYPE_HEAD].fileNamesLength; headIndex++) {
if (!fileReadString(string, sizeof(string), stream)) {
break;
}
ptr = str;
curr = ptr;
while (*curr != '\0' && *curr != ',') {
curr++;
char* sep1 = strchr(string, ',');
if (sep1 != NULL) {
*sep1 = '\0';
} else {
sep1 = string;
}
if (*curr != '\0') {
ptr = curr + 1;
curr = ptr;
while (*curr != '\0' && *curr != ',') {
curr++;
}
if (*curr != '\0') {
gHeadDescriptions[i].goodFidgetCount = atoi(ptr);
ptr = curr + 1;
curr = ptr;
while (*curr != '\0' && *curr != ',') {
curr++;
}
if (*curr != '\0') {
gHeadDescriptions[i].neutralFidgetCount = atoi(ptr);
ptr = curr + 1;
curr = strpbrk(ptr, " ,;\t\n");
if (curr != NULL) {
*curr = '\0';
}
gHeadDescriptions[i].badFidgetCount = atoi(ptr);
}
}
char* sep2 = strchr(sep1, ',');
if (sep2 != NULL) {
*sep2 = '\0';
} else {
sep2 = sep1;
}
gHeadDescriptions[headIndex].goodFidgetCount = atoi(sep1 + 1);
char* sep3 = strchr(sep2, ',');
if (sep3 != NULL) {
*sep3 = '\0';
} else {
sep3 = sep2;
}
gHeadDescriptions[headIndex].neutralFidgetCount = atoi(sep2 + 1);
char* sep4 = strpbrk(sep3 + 1, " ,;\t\n");
if (sep4 != NULL) {
*sep4 = '\0';
}
gHeadDescriptions[headIndex].badFidgetCount = atoi(sep3 + 1);
}
fileClose(stream);
@@ -357,19 +348,19 @@ void artExit()
// 0x418F1C
char* artGetObjectTypeName(int objectType)
{
return objectType >= 0 && objectType < OBJ_TYPE_COUNT ? gArtListDescriptions[objectType].name : NULL;
return objectType >= OBJ_TYPE_ITEM && objectType < OBJ_TYPE_COUNT ? gArtListDescriptions[objectType].name : NULL;
}
// 0x418F34
int artIsObjectTypeHidden(int objectType)
{
return objectType >= 0 && objectType < OBJ_TYPE_COUNT ? gArtListDescriptions[objectType].flags & 1 : 0;
return objectType >= OBJ_TYPE_ITEM && objectType < OBJ_TYPE_COUNT ? gArtListDescriptions[objectType].flags & 1 : 0;
}
// 0x418F7C
int artGetFidgetCount(int headFid)
{
if ((headFid & 0xF000000) >> 24 != OBJ_TYPE_HEAD) {
if (FID_TYPE(headFid) != OBJ_TYPE_HEAD) {
return 0;
}
@@ -522,15 +513,15 @@ int artCacheFlush()
}
// 0x4192B0
int artCopyFileName(int type, int id, char* dest)
int artCopyFileName(int objectType, int id, char* dest)
{
ArtListDescription* ptr;
if (type < 0 && type >= 11) {
if (objectType < OBJ_TYPE_ITEM && objectType >= OBJ_TYPE_COUNT) {
return -1;
}
ptr = &(gArtListDescriptions[type]);
ptr = &(gArtListDescriptions[objectType]);
if (id >= ptr->fileNamesLength) {
return -1;
@@ -622,7 +613,7 @@ char* artBuildFilePath(int fid)
v10 = (fid & 0x70000000) >> 28;
v1 = _art_alias_fid(fid);
v1 = artAliasFid(fid);
if (v1 != -1) {
v2 = v1;
}
@@ -630,15 +621,15 @@ char* artBuildFilePath(int fid)
*_art_name = '\0';
v3 = v2 & 0xFFF;
v4 = (v2 & 0xFF0000) >> 16;
v4 = FID_ANIM_TYPE(v2);
v5 = (v2 & 0xF000) >> 12;
type = (v2 & 0xF000000) >> 24;
type = FID_TYPE(v2);
if (v3 >= gArtListDescriptions[type].fileNamesLength) {
return NULL;
}
if (type < 0 || type >= 11) {
if (type < OBJ_TYPE_ITEM || type >= OBJ_TYPE_COUNT) {
return NULL;
}
@@ -649,19 +640,19 @@ char* artBuildFilePath(int fid)
return NULL;
}
if (v10) {
sprintf(_art_name, "%s%s%s\\%s%c%c.fr%c", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12, v10 + 47);
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c.fr%c", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12, v10 + 47);
} else {
sprintf(_art_name, "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12);
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[1].name, gArtListDescriptions[1].fileNames + v8, v11, v12);
}
} else if (type == 8) {
v9 = _head2[v4];
if (v9 == 'f') {
sprintf(_art_name, "%s%s%s\\%s%c%c%d.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], 102, v5);
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c%d.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], 102, v5);
} else {
sprintf(_art_name, "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], v9);
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s%c%c.frm", _cd_path_base, "art\\", gArtListDescriptions[8].name, gArtListDescriptions[8].fileNames + v8, _head1[v4], v9);
}
} else {
sprintf(_art_name, "%s%s%s\\%s", _cd_path_base, "art\\", gArtListDescriptions[type].name, gArtListDescriptions[type].fileNames + v8);
snprintf(_art_name, sizeof(_art_name), "%s%s%s\\%s", _cd_path_base, "art\\", gArtListDescriptions[type].name, gArtListDescriptions[type].fileNames + v8);
}
return _art_name;
@@ -669,55 +660,45 @@ char* artBuildFilePath(int fid)
// art_read_lst
// 0x419664
static int artReadList(const char* path, char** out_arr, int* out_count)
static int artReadList(const char* path, char** artListPtr, int* artListSizePtr)
{
File* stream;
char str[200];
char* arr;
int count;
char* brk;
stream = fileOpen(path, "rt");
File* stream = fileOpen(path, "rt");
if (stream == NULL) {
return -1;
}
count = 0;
while (fileReadString(str, sizeof(str), stream)) {
int count = 0;
char string[200];
while (fileReadString(string, sizeof(string), stream)) {
count++;
}
fileSeek(stream, 0, SEEK_SET);
*out_count = count;
*artListSizePtr = count;
arr = (char*)internal_malloc(13 * count);
*out_arr = arr;
if (arr == NULL) {
goto err;
char* artList = (char*)internal_malloc(13 * count);
*artListPtr = artList;
if (artList == NULL) {
fileClose(stream);
return -1;
}
while (fileReadString(str, sizeof(str), stream)) {
brk = strpbrk(str, " ,;\r\t\n");
while (fileReadString(string, sizeof(string), stream)) {
char* brk = strpbrk(string, " ,;\r\t\n");
if (brk != NULL) {
*brk = '\0';
}
strncpy(arr, str, 12);
arr[12] = '\0';
strncpy(artList, string, 12);
artList[12] = '\0';
arr += 13;
artList += 13;
}
fileClose(stream);
return 0;
err:
fileClose(stream);
return -1;
}
// 0x419760
@@ -854,9 +835,9 @@ ArtFrame* artGetFrame(Art* art, int frame, int rotation)
return NULL;
}
ArtFrame* frm = (ArtFrame*)((unsigned char*)art + sizeof(*art) + art->dataOffsets[rotation]);
ArtFrame* frm = (ArtFrame*)((unsigned char*)art + sizeof(*art) + art->dataOffsets[rotation] + art->padding[rotation]);
for (int index = 0; index < frame; index++) {
frm = (ArtFrame*)((unsigned char*)frm + sizeof(*frm) + frm->size);
frm = (ArtFrame*)((unsigned char*)frm + sizeof(*frm) + frm->size + paddingForSize(frm->size));
}
return frm;
}
@@ -864,54 +845,35 @@ ArtFrame* artGetFrame(Art* art, int frame, int rotation)
// 0x4198C8
bool artExists(int fid)
{
int v3;
bool result;
v3 = -1;
result = false;
if ((fid & 0xF000000) >> 24 == 1) {
v3 = _db_current(1);
// _db_current(_critter_db_handle);
_db_current(0);
}
bool result = false;
char* filePath = artBuildFilePath(fid);
if (filePath == NULL) {
goto out;
}
int fileSize;
if (dbGetFileSize(filePath, &fileSize) == -1) {
goto out;
}
result = true;
out:
if (v3 != -1) {
_db_current(v3);
if (filePath != NULL) {
int fileSize;
if (dbGetFileSize(filePath, &fileSize) != -1) {
result = true;
}
}
return result;
}
// NOTE: Exactly the same implementation as `artExists`.
//
// 0x419930
bool _art_fid_valid(int fid)
{
// NOTE: Original Code involves calling some unknown function. Check in debugger in mapper.
bool result = false;
char* filePath = artBuildFilePath(fid);
if (filePath == NULL) {
return false;
if (filePath != NULL) {
int fileSize;
if (dbGetFileSize(filePath, &fileSize) != -1) {
result = true;
}
}
int fileSize;
if (dbGetFileSize(filePath, &fileSize) == -1) {
return false;
}
return true;
return result;
}
// 0x419998
@@ -923,7 +885,7 @@ int _art_alias_num(int index)
// 0x4199AC
int artCritterFidShouldRun(int fid)
{
if ((fid & 0xF000000) >> 24 == 1) {
if (FID_TYPE(fid) == OBJ_TYPE_CRITTER) {
return gArtCritterFidShoudRunData[fid & 0xFFF];
}
@@ -931,76 +893,63 @@ int artCritterFidShouldRun(int fid)
}
// 0x4199D4
int _art_alias_fid(int fid)
int artAliasFid(int fid)
{
int v2;
int v3;
int result;
int type = FID_TYPE(fid);
int anim = FID_ANIM_TYPE(fid);
if (type == OBJ_TYPE_CRITTER) {
if (anim == ANIM_ELECTRIFY
|| anim == ANIM_BURNED_TO_NOTHING
|| anim == ANIM_ELECTRIFIED_TO_NOTHING
|| anim == ANIM_ELECTRIFY_SF
|| anim == ANIM_BURNED_TO_NOTHING_SF
|| anim == ANIM_ELECTRIFIED_TO_NOTHING_SF
|| anim == ANIM_FIRE_DANCE
|| anim == ANIM_CALLED_SHOT_PIC) {
// NOTE: Original code is slightly different. It uses many mutually
// mirrored bitwise operators. Probably result of some macros for
// getting/setting individual bits on fid.
return (fid & 0x70000000) | ((anim << 16) & 0xFF0000) | 0x1000000 | (fid & 0xF000) | (_anon_alias[fid & 0xFFF] & 0xFFF);
}
}
v2 = (fid & 0xF000000) >> 24;
v3 = (fid & 0xFF0000) >> 16;
if (v2 != 1 || v3 != 27 && v3 != 29 && v3 != 30 && v3 != 55 && v3 != 57 && v3 != 58 && v3 != 33 && v3 != 64)
result = -1;
else
result = ((fid & 0x70000000) >> 28 << 28) & 0x70000000 | (v3 << 16) & 0xFF0000 | 0x1000000 | (((fid & 0xF000) >> 12) << 12) & 0xF000 | _anon_alias[fid & 0xFFF] & 0xFFF;
return result;
return -1;
}
// 0x419A78
static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
{
int v4;
char* str;
char* ptr;
int result;
char path[COMPAT_MAX_PATH];
bool loaded;
int fileSize;
int result = -1;
v4 = -1;
result = -1;
char* artFilePath = artBuildFilePath(fid);
if (artFilePath != NULL) {
bool loaded = false;
File* stream = NULL;
if ((fid & 0xF000000) >> 24 == 1) {
v4 = _db_current(1);
// _db_current(_critter_db_handle);
_db_current(0);
}
str = artBuildFilePath(fid);
if (str != NULL) {
loaded = false;
if (gArtLanguageInitialized) {
ptr = str;
while (*ptr != '\0' && *ptr != '\\') {
ptr++;
char* pch = strchr(artFilePath, '\\');
if (pch == NULL) {
pch = artFilePath;
}
if (*ptr == '\0') {
ptr = str;
}
char localizedPath[COMPAT_MAX_PATH];
snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch);
sprintf(path, "art\\%s\\%s", gArtLanguage, ptr);
if (dbGetFileSize(path, &fileSize) == 0) {
loaded = true;
}
stream = fileOpen(localizedPath, "rb");
}
if (!loaded) {
if (dbGetFileSize(str, &fileSize) == 0) {
loaded = true;
if (stream == NULL) {
stream = fileOpen(artFilePath, "rb");
}
if (stream != NULL) {
Art art;
if (artReadHeader(&art, stream) == 0) {
*sizePtr = artGetDataSize(&art);
result = 0;
}
fileClose(stream);
}
if (loaded) {
*sizePtr = fileSize;
result = 0;
}
}
if (v4 != -1) {
_db_current(v4);
}
return result;
@@ -1009,58 +958,37 @@ static int artCacheGetFileSizeImpl(int fid, int* sizePtr)
// 0x419B78
static int artCacheReadDataImpl(int fid, int* sizePtr, unsigned char* data)
{
int v4;
char* str;
char* ptr;
int result;
char path[COMPAT_MAX_PATH];
bool loaded;
int result = -1;
v4 = -1;
result = -1;
if ((fid & 0xF000000) >> 24 == 1) {
v4 = _db_current(1);
// _db_current(_critter_db_handle);
_db_current(0);
}
str = artBuildFilePath(fid);
if (str != NULL) {
loaded = false;
char* artFileName = artBuildFilePath(fid);
if (artFileName != NULL) {
bool loaded = false;
if (gArtLanguageInitialized) {
ptr = str;
while (*ptr != '\0' && *ptr != '\\') {
ptr++;
char* pch = strchr(artFileName, '\\');
if (pch == NULL) {
pch = artFileName;
}
if (*ptr == '\0') {
ptr = str;
}
char localizedPath[COMPAT_MAX_PATH];
snprintf(localizedPath, sizeof(localizedPath), "art\\%s\\%s", gArtLanguage, pch);
sprintf(path, "art\\%s\\%s", gArtLanguage, ptr);
if (artRead(str, data) == 0) {
if (artRead(localizedPath, data) == 0) {
loaded = true;
}
}
if (!loaded) {
if (artRead(str, data) == 0) {
if (artRead(artFileName, data) == 0) {
loaded = true;
}
}
if (loaded) {
// TODO: Why it adds 74?
*sizePtr = ((Art*)data)->field_3A + 74;
*sizePtr = artGetDataSize((Art*)data);
result = 0;
}
}
if (v4 != -1) {
_db_current(v4);
}
return result;
}
@@ -1071,7 +999,7 @@ static void artCacheFreeImpl(void* ptr)
}
// 0x419C88
int buildFid(int objectType, int a2, int anim, int a3, int rotation)
int buildFid(int objectType, int frmId, int animType, int a3, int rotation)
{
int v7, v8, v9, v10;
@@ -1081,13 +1009,13 @@ int buildFid(int objectType, int a2, int anim, int a3, int rotation)
goto zero;
}
if (anim == 33 || anim < 20 || anim > 35) {
if (animType == ANIM_FIRE_DANCE || animType < ANIM_FALL_BACK || animType > ANIM_FALL_FRONT_BLOOD) {
goto zero;
}
v7 = ((a3 << 12) & 0xF000) | (anim << 16) & 0xFF0000 | 0x1000000;
v8 = (rotation << 28) & 0x70000000 | v7;
v9 = a2 & 0xFFF;
v7 = ((a3 << 12) & 0xF000) | ((animType << 16) & 0xFF0000) | 0x1000000;
v8 = ((rotation << 28) & 0x70000000) | v7;
v9 = frmId & 0xFFF;
if (artExists(v9 | v8) != 0) {
goto out;
@@ -1108,13 +1036,14 @@ zero:
out:
return (v10 << 28) & 0x70000000 | (objectType << 24) | (anim << 16) & 0xFF0000 | (a3 << 12) & 0xF000 | a2 & 0xFFF;
return ((v10 << 28) & 0x70000000) | (objectType << 24) | ((animType << 16) & 0xFF0000) | ((a3 << 12) & 0xF000) | (frmId & 0xFFF);
}
// 0x419D60
static int artReadFrameData(unsigned char* data, File* stream, int count)
static int artReadFrameData(unsigned char* data, File* stream, int count, int* paddingPtr)
{
unsigned char* ptr = data;
int padding = 0;
for (int index = 0; index < count; index++) {
ArtFrame* frame = (ArtFrame*)ptr;
@@ -1126,8 +1055,12 @@ static int artReadFrameData(unsigned char* data, File* stream, int count)
if (fileRead(ptr + sizeof(ArtFrame), frame->size, 1, stream) != 1) return -1;
ptr += sizeof(ArtFrame) + frame->size;
ptr += paddingForSize(frame->size);
padding += paddingForSize(frame->size);
}
*paddingPtr = padding;
return 0;
}
@@ -1141,7 +1074,7 @@ static int artReadHeader(Art* art, File* stream)
if (fileReadInt16List(stream, art->xOffsets, ROTATION_COUNT) == -1) return -1;
if (fileReadInt16List(stream, art->yOffsets, ROTATION_COUNT) == -1) return -1;
if (fileReadInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -1) return -1;
if (fileReadInt32(stream, &(art->field_3A)) == -1) return -1;
if (fileReadInt32(stream, &(art->dataSize)) == -1) return -1;
return 0;
}
@@ -1160,9 +1093,16 @@ int artRead(const char* path, unsigned char* data)
return -3;
}
int currentPadding = paddingForSize(sizeof(Art));
int previousPadding = 0;
for (int index = 0; index < ROTATION_COUNT; index++) {
art->padding[index] = currentPadding;
if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) {
if (artReadFrameData(data + sizeof(Art) + art->dataOffsets[index], stream, art->frameCount) != 0) {
art->padding[index] += previousPadding;
currentPadding += previousPadding;
if (artReadFrameData(data + sizeof(Art) + art->dataOffsets[index] + art->padding[index], stream, art->frameCount, &previousPadding) != 0) {
fileClose(stream);
return -5;
}
@@ -1172,3 +1112,136 @@ int artRead(const char* path, unsigned char* data)
fileClose(stream);
return 0;
}
// NOTE: Unused.
//
// 0x41A070
int artWriteFrameData(unsigned char* data, File* stream, int count)
{
unsigned char* ptr = data;
for (int index = 0; index < count; index++) {
ArtFrame* frame = (ArtFrame*)ptr;
if (fileWriteInt16(stream, frame->width) == -1) return -1;
if (fileWriteInt16(stream, frame->height) == -1) return -1;
if (fileWriteInt32(stream, frame->size) == -1) return -1;
if (fileWriteInt16(stream, frame->x) == -1) return -1;
if (fileWriteInt16(stream, frame->y) == -1) return -1;
if (fileWrite(ptr + sizeof(ArtFrame), frame->size, 1, stream) != 1) return -1;
ptr += sizeof(ArtFrame) + frame->size;
ptr += paddingForSize(frame->size);
}
return 0;
}
// NOTE: Unused.
//
// 0x41A138
int artWriteHeader(Art* art, File* stream)
{
if (fileWriteInt32(stream, art->field_0) == -1) return -1;
if (fileWriteInt16(stream, art->framesPerSecond) == -1) return -1;
if (fileWriteInt16(stream, art->actionFrame) == -1) return -1;
if (fileWriteInt16(stream, art->frameCount) == -1) return -1;
if (fileWriteInt16List(stream, art->xOffsets, ROTATION_COUNT) == -1) return -1;
if (fileWriteInt16List(stream, art->yOffsets, ROTATION_COUNT) == -1) return -1;
if (fileWriteInt32List(stream, art->dataOffsets, ROTATION_COUNT) == -1) return -1;
if (fileWriteInt32(stream, art->dataSize) == -1) return -1;
return 0;
}
// NOTE: Unused.
//
// 0x41A1E8
int artWrite(const char* path, unsigned char* data)
{
if (data == NULL) {
return -1;
}
File* stream = fileOpen(path, "wb");
if (stream == NULL) {
return -1;
}
Art* art = (Art*)data;
if (artWriteHeader(art, stream) == -1) {
fileClose(stream);
return -1;
}
for (int index = 0; index < ROTATION_COUNT; index++) {
if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) {
if (artWriteFrameData(data + sizeof(Art) + art->dataOffsets[index] + art->padding[index], stream, art->frameCount) != 0) {
fileClose(stream);
return -1;
}
}
}
fileClose(stream);
return 0;
}
static int artGetDataSize(Art* art)
{
int dataSize = sizeof(*art) + art->dataSize;
for (int index = 0; index < ROTATION_COUNT; index++) {
if (index == 0 || art->dataOffsets[index - 1] != art->dataOffsets[index]) {
// Assume worst case - every frame is unaligned and need
// max padding.
dataSize += (sizeof(int) - 1) * art->frameCount;
}
}
return dataSize;
}
static int paddingForSize(int size)
{
return (sizeof(int) - size % sizeof(int)) % sizeof(int);
}
FrmImage::FrmImage()
{
_key = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
}
FrmImage::~FrmImage()
{
unlock();
}
bool FrmImage::lock(unsigned int fid)
{
if (isLocked()) {
return false;
}
_data = artLockFrameDataReturningSize(fid, &_key, &_width, &_height);
if (!_data) {
return false;
}
return true;
}
void FrmImage::unlock()
{
if (isLocked()) {
artUnlock(_key);
_key = nullptr;
_data = nullptr;
_width = 0;
_height = 0;
}
}
} // namespace fallout

View File

@@ -8,6 +8,8 @@
#include "platform_compat.h"
#include "proto_types.h"
namespace fallout {
typedef enum Head {
HEAD_INVALID,
HEAD_MARCUS,
@@ -65,7 +67,6 @@ typedef enum Background {
BACKGROUND_COUNT,
} Background;
#pragma pack(2)
typedef struct Art {
int field_0;
short framesPerSecond;
@@ -74,9 +75,9 @@ typedef struct Art {
short xOffsets[6];
short yOffsets[6];
int dataOffsets[6];
int field_3A;
int padding[6];
int dataSize;
} Art;
#pragma pack()
typedef struct ArtFrame {
short width;
@@ -127,7 +128,7 @@ unsigned char* artLockFrameData(int fid, int frame, int direction, CacheEntry**
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 artCopyFileName(int objectType, int a2, char* a3);
int _art_get_code(int a1, int a2, char* a3, char* a4);
char* artBuildFilePath(int a1);
int artGetFramesPerSecond(Art* art);
@@ -144,8 +145,31 @@ 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 buildFid(int a1, int a2, int a3, int a4, int a5);
int artAliasFid(int fid);
int buildFid(int objectType, int frmId, int animType, int a4, int rotation);
int artRead(const char* path, unsigned char* data);
int artWrite(const char* path, unsigned char* data);
class FrmImage {
public:
FrmImage();
~FrmImage();
bool isLocked() const { return _key != nullptr; }
bool lock(unsigned int fid);
void unlock();
int getWidth() const { return _width; }
int getHeight() const { return _height; }
unsigned char* getData() const { return _data; }
private:
CacheEntry* _key;
unsigned char* _data;
int _width;
int _height;
};
} // namespace fallout
#endif

View File

@@ -1,42 +1,60 @@
#include "audio.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "db.h"
#include "debug.h"
#include "memory_manager.h"
#include "pointer_registry.h"
#include "sound.h"
#include "sound_decoder.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
namespace fallout {
static bool _defaultCompressionFunc(char* filePath);
typedef enum AudioFlags {
AUDIO_IN_USE = 0x01,
AUDIO_COMPRESSED = 0x02,
} AudioFileFlags;
typedef struct Audio {
int flags;
int stream;
SoundDecoder* soundDecoder;
int fileSize;
int sampleRate;
int channels;
int position;
} Audio;
static bool defaultCompressionFunc(char* filePath);
static int audioSoundDecoderReadHandler(int fileHandle, void* buf, unsigned int size);
// 0x5108BC
static AudioFileIsCompressedProc* _queryCompressedFunc = _defaultCompressionFunc;
static AudioQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc;
// 0x56CB00
static int gAudioListLength;
// 0x56CB04
static AudioFile* gAudioList;
static Audio* gAudioList;
// 0x41A2B0
static bool _defaultCompressionFunc(char* filePath)
static bool defaultCompressionFunc(char* filePath)
{
char* pch = strrchr(filePath, '.');
if (pch != NULL) {
strcpy(pch + 1, "war");
strcpy(pch + 1, "raw");
}
return false;
}
// 0x41A2D0
static int audioSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size)
static int audioSoundDecoderReadHandler(int handle, void* buffer, unsigned int size)
{
return fileRead(buffer, 1, size, (File*)intToPtr(fileHandle));
return fileRead(buffer, 1, size, (File*)intToPtr(handle));
}
// AudioOpen
@@ -44,10 +62,10 @@ static int audioSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned i
int audioOpen(const char* fname, int flags, ...)
{
char path[80];
sprintf(path, "%s", fname);
snprintf(path, sizeof(path), "%s", fname);
int compression;
if (_queryCompressedFunc(path)) {
if (queryCompressedFunc(path)) {
compression = 2;
} else {
compression = 0;
@@ -82,27 +100,27 @@ int audioOpen(const char* fname, int flags, ...)
int index;
for (index = 0; index < gAudioListLength; index++) {
if ((gAudioList[index].flags & AUDIO_FILE_IN_USE) == 0) {
if ((gAudioList[index].flags & AUDIO_IN_USE) == 0) {
break;
}
}
if (index == gAudioListLength) {
if (gAudioList != NULL) {
gAudioList = (AudioFile*)internal_realloc_safe(gAudioList, sizeof(*gAudioList) * (gAudioListLength + 1), __FILE__, __LINE__); // "..\int\audio.c", 216
gAudioList = (Audio*)internal_realloc_safe(gAudioList, sizeof(*gAudioList) * (gAudioListLength + 1), __FILE__, __LINE__); // "..\int\audio.c", 216
} else {
gAudioList = (AudioFile*)internal_malloc_safe(sizeof(*gAudioList), __FILE__, __LINE__); // "..\int\audio.c", 218
gAudioList = (Audio*)internal_malloc_safe(sizeof(*gAudioList), __FILE__, __LINE__); // "..\int\audio.c", 218
}
gAudioListLength++;
}
AudioFile* audioFile = &(gAudioList[index]);
audioFile->flags = AUDIO_FILE_IN_USE;
audioFile->fileHandle = ptrToInt(stream);
Audio* audioFile = &(gAudioList[index]);
audioFile->flags = AUDIO_IN_USE;
audioFile->stream = ptrToInt(stream);
if (compression == 2) {
audioFile->flags |= AUDIO_FILE_COMPRESSED;
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->flags |= AUDIO_COMPRESSED;
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
audioFile->fileSize *= 2;
} else {
audioFile->fileSize = fileGetSize(stream);
@@ -114,30 +132,30 @@ int audioOpen(const char* fname, int flags, ...)
}
// 0x41A50C
int audioClose(int fileHandle)
int audioClose(int handle)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
fileClose((File*)intToPtr(audioFile->fileHandle, true));
Audio* audioFile = &(gAudioList[handle - 1]);
fileClose((File*)intToPtr(audioFile->stream, true));
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
soundDecoderFree(audioFile->soundDecoder);
}
memset(audioFile, 0, sizeof(AudioFile));
memset(audioFile, 0, sizeof(Audio));
return 0;
}
// 0x41A574
int audioRead(int fileHandle, void* buffer, unsigned int size)
int audioRead(int handle, void* buffer, unsigned int size)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
Audio* audioFile = &(gAudioList[handle - 1]);
int bytesRead;
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
bytesRead = soundDecoderDecode(audioFile->soundDecoder, buffer, size);
} else {
bytesRead = fileRead(buffer, 1, size, (File*)intToPtr(audioFile->fileHandle));
bytesRead = fileRead(buffer, 1, size, (File*)intToPtr(audioFile->stream));
}
audioFile->position += bytesRead;
@@ -146,13 +164,13 @@ int audioRead(int fileHandle, void* buffer, unsigned int size)
}
// 0x41A5E0
long audioSeek(int fileHandle, long offset, int origin)
long audioSeek(int handle, long offset, int origin)
{
int pos;
unsigned char* buf;
int v10;
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
Audio* audioFile = &(gAudioList[handle - 1]);
switch (origin) {
case SEEK_SET:
@@ -168,11 +186,11 @@ long audioSeek(int fileHandle, long offset, int origin)
assert(false && "Should be unreachable");
}
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
if ((audioFile->flags & AUDIO_COMPRESSED) != 0) {
if (pos < audioFile->position) {
soundDecoderFree(audioFile->soundDecoder);
fileSeek((File*)intToPtr(audioFile->fileHandle), 0, SEEK_SET);
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
fileSeek((File*)intToPtr(audioFile->stream), 0, SEEK_SET);
audioFile->soundDecoder = soundDecoderInit(audioSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
audioFile->position = 0;
audioFile->fileSize *= 2;
@@ -180,11 +198,11 @@ long audioSeek(int fileHandle, long offset, int origin)
buf = (unsigned char*)internal_malloc_safe(4096, __FILE__, __LINE__); // "..\int\audio.c", 361
while (pos > 4096) {
pos -= 4096;
audioRead(fileHandle, buf, 4096);
audioRead(handle, buf, 4096);
}
if (pos != 0) {
audioRead(fileHandle, buf, pos);
audioRead(handle, buf, pos);
}
internal_free_safe(buf, __FILE__, __LINE__); // // "..\int\audio.c", 367
@@ -194,11 +212,11 @@ long audioSeek(int fileHandle, long offset, int origin)
v10 = audioFile->position - pos;
while (v10 > 1024) {
v10 -= 1024;
audioRead(fileHandle, buf, 1024);
audioRead(handle, buf, 1024);
}
if (v10 != 0) {
audioRead(fileHandle, buf, v10);
audioRead(handle, buf, v10);
}
// TODO: Probably leaks memory.
@@ -206,21 +224,21 @@ long audioSeek(int fileHandle, long offset, int origin)
return audioFile->position;
} else {
return fileSeek((File*)intToPtr(audioFile->fileHandle), offset, origin);
return fileSeek((File*)intToPtr(audioFile->stream), offset, origin);
}
}
// 0x41A78C
long audioGetSize(int fileHandle)
long audioGetSize(int handle)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
Audio* audioFile = &(gAudioList[handle - 1]);
return audioFile->fileSize;
}
// 0x41A7A8
long audioTell(int fileHandle)
long audioTell(int handle)
{
AudioFile* audioFile = &(gAudioList[fileHandle - 1]);
Audio* audioFile = &(gAudioList[handle - 1]);
return audioFile->position;
}
@@ -233,9 +251,9 @@ int audioWrite(int handle, const void* buf, unsigned int size)
}
// 0x41A7D4
int audioInit(AudioFileIsCompressedProc* isCompressedProc)
int audioInit(AudioQueryCompressedFunc* func)
{
_queryCompressedFunc = isCompressedProc;
queryCompressedFunc = func;
gAudioList = NULL;
gAudioListLength = 0;
@@ -252,3 +270,5 @@ void audioExit()
gAudioListLength = 0;
gAudioList = NULL;
}
} // namespace fallout

View File

@@ -1,16 +1,20 @@
#ifndef AUDIO_H
#define AUDIO_H
#include "audio_file.h"
namespace fallout {
typedef bool(AudioQueryCompressedFunc)(char* filePath);
int audioOpen(const char* fname, int mode, ...);
int audioClose(int fileHandle);
int audioRead(int fileHandle, void* buffer, unsigned int size);
long audioSeek(int fileHandle, long offset, int origin);
long audioGetSize(int fileHandle);
long audioTell(int fileHandle);
int audioClose(int handle);
int audioRead(int handle, void* buffer, unsigned int size);
long audioSeek(int handle, long offset, int origin);
long audioGetSize(int handle);
long audioTell(int handle);
int audioWrite(int handle, const void* buf, unsigned int size);
int audioInit(AudioFileIsCompressedProc* isCompressedProc);
int audioInit(AudioQueryCompressedFunc* func);
void audioExit();
} // namespace fallout
#endif /* AUDIO_H */

View File

@@ -6,6 +6,8 @@
#include <SDL.h>
namespace fallout {
#define AUDIO_ENGINE_SOUND_BUFFERS 8
struct AudioEngineSoundBuffer {
@@ -32,6 +34,11 @@ static SDL_AudioSpec gAudioEngineSpec;
static SDL_AudioDeviceID gAudioEngineDeviceId = -1;
static AudioEngineSoundBuffer gAudioEngineSoundBuffers[AUDIO_ENGINE_SOUND_BUFFERS];
static bool audioEngineIsInitialized()
{
return gAudioEngineDeviceId != -1;
}
static bool soundBufferIsValid(int soundBufferIndex)
{
return soundBufferIndex >= 0 && soundBufferIndex < AUDIO_ENGINE_SOUND_BUFFERS;
@@ -111,30 +118,36 @@ bool audioEngineInit()
void audioEngineExit()
{
if (gAudioEngineDeviceId != -1) {
if (audioEngineIsInitialized()) {
SDL_CloseAudioDevice(gAudioEngineDeviceId);
gAudioEngineDeviceId = -1;
}
SDL_QuitSubSystem(SDL_INIT_AUDIO);
if (SDL_WasInit(SDL_INIT_AUDIO)) {
SDL_QuitSubSystem(SDL_INIT_AUDIO);
}
}
void audioEnginePause()
{
if (gAudioEngineDeviceId != -1) {
if (audioEngineIsInitialized()) {
SDL_PauseAudioDevice(gAudioEngineDeviceId, 1);
}
}
void audioEngineResume()
{
if (gAudioEngineDeviceId != -1) {
if (audioEngineIsInitialized()) {
SDL_PauseAudioDevice(gAudioEngineDeviceId, 0);
}
}
int audioEngineCreateSoundBuffer(unsigned int size, int bitsPerSample, int channels, int rate)
{
if (!audioEngineIsInitialized()) {
return -1;
}
for (int index = 0; index < AUDIO_ENGINE_SOUND_BUFFERS; index++) {
AudioEngineSoundBuffer* soundBuffer = &(gAudioEngineSoundBuffers[index]);
std::lock_guard<std::recursive_mutex> lock(soundBuffer->mutex);
@@ -160,6 +173,10 @@ int audioEngineCreateSoundBuffer(unsigned int size, int bitsPerSample, int chann
bool audioEngineSoundBufferRelease(int soundBufferIndex)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -184,6 +201,10 @@ bool audioEngineSoundBufferRelease(int soundBufferIndex)
bool audioEngineSoundBufferSetVolume(int soundBufferIndex, int volume)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -202,6 +223,10 @@ bool audioEngineSoundBufferSetVolume(int soundBufferIndex, int volume)
bool audioEngineSoundBufferGetVolume(int soundBufferIndex, int* volumePtr)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -220,6 +245,10 @@ bool audioEngineSoundBufferGetVolume(int soundBufferIndex, int* volumePtr)
bool audioEngineSoundBufferSetPan(int soundBufferIndex, int pan)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -239,6 +268,10 @@ bool audioEngineSoundBufferSetPan(int soundBufferIndex, int pan)
bool audioEngineSoundBufferPlay(int soundBufferIndex, unsigned int flags)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -261,6 +294,10 @@ bool audioEngineSoundBufferPlay(int soundBufferIndex, unsigned int flags)
bool audioEngineSoundBufferStop(int soundBufferIndex)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -279,6 +316,10 @@ bool audioEngineSoundBufferStop(int soundBufferIndex)
bool audioEngineSoundBufferGetCurrentPosition(int soundBufferIndex, unsigned int* readPosPtr, unsigned int* writePosPtr)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -310,6 +351,10 @@ bool audioEngineSoundBufferGetCurrentPosition(int soundBufferIndex, unsigned int
bool audioEngineSoundBufferSetCurrentPosition(int soundBufferIndex, unsigned int pos)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -328,6 +373,10 @@ bool audioEngineSoundBufferSetCurrentPosition(int soundBufferIndex, unsigned int
bool audioEngineSoundBufferLock(int soundBufferIndex, unsigned int writePos, unsigned int writeBytes, void** audioPtr1, unsigned int* audioBytes1, void** audioPtr2, unsigned int* audioBytes2, unsigned int flags)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -385,6 +434,10 @@ bool audioEngineSoundBufferLock(int soundBufferIndex, unsigned int writePos, uns
bool audioEngineSoundBufferUnlock(int soundBufferIndex, void* audioPtr1, unsigned int audioBytes1, void* audioPtr2, unsigned int audioBytes2)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -403,6 +456,10 @@ bool audioEngineSoundBufferUnlock(int soundBufferIndex, void* audioPtr1, unsigne
bool audioEngineSoundBufferGetStatus(int soundBufferIndex, unsigned int* statusPtr)
{
if (!audioEngineIsInitialized()) {
return false;
}
if (!soundBufferIsValid(soundBufferIndex)) {
return false;
}
@@ -430,3 +487,5 @@ bool audioEngineSoundBufferGetStatus(int soundBufferIndex, unsigned int* statusP
return true;
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef AUDIO_ENGINE_H
#define AUDIO_ENGINE_H
namespace fallout {
#define AUDIO_ENGINE_SOUND_BUFFER_LOCK_FROM_WRITE_POS 0x00000001
#define AUDIO_ENGINE_SOUND_BUFFER_LOCK_ENTIRE_BUFFER 0x00000002
@@ -26,4 +28,6 @@ bool audioEngineSoundBufferLock(int soundBufferIndex, unsigned int writePos, uns
bool audioEngineSoundBufferUnlock(int soundBufferIndex, void* audioPtr1, unsigned int audioBytes1, void* audioPtr2, unsigned int audioBytes2);
bool audioEngineSoundBufferGetStatus(int soundBufferIndex, unsigned int* status);
} // namespace fallout
#endif /* AUDIO_ENGINE_H */

View File

@@ -1,20 +1,38 @@
#include "audio_file.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "debug.h"
#include "memory_manager.h"
#include "platform_compat.h"
#include "pointer_registry.h"
#include "sound.h"
#include "sound_decoder.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
namespace fallout {
static bool _defaultCompressionFunc__(char* filePath);
static int audioFileSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size);
typedef enum AudioFileFlags {
AUDIO_FILE_IN_USE = 0x01,
AUDIO_FILE_COMPRESSED = 0x02,
} AudioFileFlags;
typedef struct AudioFile {
int flags;
int stream;
SoundDecoder* soundDecoder;
int fileSize;
int sampleRate;
int channels;
int position;
} AudioFile;
static bool defaultCompressionFunc(char* filePath);
static int audioFileSoundDecoderReadHandler(int handle, void* buffer, unsigned int size);
// 0x5108C0
static AudioFileIsCompressedProc* _queryCompressedFunc_2 = _defaultCompressionFunc__;
static AudioFileQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc;
// 0x56CB10
static AudioFile* gAudioFileList;
@@ -23,20 +41,20 @@ static AudioFile* gAudioFileList;
static int gAudioFileListLength;
// 0x41A850
static bool _defaultCompressionFunc__(char* filePath)
static bool defaultCompressionFunc(char* filePath)
{
char* pch = strrchr(filePath, '.');
if (pch != NULL) {
strcpy(pch + 1, "war");
strcpy(pch + 1, "raw");
}
return false;
}
// 0x41A870
static int audioFileSoundDecoderReadHandler(int fileHandle, void* buffer, unsigned int size)
static int audioFileSoundDecoderReadHandler(int handle, void* buffer, unsigned int size)
{
return fread(buffer, 1, size, (FILE*)intToPtr(fileHandle));
return fread(buffer, 1, size, (FILE*)intToPtr(handle));
}
// 0x41A88C
@@ -46,7 +64,7 @@ int audioFileOpen(const char* fname, int flags, ...)
strcpy(path, fname);
int compression;
if (_queryCompressedFunc_2(path)) {
if (queryCompressedFunc(path)) {
compression = 2;
} else {
compression = 0;
@@ -96,14 +114,14 @@ int audioFileOpen(const char* fname, int flags, ...)
AudioFile* audioFile = &(gAudioFileList[index]);
audioFile->flags = AUDIO_FILE_IN_USE;
audioFile->fileHandle = ptrToInt(stream);
audioFile->stream = ptrToInt(stream);
if (compression == 2) {
audioFile->flags |= AUDIO_FILE_COMPRESSED;
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
audioFile->fileSize *= 2;
} else {
audioFile->fileSize = compat_filelength(fileno(stream));
audioFile->fileSize = getFileSize(stream);
}
audioFile->position = 0;
@@ -112,10 +130,10 @@ int audioFileOpen(const char* fname, int flags, ...)
}
// 0x41AAA0
int audioFileClose(int fileHandle)
int audioFileClose(int handle)
{
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
fclose((FILE*)intToPtr(audioFile->fileHandle, true));
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
fclose((FILE*)intToPtr(audioFile->stream, true));
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
soundDecoderFree(audioFile->soundDecoder);
@@ -128,16 +146,16 @@ int audioFileClose(int fileHandle)
}
// 0x41AB08
int audioFileRead(int fileHandle, void* buffer, unsigned int size)
int audioFileRead(int handle, void* buffer, unsigned int size)
{
AudioFile* ptr = &(gAudioFileList[fileHandle - 1]);
AudioFile* ptr = &(gAudioFileList[handle - 1]);
int bytesRead;
if ((ptr->flags & AUDIO_FILE_COMPRESSED) != 0) {
bytesRead = soundDecoderDecode(ptr->soundDecoder, buffer, size);
} else {
bytesRead = fread(buffer, 1, size, (FILE*)intToPtr(ptr->fileHandle));
bytesRead = fread(buffer, 1, size, (FILE*)intToPtr(ptr->stream));
}
ptr->position += bytesRead;
@@ -146,13 +164,13 @@ int audioFileRead(int fileHandle, void* buffer, unsigned int size)
}
// 0x41AB74
long audioFileSeek(int fileHandle, long offset, int origin)
long audioFileSeek(int handle, long offset, int origin)
{
void* buf;
int remaining;
int a4;
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
switch (origin) {
case SEEK_SET:
@@ -172,20 +190,20 @@ long audioFileSeek(int fileHandle, long offset, int origin)
if (a4 <= audioFile->position) {
soundDecoderFree(audioFile->soundDecoder);
fseek((FILE*)intToPtr(audioFile->fileHandle), 0, 0);
fseek((FILE*)intToPtr(audioFile->stream), 0, 0);
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->fileHandle, &(audioFile->field_14), &(audioFile->field_10), &(audioFile->fileSize));
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(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);
audioFileRead(handle, buf, 4096);
a4 -= 4096;
}
if (a4 != 0) {
audioFileRead(fileHandle, buf, a4);
audioFileRead(handle, buf, a4);
}
internal_free_safe(buf, __FILE__, __LINE__); // "..\int\audiof.c", 370
}
@@ -193,46 +211,46 @@ long audioFileSeek(int fileHandle, long offset, int origin)
buf = internal_malloc_safe(0x400, __FILE__, __LINE__); // "..\int\audiof.c", 316
remaining = audioFile->position - a4;
while (remaining > 1024) {
audioFileRead(fileHandle, buf, 1024);
audioFileRead(handle, buf, 1024);
remaining -= 1024;
}
if (remaining != 0) {
audioFileRead(fileHandle, buf, remaining);
audioFileRead(handle, buf, remaining);
}
// TODO: Obiously leaks memory.
}
return audioFile->position;
}
return fseek((FILE*)intToPtr(audioFile->fileHandle), offset, origin);
return fseek((FILE*)intToPtr(audioFile->stream), offset, origin);
}
// 0x41AD20
long audioFileGetSize(int fileHandle)
long audioFileGetSize(int handle)
{
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
return audioFile->fileSize;
}
// 0x41AD3C
long audioFileTell(int fileHandle)
long audioFileTell(int handle)
{
AudioFile* audioFile = &(gAudioFileList[fileHandle - 1]);
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
return audioFile->position;
}
// AudiofWrite
// 0x41AD58
int audioFileWrite(int fileHandle, const void* buffer, unsigned int size)
int audioFileWrite(int handle, const void* buffer, unsigned int size)
{
debugPrint("AudiofWrite shouldn't be ever called\n");
return 0;
}
// 0x41AD68
int audioFileInit(AudioFileIsCompressedProc* isCompressedProc)
int audioFileInit(AudioFileQueryCompressedFunc* func)
{
_queryCompressedFunc_2 = isCompressedProc;
queryCompressedFunc = func;
gAudioFileList = NULL;
gAudioFileListLength = 0;
@@ -249,3 +267,5 @@ void audioFileExit()
gAudioFileListLength = 0;
gAudioFileList = NULL;
}
} // namespace fallout

View File

@@ -1,33 +1,20 @@
#ifndef AUDIO_FILE_H
#define AUDIO_FILE_H
#include "sound_decoder.h"
namespace fallout {
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);
typedef bool(AudioFileQueryCompressedFunc)(char* filePath);
int audioFileOpen(const char* fname, int flags, ...);
int audioFileClose(int a1);
int audioFileRead(int a1, void* buf, unsigned int size);
int audioFileClose(int handle);
int audioFileRead(int handle, void* buf, unsigned int size);
long audioFileSeek(int handle, long offset, int origin);
long audioFileGetSize(int a1);
long audioFileTell(int a1);
long audioFileGetSize(int handle);
long audioFileTell(int handle);
int audioFileWrite(int handle, const void* buf, unsigned int size);
int audioFileInit(AudioFileIsCompressedProc* isCompressedProc);
int audioFileInit(AudioFileQueryCompressedFunc* func);
void audioFileExit();
} // namespace fallout
#endif /* AUDIO_FILE_H */

View File

@@ -1,30 +1,34 @@
#include "automap.h"
#include "art.h"
#include "color.h"
#include "config.h"
#include "core.h"
#include "dbox.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_config.h"
#include "game_mouse.h"
#include "game_sound.h"
#include "graph_lib.h"
#include "item.h"
#include "map.h"
#include "memory.h"
#include "object.h"
#include "platform_compat.h"
#include "text_font.h"
#include "window_manager.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "art.h"
#include "color.h"
#include "config.h"
#include "dbox.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_mouse.h"
#include "game_sound.h"
#include "graph_lib.h"
#include "input.h"
#include "item.h"
#include "kb.h"
#include "map.h"
#include "memory.h"
#include "object.h"
#include "platform_compat.h"
#include "settings.h"
#include "svga.h"
#include "text_font.h"
#include "window_manager.h"
namespace fallout {
#define AUTOMAP_OFFSET_COUNT (AUTOMAP_MAP_COUNT * ELEVATION_COUNT)
#define AUTOMAP_WINDOW_WIDTH (519)
@@ -66,7 +70,7 @@ static const int _defam[AUTOMAP_MAP_COUNT][ELEVATION_COUNT] = {
};
// 0x41B560
static const int _displayMapList[AUTOMAP_MAP_COUNT] = {
static int _displayMapList[AUTOMAP_MAP_COUNT] = {
-1,
-1,
-1,
@@ -267,12 +271,9 @@ int automapReset()
// 0x41B81C
void automapExit()
{
char* masterPatchesPath;
if (configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, &masterPatchesPath)) {
char path[COMPAT_MAX_PATH];
sprintf(path, "%s\\%s\\%s", masterPatchesPath, "MAPS", AUTOMAP_DB);
compat_remove(path);
}
char path[COMPAT_MAX_PATH];
snprintf(path, sizeof(path), "%s\\%s\\%s", settings.system.master_patches_path.c_str(), "MAPS", AUTOMAP_DB);
compat_remove(path);
}
// 0x41B87C
@@ -296,18 +297,15 @@ int _automapDisplayMap(int map)
// 0x41B8BC
void automapShow(bool isInGame, bool isUsingScanner)
{
ScopedGameMode gm(GameMode::kAutomap);
int frmIds[AUTOMAP_FRM_COUNT];
memcpy(frmIds, gAutomapFrmIds, sizeof(gAutomapFrmIds));
unsigned char* frmData[AUTOMAP_FRM_COUNT];
CacheEntry* frmHandle[AUTOMAP_FRM_COUNT];
FrmImage frmImages[AUTOMAP_FRM_COUNT];
for (int index = 0; index < AUTOMAP_FRM_COUNT; index++) {
int fid = buildFid(6, frmIds[index], 0, 0, 0);
frmData[index] = artLockFrameData(fid, 0, 0, &(frmHandle[index]));
if (frmData[index] == NULL) {
while (--index >= 0) {
artUnlock(frmHandle[index]);
}
int fid = buildFid(OBJ_TYPE_INTERFACE, frmIds[index], 0, 0, 0);
if (!frmImages[index].lock(fid)) {
return;
}
}
@@ -325,19 +323,55 @@ void automapShow(bool isInGame, bool isUsingScanner)
int automapWindowX = (screenGetWidth() - AUTOMAP_WINDOW_WIDTH) / 2;
int automapWindowY = (screenGetHeight() - AUTOMAP_WINDOW_HEIGHT) / 2;
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
int window = windowCreate(automapWindowX, automapWindowY, AUTOMAP_WINDOW_WIDTH, AUTOMAP_WINDOW_HEIGHT, color, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
int scannerBtn = buttonCreate(window, 111, 454, 15, 16, -1, -1, -1, KEY_LOWERCASE_S, frmData[AUTOMAP_FRM_BUTTON_UP], frmData[AUTOMAP_FRM_BUTTON_DOWN], NULL, BUTTON_FLAG_TRANSPARENT);
int scannerBtn = buttonCreate(window,
111,
454,
15,
16,
-1,
-1,
-1,
KEY_LOWERCASE_S,
frmImages[AUTOMAP_FRM_BUTTON_UP].getData(),
frmImages[AUTOMAP_FRM_BUTTON_DOWN].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scannerBtn != -1) {
buttonSetCallbacks(scannerBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
int cancelBtn = buttonCreate(window, 277, 454, 15, 16, -1, -1, -1, KEY_ESCAPE, frmData[AUTOMAP_FRM_BUTTON_UP], frmData[AUTOMAP_FRM_BUTTON_DOWN], NULL, BUTTON_FLAG_TRANSPARENT);
int cancelBtn = buttonCreate(window,
277,
454,
15,
16,
-1,
-1,
-1,
KEY_ESCAPE,
frmImages[AUTOMAP_FRM_BUTTON_UP].getData(),
frmImages[AUTOMAP_FRM_BUTTON_DOWN].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
buttonSetCallbacks(cancelBtn, _gsound_red_butt_press, _gsound_red_butt_release);
}
int switchBtn = buttonCreate(window, 457, 340, 42, 74, -1, -1, KEY_LOWERCASE_L, KEY_LOWERCASE_H, frmData[AUTOMAP_FRM_SWITCH_UP], frmData[AUTOMAP_FRM_SWITCH_DOWN], NULL, BUTTON_FLAG_TRANSPARENT | BUTTON_FLAG_0x01);
int switchBtn = buttonCreate(window,
457,
340,
42,
74,
-1,
-1,
KEY_LOWERCASE_L,
KEY_LOWERCASE_H,
frmImages[AUTOMAP_FRM_SWITCH_UP].getData(),
frmImages[AUTOMAP_FRM_SWITCH_DOWN].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT | BUTTON_FLAG_0x01);
if (switchBtn != -1) {
buttonSetCallbacks(switchBtn, _gsound_toggle_butt_press_, _gsound_toggle_butt_press_);
}
@@ -358,18 +392,20 @@ void automapShow(bool isInGame, bool isUsingScanner)
gAutomapFlags |= AUTOMAP_WITH_SCANNER;
}
automapRenderInMapWindow(window, elevation, frmData[AUTOMAP_FRM_BACKGROUND], gAutomapFlags);
automapRenderInMapWindow(window, elevation, frmImages[AUTOMAP_FRM_BACKGROUND].getData(), gAutomapFlags);
bool isoWasEnabled = isoDisable();
gameMouseSetCursor(MOUSE_CURSOR_ARROW);
bool done = false;
while (!done) {
sharedFpsLimiter.mark();
bool needsRefresh = false;
// FIXME: There is minor bug in the interface - pressing H/L to toggle
// high/low details does not update switch state.
int keyCode = _get_input();
int keyCode = inputGetInput();
switch (keyCode) {
case KEY_TAB:
case KEY_ESCAPE:
@@ -442,9 +478,12 @@ void automapShow(bool isInGame, bool isUsingScanner)
}
if (needsRefresh) {
automapRenderInMapWindow(window, elevation, frmData[AUTOMAP_FRM_BACKGROUND], gAutomapFlags);
automapRenderInMapWindow(window, elevation, frmImages[AUTOMAP_FRM_BACKGROUND].getData(), gAutomapFlags);
needsRefresh = false;
}
renderPresent();
sharedFpsLimiter.throttle();
}
if (isoWasEnabled) {
@@ -453,10 +492,6 @@ void automapShow(bool isInGame, bool isUsingScanner)
windowDestroy(window);
fontSetCurrent(oldFont);
for (int index = 0; index < AUTOMAP_FRM_COUNT; index++) {
artUnlock(frmHandle[index]);
}
}
// Renders automap in Map window.
@@ -482,7 +517,7 @@ static void automapRenderInMapWindow(int window, int elevation, unsigned char* b
continue;
}
int objectType = (object->fid & 0xF000000) >> 24;
int objectType = FID_TYPE(object->fid);
unsigned char objectColor;
if ((flags & AUTOMAP_IN_GAME) != 0) {
@@ -674,7 +709,7 @@ int automapSaveCurrent()
// NOTE: Not sure about the size.
char path[256];
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
File* stream1 = fileOpen(path, "r+b");
if (stream1 == NULL) {
@@ -705,7 +740,7 @@ int automapSaveCurrent()
}
if (entryOffset != 0) {
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_TMP);
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_TMP);
File* stream2 = fileOpen(path, "wb");
if (stream2 == NULL) {
@@ -799,15 +834,9 @@ int automapSaveCurrent()
internal_free(gAutomapEntry.data);
internal_free(gAutomapEntry.compressedData);
char* masterPatchesPath;
if (!configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_MASTER_PATCHES_KEY, &masterPatchesPath)) {
debugPrint("\nAUTOMAP: Error reading config info!\n");
return -1;
}
// NOTE: Not sure about the size.
char automapDbPath[512];
sprintf(automapDbPath, "%s\\%s\\%s", masterPatchesPath, "MAPS", AUTOMAP_DB);
snprintf(automapDbPath, sizeof(automapDbPath), "%s\\%s\\%s", settings.system.master_patches_path.c_str(), "MAPS", AUTOMAP_DB);
if (compat_remove(automapDbPath) != 0) {
debugPrint("\nAUTOMAP: Error removing database!\n");
return -1;
@@ -815,7 +844,7 @@ int automapSaveCurrent()
// NOTE: Not sure about the size.
char automapTmpPath[512];
sprintf(automapTmpPath, "%s\\%s\\%s", masterPatchesPath, "MAPS", AUTOMAP_TMP);
snprintf(automapTmpPath, sizeof(automapTmpPath), "%s\\%s\\%s", settings.system.master_patches_path.c_str(), "MAPS", AUTOMAP_TMP);
if (compat_rename(automapTmpPath, automapDbPath) != 0) {
debugPrint("\nAUTOMAP: Error renaming database!\n");
return -1;
@@ -902,7 +931,7 @@ static int automapLoadEntry(int map, int elevation)
gAutomapEntry.compressedData = NULL;
char path[COMPAT_MAX_PATH];
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
bool success = true;
@@ -1048,7 +1077,7 @@ static void _decode_map_data(int elevation)
if (object->tile != -1 && (object->flags & OBJECT_SEEN) != 0) {
int contentType;
int objectType = (object->fid & 0xF000000) >> 24;
int objectType = FID_TYPE(object->fid);
if (objectType == OBJ_TYPE_SCENERY && object->pid != PROTO_ID_0x2000158) {
contentType = 2;
} else if (objectType == OBJ_TYPE_WALL) {
@@ -1077,7 +1106,7 @@ static int automapCreate()
memcpy(gAutomapHeader.offsets, _defam, sizeof(_defam));
char path[COMPAT_MAX_PATH];
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
File* stream = fileOpen(path, "wb");
if (stream == NULL) {
@@ -1132,7 +1161,7 @@ static int _copy_file_data(File* stream1, File* stream2, int length)
int automapGetHeader(AutomapHeader** automapHeaderPtr)
{
char path[COMPAT_MAX_PATH];
sprintf(path, "%s\\%s", "MAPS", AUTOMAP_DB);
snprintf(path, sizeof(path), "%s\\%s", "MAPS", AUTOMAP_DB);
File* stream = fileOpen(path, "rb");
if (stream == NULL) {
@@ -1153,3 +1182,12 @@ int automapGetHeader(AutomapHeader** automapHeaderPtr)
return 0;
}
void automapSetDisplayMap(int map, bool available)
{
if (map >= 0 && map < AUTOMAP_MAP_COUNT) {
_displayMapList[map] = available ? 0 : -1;
}
}
} // namespace fallout

View File

@@ -4,6 +4,8 @@
#include "db.h"
#include "map_defs.h"
namespace fallout {
#define AUTOMAP_DB ("AUTOMAP.DB")
#define AUTOMAP_TMP ("AUTOMAP.TMP")
@@ -54,4 +56,8 @@ int automapRenderInPipboyWindow(int win, int map, int elevation);
int automapSaveCurrent();
int automapGetHeader(AutomapHeader** automapHeaderPtr);
void automapSetDisplayMap(int map, bool available);
} // namespace fallout
#endif /* AUTOMAP_H */

View File

@@ -11,6 +11,8 @@
static HANDLE gInterplayGenericAutorunMutex;
#endif
namespace fallout {
// 0x4139C0
bool autorunMutexCreate()
{
@@ -34,3 +36,5 @@ void autorunMutexClose()
}
#endif
}
} // namespace fallout

View File

@@ -1,7 +1,11 @@
#ifndef AUTORUN_H
#define AUTORUN_H
namespace fallout {
bool autorunMutexCreate();
void autorunMutexClose();
} // namespace fallout
#endif /* AUTORUN_H */

View File

@@ -1,13 +1,15 @@
#include "cache.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "debug.h"
#include "memory.h"
#include "sound.h"
#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
namespace fallout {
// The initial number of cache entries in new cache.
#define CACHE_ENTRIES_INITIAL_CAPACITY (100)
@@ -192,13 +194,13 @@ bool cacheFlush(Cache* cache)
}
// 0x42019C
bool cachePrintStats(Cache* cache, char* dest)
bool cachePrintStats(Cache* cache, char* dest, size_t size)
{
if (cache == NULL || dest == NULL) {
return false;
}
sprintf(dest, "Cache stats are disabled.%s", "\n");
snprintf(dest, size, "Cache stats are disabled.%s", "\n");
return true;
}
@@ -613,3 +615,5 @@ static int cacheEntriesCompareByMostRecentHit(const void* a1, const void* a2)
return 0;
}
}
} // namespace fallout

View File

@@ -1,8 +1,12 @@
#ifndef CACHE_H
#define CACHE_H
#include <stddef.h>
#include "heap.h"
namespace fallout {
#define INVALID_CACHE_ENTRY ((CacheEntry*)-1)
typedef enum CacheEntryFlags {
@@ -64,6 +68,8 @@ 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 cachePrintStats(Cache* cache, char* dest, size_t size);
} // namespace fallout
#endif /* CACHE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -3,6 +3,8 @@
#include "db.h"
namespace fallout {
extern int gCharacterEditorRemainingCharacterPoints;
int characterEditorShow(bool isCreationMode);
@@ -13,4 +15,6 @@ int characterEditorSave(File* stream);
int characterEditorLoad(File* stream);
void characterEditorReset();
} // namespace fallout
#endif /* CHARACTER_EDITOR_H */

View File

@@ -1,30 +1,40 @@
#include "character_selector.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <vector>
#include "art.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 "input.h"
#include "kb.h"
#include "memory.h"
#include "message.h"
#include "mouse.h"
#include "object.h"
#include "options.h"
#include "palette.h"
#include "platform_compat.h"
#include "proto.h"
#include "settings.h"
#include "sfall_config.h"
#include "skill.h"
#include "stat.h"
#include "svga.h"
#include "text_font.h"
#include "trait.h"
#include "window_manager.h"
#include <stdio.h>
#include <string.h>
namespace fallout {
#define CS_WINDOW_WIDTH (640)
#define CS_WINDOW_HEIGHT (480)
@@ -57,12 +67,28 @@
#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;
static bool characterSelectorWindowInit();
static void characterSelectorWindowFree();
static bool characterSelectorWindowRefresh();
static bool characterSelectorWindowRenderFace();
static bool characterSelectorWindowRenderStats();
static bool characterSelectorWindowRenderBio();
static bool characterSelectorWindowFatalError(bool result);
static void premadeCharactersLocalizePath(char* path);
// 0x51C84C
static int gCurrentPremadeCharacter = PREMADE_CHARACTER_NARG;
@@ -75,7 +101,7 @@ static PremadeCharacterDescription gPremadeCharacterDescriptions[PREMADE_CHARACT
};
// 0x51C8D4
static const int gPremadeCharacterCount = PREMADE_CHARACTER_COUNT;
static int gPremadeCharacterCount = PREMADE_CHARACTER_COUNT;
// 0x51C7F8
static int gCharacterSelectorWindow = -1;
@@ -89,92 +115,35 @@ static unsigned char* gCharacterSelectorBackground = NULL;
// 0x51C804
static int gCharacterSelectorWindowPreviousButton = -1;
// 0x51C808
static CacheEntry* gCharacterSelectorWindowPreviousButtonUpFrmHandle = NULL;
// 0x51C80C
static CacheEntry* gCharacterSelectorWindowPreviousButtonDownFrmHandle = NULL;
// 0x51C810
static int gCharacterSelectorWindowNextButton = -1;
// 0x51C814
static CacheEntry* gCharacterSelectorWindowNextButtonUpFrmHandle = NULL;
// 0x51C818
static CacheEntry* gCharacterSelectorWindowNextButtonDownFrmHandle = NULL;
// 0x51C81C
static int gCharacterSelectorWindowTakeButton = -1;
// 0x51C820
static CacheEntry* gCharacterSelectorWindowTakeButtonUpFrmHandle = NULL;
// 0x51C824
static CacheEntry* gCharacterSelectorWindowTakeButtonDownFrmHandle = NULL;
// 0x51C828
static int gCharacterSelectorWindowModifyButton = -1;
// 0x51C82C
static CacheEntry* gCharacterSelectorWindowModifyButtonUpFrmHandle = NULL;
// 0x51C830
static CacheEntry* gCharacterSelectorWindowModifyButtonDownFrmHandle = NULL;
// 0x51C834
static int gCharacterSelectorWindowCreateButton = -1;
// 0x51C838
static CacheEntry* gCharacterSelectorWindowCreateButtonUpFrmHandle = NULL;
// 0x51C83C
static CacheEntry* gCharacterSelectorWindowCreateButtonDownFrmHandle = NULL;
// 0x51C840
static int gCharacterSelectorWindowBackButton = -1;
// 0x51C844
static CacheEntry* gCharacterSelectorWindowBackButtonUpFrmHandle = NULL;
static FrmImage _takeButtonNormalFrmImage;
static FrmImage _takeButtonPressedFrmImage;
static FrmImage _modifyButtonNormalFrmImage;
static FrmImage _modifyButtonPressedFrmImage;
static FrmImage _createButtonNormalFrmImage;
static FrmImage _createButtonPressedFrmImage;
static FrmImage _backButtonNormalFrmImage;
static FrmImage _backButtonPressedFrmImage;
static FrmImage _nextButtonNormalFrmImage;
static FrmImage _nextButtonPressedFrmImage;
static FrmImage _previousButtonNormalFrmImage;
static FrmImage _previousButtonPressedFrmImage;
// 0x51C848
static CacheEntry* gCharacterSelectorWindowBackButtonDownFrmHandle = NULL;
// 0x667764
static unsigned char* gCharacterSelectorWindowTakeButtonUpFrmData;
// 0x667768
static unsigned char* gCharacterSelectorWindowModifyButtonDownFrmData;
// 0x66776C
static unsigned char* gCharacterSelectorWindowBackButtonUpFrmData;
// 0x667770
static unsigned char* gCharacterSelectorWindowCreateButtonUpFrmData;
// 0x667774
static unsigned char* gCharacterSelectorWindowModifyButtonUpFrmData;
// 0x667778
static unsigned char* gCharacterSelectorWindowBackButtonDownFrmData;
// 0x66777C
static unsigned char* gCharacterSelectorWindowCreateButtonDownFrmData;
// 0x667780
static unsigned char* gCharacterSelectorWindowTakeButtonDownFrmData;
// 0x667784
static unsigned char* gCharacterSelectorWindowNextButtonDownFrmData;
// 0x667788
static unsigned char* gCharacterSelectorWindowNextButtonUpFrmData;
// 0x66778C
static unsigned char* gCharacterSelectorWindowPreviousButtonUpFrmData;
// 0x667790
static unsigned char* gCharacterSelectorWindowPreviousButtonDownFrmData;
static std::vector<PremadeCharacterDescription> gCustomPremadeCharacterDescriptions;
// 0x4A71D0
int characterSelectorOpen()
@@ -194,11 +163,13 @@ int characterSelectorOpen()
int rc = 0;
bool done = false;
while (!done) {
sharedFpsLimiter.mark();
if (_game_user_wants_to_quit != 0) {
break;
}
int keyCode = _get_input();
int keyCode = inputGetInput();
switch (keyCode) {
case KEY_MINUS:
@@ -221,6 +192,8 @@ int characterSelectorOpen()
if (characterEditorShow(1) == 0) {
rc = 2;
done = true;
} else {
characterSelectorWindowRefresh();
}
break;
@@ -229,6 +202,8 @@ int characterSelectorOpen()
if (!characterEditorShow(1)) {
rc = 2;
done = true;
} else {
characterSelectorWindowRefresh();
}
break;
@@ -264,6 +239,9 @@ int characterSelectorOpen()
characterSelectorWindowRefresh();
break;
}
renderPresent();
sharedFpsLimiter.throttle();
}
paletteFadeTo(gPaletteBlack);
@@ -279,9 +257,6 @@ int characterSelectorOpen()
// 0x4A7468
static bool characterSelectorWindowInit()
{
int backgroundFid;
unsigned char* backgroundFrmData;
if (gCharacterSelectorWindow != -1) {
return false;
}
@@ -290,22 +265,21 @@ static bool characterSelectorWindowInit()
int characterSelectorWindowY = (screenGetHeight() - CS_WINDOW_HEIGHT) / 2;
gCharacterSelectorWindow = windowCreate(characterSelectorWindowX, characterSelectorWindowY, CS_WINDOW_WIDTH, CS_WINDOW_HEIGHT, _colorTable[0], 0);
if (gCharacterSelectorWindow == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowBuffer = windowGetBuffer(gCharacterSelectorWindow);
if (gCharacterSelectorWindowBuffer == NULL) {
goto err;
return characterSelectorWindowFatalError(false);
}
CacheEntry* backgroundFrmHandle;
backgroundFid = buildFid(6, 174, 0, 0, 0);
backgroundFrmData = artLockFrameData(backgroundFid, 0, 0, &backgroundFrmHandle);
if (backgroundFrmData == NULL) {
goto err;
FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 174, 0, 0, 0);
if (!backgroundFrmImage.lock(backgroundFid)) {
return characterSelectorWindowFatalError(false);
}
blitBufferToBuffer(backgroundFrmData,
blitBufferToBuffer(backgroundFrmImage.getData(),
CS_WINDOW_WIDTH,
CS_WINDOW_HEIGHT,
CS_WINDOW_WIDTH,
@@ -314,30 +288,28 @@ static bool characterSelectorWindowInit()
gCharacterSelectorBackground = (unsigned char*)internal_malloc(CS_WINDOW_BACKGROUND_WIDTH * CS_WINDOW_BACKGROUND_HEIGHT);
if (gCharacterSelectorBackground == NULL)
goto err;
return characterSelectorWindowFatalError(false);
blitBufferToBuffer(backgroundFrmData + CS_WINDOW_WIDTH * CS_WINDOW_BACKGROUND_Y + CS_WINDOW_BACKGROUND_X,
blitBufferToBuffer(backgroundFrmImage.getData() + 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);
backgroundFrmImage.unlock();
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(OBJ_TYPE_INTERFACE, 122, 0, 0, 0);
if (!_previousButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(6, 123, 0, 0, 0);
gCharacterSelectorWindowPreviousButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowPreviousButtonDownFrmHandle);
if (gCharacterSelectorWindowPreviousButtonDownFrmData == NULL) {
goto err;
fid = buildFid(OBJ_TYPE_INTERFACE, 123, 0, 0, 0);
if (!_previousButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowPreviousButton = buttonCreate(gCharacterSelectorWindow,
@@ -349,27 +321,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
500,
gCharacterSelectorWindowPreviousButtonUpFrmData,
gCharacterSelectorWindowPreviousButtonDownFrmData,
_previousButtonNormalFrmImage.getData(),
_previousButtonPressedFrmImage.getData(),
NULL,
0);
if (gCharacterSelectorWindowPreviousButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
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(OBJ_TYPE_INTERFACE, 124, 0, 0, 0);
if (!_nextButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(6, 125, 0, 0, 0);
gCharacterSelectorWindowNextButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowNextButtonDownFrmHandle);
if (gCharacterSelectorWindowNextButtonDownFrmData == NULL) {
goto err;
fid = buildFid(OBJ_TYPE_INTERFACE, 125, 0, 0, 0);
if (!_nextButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowNextButton = buttonCreate(gCharacterSelectorWindow,
@@ -381,27 +351,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
501,
gCharacterSelectorWindowNextButtonUpFrmData,
gCharacterSelectorWindowNextButtonDownFrmData,
_nextButtonNormalFrmImage.getData(),
_nextButtonPressedFrmImage.getData(),
NULL,
0);
if (gCharacterSelectorWindowNextButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
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(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!_takeButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowTakeButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowTakeButtonDownFrmHandle);
if (gCharacterSelectorWindowTakeButtonDownFrmData == NULL) {
goto err;
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!_takeButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowTakeButton = buttonCreate(gCharacterSelectorWindow,
@@ -413,26 +381,24 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_LOWERCASE_T,
gCharacterSelectorWindowTakeButtonUpFrmData,
gCharacterSelectorWindowTakeButtonDownFrmData,
_takeButtonNormalFrmImage.getData(),
_takeButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowTakeButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
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(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!_modifyButtonNormalFrmImage.lock(fid))
return characterSelectorWindowFatalError(false);
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowModifyButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowModifyButtonDownFrmHandle);
if (gCharacterSelectorWindowModifyButtonDownFrmData == NULL) {
goto err;
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!_modifyButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowModifyButton = buttonCreate(gCharacterSelectorWindow,
@@ -444,27 +410,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_LOWERCASE_M,
gCharacterSelectorWindowModifyButtonUpFrmData,
gCharacterSelectorWindowModifyButtonDownFrmData,
_modifyButtonNormalFrmImage.getData(),
_modifyButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowModifyButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
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(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!_createButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowCreateButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowCreateButtonDownFrmHandle);
if (gCharacterSelectorWindowCreateButtonDownFrmData == NULL) {
goto err;
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!_createButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowCreateButton = buttonCreate(gCharacterSelectorWindow,
@@ -476,27 +440,25 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_LOWERCASE_C,
gCharacterSelectorWindowCreateButtonUpFrmData,
gCharacterSelectorWindowCreateButtonDownFrmData,
_createButtonNormalFrmImage.getData(),
_createButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowCreateButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
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(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!_backButtonNormalFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
fid = buildFid(6, 9, 0, 0, 0);
gCharacterSelectorWindowBackButtonDownFrmData = artLockFrameData(fid, 0, 0, &gCharacterSelectorWindowBackButtonDownFrmHandle);
if (gCharacterSelectorWindowBackButtonDownFrmData == NULL) {
goto err;
fid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!_backButtonPressedFrmImage.lock(fid)) {
return characterSelectorWindowFatalError(false);
}
gCharacterSelectorWindowBackButton = buttonCreate(gCharacterSelectorWindow,
@@ -508,12 +470,12 @@ static bool characterSelectorWindowInit()
-1,
-1,
KEY_ESCAPE,
gCharacterSelectorWindowBackButtonUpFrmData,
gCharacterSelectorWindowBackButtonDownFrmData,
_backButtonNormalFrmImage.getData(),
_backButtonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (gCharacterSelectorWindowBackButton == -1) {
goto err;
return characterSelectorWindowFatalError(false);
}
buttonSetCallbacks(gCharacterSelectorWindowBackButton, _gsound_red_butt_press, _gsound_red_butt_release);
@@ -523,16 +485,10 @@ static bool characterSelectorWindowInit()
windowRefresh(gCharacterSelectorWindow);
if (!characterSelectorWindowRefresh()) {
goto err;
return characterSelectorWindowFatalError(false);
}
return true;
err:
characterSelectorWindowFree();
return false;
}
// 0x4A7AD4
@@ -547,102 +503,48 @@ static void characterSelectorWindowFree()
gCharacterSelectorWindowPreviousButton = -1;
}
if (gCharacterSelectorWindowPreviousButtonDownFrmData != NULL) {
artUnlock(gCharacterSelectorWindowPreviousButtonDownFrmHandle);
gCharacterSelectorWindowPreviousButtonDownFrmHandle = NULL;
gCharacterSelectorWindowPreviousButtonDownFrmData = NULL;
}
if (gCharacterSelectorWindowPreviousButtonUpFrmData != NULL) {
artUnlock(gCharacterSelectorWindowPreviousButtonUpFrmHandle);
gCharacterSelectorWindowPreviousButtonUpFrmHandle = NULL;
gCharacterSelectorWindowPreviousButtonUpFrmData = NULL;
}
_previousButtonNormalFrmImage.unlock();
_previousButtonPressedFrmImage.unlock();
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;
}
_nextButtonNormalFrmImage.unlock();
_nextButtonPressedFrmImage.unlock();
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;
}
_takeButtonNormalFrmImage.unlock();
_takeButtonPressedFrmImage.unlock();
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;
}
_modifyButtonNormalFrmImage.unlock();
_modifyButtonPressedFrmImage.unlock();
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;
}
_createButtonNormalFrmImage.unlock();
_createButtonPressedFrmImage.unlock();
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;
}
_backButtonNormalFrmImage.unlock();
_backButtonPressedFrmImage.unlock();
if (gCharacterSelectorBackground != NULL) {
internal_free(gCharacterSelectorBackground);
@@ -657,7 +559,9 @@ static void characterSelectorWindowFree()
static bool characterSelectorWindowRefresh()
{
char path[COMPAT_MAX_PATH];
sprintf(path, "%s.gcd", gPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
snprintf(path, sizeof(path), "%s.gcd", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
premadeCharactersLocalizePath(path);
if (_proto_dude_init(path) == -1) {
debugPrint("\n ** Error in dude init! **\n");
return false;
@@ -687,18 +591,17 @@ static 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);
FrmImage faceFrmImage;
int faceFid = buildFid(OBJ_TYPE_INTERFACE, gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].face, 0, 0, 0);
if (faceFrmImage.lock(faceFid)) {
unsigned char* data = faceFrmImage.getData();
if (data != NULL) {
int width = artGetWidth(frm, 0, 0);
int height = artGetHeight(frm, 0, 0);
int width = faceFrmImage.getWidth();
int height = faceFrmImage.getHeight();
blitBufferToBufferTrans(data, width, height, width, (gCharacterSelectorWindowBuffer + CS_WINDOW_WIDTH * 23 + 27), CS_WINDOW_WIDTH);
success = true;
}
artUnlock(faceFrmHandle);
faceFrmImage.unlock();
}
return success;
@@ -734,13 +637,13 @@ static bool characterSelectorWindowRenderStats()
value = critterGetStat(gDude, STAT_STRENGTH);
str = statGetName(STAT_STRENGTH);
sprintf(text, "%s %02d", str, value);
snprintf(text, sizeof(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);
snprintf(text, sizeof(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]);
@@ -751,13 +654,13 @@ static bool characterSelectorWindowRenderStats()
value = critterGetStat(gDude, STAT_PERCEPTION);
str = statGetName(STAT_PERCEPTION);
sprintf(text, "%s %02d", str, value);
snprintf(text, sizeof(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);
snprintf(text, sizeof(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]);
@@ -766,15 +669,15 @@ static bool characterSelectorWindowRenderStats()
y += vh;
value = critterGetStat(gDude, STAT_ENDURANCE);
str = statGetName(STAT_PERCEPTION);
str = statGetName(STAT_ENDURANCE);
sprintf(text, "%s %02d", str, value);
snprintf(text, sizeof(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);
snprintf(text, sizeof(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]);
@@ -785,13 +688,13 @@ static bool characterSelectorWindowRenderStats()
value = critterGetStat(gDude, STAT_CHARISMA);
str = statGetName(STAT_CHARISMA);
sprintf(text, "%s %02d", str, value);
snprintf(text, sizeof(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);
snprintf(text, sizeof(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]);
@@ -802,13 +705,13 @@ static bool characterSelectorWindowRenderStats()
value = critterGetStat(gDude, STAT_INTELLIGENCE);
str = statGetName(STAT_INTELLIGENCE);
sprintf(text, "%s %02d", str, value);
snprintf(text, sizeof(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);
snprintf(text, sizeof(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]);
@@ -819,13 +722,13 @@ static bool characterSelectorWindowRenderStats()
value = critterGetStat(gDude, STAT_AGILITY);
str = statGetName(STAT_AGILITY);
sprintf(text, "%s %02d", str, value);
snprintf(text, sizeof(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);
snprintf(text, sizeof(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]);
@@ -836,13 +739,13 @@ static bool characterSelectorWindowRenderStats()
value = critterGetStat(gDude, STAT_LUCK);
str = statGetName(STAT_LUCK);
sprintf(text, "%s %02d", str, value);
snprintf(text, sizeof(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);
snprintf(text, sizeof(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]);
@@ -862,7 +765,7 @@ static bool characterSelectorWindowRenderStats()
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);
snprintf(text, sizeof(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]);
@@ -877,7 +780,7 @@ static bool characterSelectorWindowRenderStats()
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);
snprintf(text, sizeof(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]);
@@ -895,7 +798,7 @@ static bool characterSelectorWindowRenderStats()
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);
snprintf(text, sizeof(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]);
@@ -910,7 +813,7 @@ static bool characterSelectorWindowRenderStats()
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);
snprintf(text, sizeof(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]);
@@ -931,7 +834,7 @@ static bool characterSelectorWindowRenderStats()
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);
snprintf(text, sizeof(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]);
@@ -963,7 +866,8 @@ static bool characterSelectorWindowRenderBio()
fontSetCurrent(101);
char path[COMPAT_MAX_PATH];
sprintf(path, "%s.bio", gPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
snprintf(path, sizeof(path), "%s.bio", gCustomPremadeCharacterDescriptions[gCurrentPremadeCharacter].fileName);
premadeCharactersLocalizePath(path);
File* stream = fileOpen(path, "rt");
if (stream != NULL) {
@@ -983,3 +887,120 @@ static bool characterSelectorWindowRenderBio()
return true;
}
// NOTE: Inlined.
//
// 0x4A8BD0
static bool characterSelectorWindowFatalError(bool result)
{
characterSelectorWindowFree();
return result;
}
void premadeCharactersInit()
{
char* fileNamesString;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FILE_NAMES_KEY, &fileNamesString);
if (fileNamesString != NULL && *fileNamesString == '\0') {
fileNamesString = NULL;
}
char* faceFidsString;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_PREMADE_CHARACTERS_FACE_FIDS_KEY, &faceFidsString);
if (faceFidsString != NULL && *faceFidsString == '\0') {
faceFidsString = NULL;
}
if (fileNamesString != NULL && faceFidsString != NULL) {
int fileNamesLength = 0;
for (char* pch = fileNamesString; pch != NULL; pch = strchr(pch + 1, ',')) {
fileNamesLength++;
}
int faceFidsLength = 0;
for (char* pch = faceFidsString; pch != NULL; pch = strchr(pch + 1, ',')) {
faceFidsLength++;
}
int premadeCharactersCount = std::min(fileNamesLength, faceFidsLength);
gCustomPremadeCharacterDescriptions.resize(premadeCharactersCount);
for (int index = 0; index < premadeCharactersCount; index++) {
char* pch;
pch = strchr(fileNamesString, ',');
if (pch != NULL) {
*pch = '\0';
}
if (strlen(fileNamesString) > 11) {
// Sfall fails here.
continue;
}
snprintf(gCustomPremadeCharacterDescriptions[index].fileName, sizeof(gCustomPremadeCharacterDescriptions[index].fileName), "premade\\%s", fileNamesString);
if (pch != NULL) {
*pch = ',';
}
fileNamesString = pch + 1;
pch = strchr(faceFidsString, ',');
if (pch != NULL) {
*pch = '\0';
}
gCustomPremadeCharacterDescriptions[index].face = atoi(faceFidsString);
if (pch != NULL) {
*pch = ',';
}
faceFidsString = pch + 1;
gCustomPremadeCharacterDescriptions[index].field_18[0] = '\0';
}
}
if (gCustomPremadeCharacterDescriptions.empty()) {
gCustomPremadeCharacterDescriptions.resize(PREMADE_CHARACTER_COUNT);
for (int index = 0; index < PREMADE_CHARACTER_COUNT; index++) {
strcpy(gCustomPremadeCharacterDescriptions[index].fileName, gPremadeCharacterDescriptions[index].fileName);
gCustomPremadeCharacterDescriptions[index].face = gPremadeCharacterDescriptions[index].face;
strcpy(gCustomPremadeCharacterDescriptions[index].field_18, gPremadeCharacterDescriptions[index].field_18);
}
}
gPremadeCharacterCount = gCustomPremadeCharacterDescriptions.size();
}
void premadeCharactersExit()
{
gCustomPremadeCharacterDescriptions.clear();
}
static void premadeCharactersLocalizePath(char* path)
{
if (compat_strnicmp(path, "premade\\", 8) != 0) {
return;
}
const char* language = settings.system.language.c_str();
if (compat_stricmp(language, ENGLISH) == 0) {
return;
}
char localizedPath[COMPAT_MAX_PATH];
strncpy(localizedPath, path, 8);
strcpy(localizedPath + 8, language);
strcpy(localizedPath + 8 + strlen(language), path + 7);
int fileSize;
if (dbGetFileSize(localizedPath, &fileSize) == 0) {
strcpy(path, localizedPath);
}
}
} // namespace fallout

View File

@@ -1,19 +1,13 @@
#ifndef CHARACTER_SELECTOR_H
#define CHARACTER_SELECTOR_H
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;
namespace fallout {
int characterSelectorOpen();
void premadeCharactersInit();
void premadeCharactersExit();
} // namespace fallout
#endif /* CHARACTER_SELECTOR_H */

View File

@@ -1,12 +1,22 @@
#include "color.h"
#include "core.h"
#include <string.h>
#include <math.h>
#include <string.h>
#include <algorithm>
#include "svga.h"
namespace fallout {
#define COLOR_PALETTE_STACK_CAPACITY 16
typedef struct ColorPaletteStackEntry {
unsigned char mappedColors[256];
unsigned char cmap[768];
unsigned char colorTable[32768];
} ColorPaletteStackEntry;
static int colorPaletteFileOpen(const char* filePath, int flags);
static int colorPaletteFileRead(int fd, void* buffer, size_t size);
static int colorPaletteFileClose(int fd);
@@ -25,6 +35,12 @@ static char _aColor_cNoError[] = "color.c: No errors\n";
// 0x50F95C
static char _aColor_cColorTa[] = "color.c: color table not found\n";
// 0x50F984
static char _aColor_cColorpa[] = "color.c: colorpalettestack overflow";
// 0x50F9AC
static char aColor_cColor_0[] = "color.c: colorpalettestack underflow";
// 0x51DF10
static char* _errorStr = _aColor_cNoError;
@@ -54,6 +70,9 @@ unsigned char _cmap[768] = {
0x3F, 0x3F, 0x3F
};
// 0x673050
static ColorPaletteStackEntry* gColorPaletteStack[COLOR_PALETTE_STACK_CAPACITY];
// 0x673090
unsigned char _systemCmap[256 * 3];
@@ -67,17 +86,20 @@ unsigned char* _blendTable[256];
unsigned char _mappedColor[256];
// 0x6738D0
unsigned char _colorMixAddTable[65536];
Color colorMixAddTable[256][256];
// 0x6838D0
unsigned char _intensityColorTable[65536];
Color intensityColorTable[256][256];
// 0x6938D0
unsigned char _colorMixMulTable[65536];
Color colorMixMulTable[256][256];
// 0x6A38D0
unsigned char _colorTable[32768];
// 0x6AB8D0
static int gColorPaletteStackSize;
// 0x6AB928
static ColorPaletteFileReadProc* gColorPaletteFileReadProc;
@@ -150,22 +172,19 @@ static void colorPaletteFreeDefaultImpl(void* ptr)
}
// 0x4C72B4
int _calculateColor(int a1, int a2)
int _calculateColor(int intensity, Color color)
{
int v1 = (a1 >> 9) + ((a2 & 0xFF) << 8);
return _intensityColorTable[v1];
return intensityColorTable[color][intensity / 512];
}
// 0x4C72E0
int _Color2RGB_(int a1)
int Color2RGB(Color c)
{
int v1, v2, v3;
int r = _cmap[3 * c] >> 1;
int g = _cmap[3 * c + 1] >> 1;
int b = _cmap[3 * c + 2] >> 1;
v1 = _cmap[3 * a1] >> 1;
v2 = _cmap[3 * a1 + 1] >> 1;
v3 = _cmap[3 * a1 + 2] >> 1;
return (((v1 << 5) | v2) << 5) | v3;
return (r << 10) | (g << 5) | b;
}
// Performs animated palette transition.
@@ -174,6 +193,8 @@ int _Color2RGB_(int a1)
void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalette, int steps)
{
for (int step = 0; step < steps; step++) {
sharedFpsLimiter.mark();
unsigned char palette[768];
for (int index = 0; index < 768; index++) {
@@ -187,9 +208,14 @@ void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalett
}
_setSystemPalette(palette);
renderPresent();
sharedFpsLimiter.throttle();
}
sharedFpsLimiter.mark();
_setSystemPalette(newPalette);
renderPresent();
sharedFpsLimiter.throttle();
}
// 0x4C73D4
@@ -237,29 +263,28 @@ void _setSystemPaletteEntries(unsigned char* palette, int start, int end)
}
// 0x4C7550
static void _setIntensityTableColor(int a1)
static void _setIntensityTableColor(int cc)
{
int v1, v2, v3, v4, v5, v6, v7, v8, v9, v10;
v5 = 0;
v10 = a1 << 8;
int shift = 0;
for (int index = 0; index < 128; index++) {
v1 = (_Color2RGB_(a1) & 0x7C00) >> 10;
v2 = (_Color2RGB_(a1) & 0x3E0) >> 5;
v3 = (_Color2RGB_(a1) & 0x1F);
int r = (Color2RGB(cc) & 0x7C00) >> 10;
int g = (Color2RGB(cc) & 0x3E0) >> 5;
int b = (Color2RGB(cc) & 0x1F);
v4 = (((v1 * v5) >> 16) << 10) | (((v2 * v5) >> 16) << 5) | ((v3 * v5) >> 16);
_intensityColorTable[index + v10] = _colorTable[v4];
int darkerR = ((r * shift) >> 16);
int darkerG = ((g * shift) >> 16);
int darkerB = ((b * shift) >> 16);
int darkerColor = (darkerR << 10) | (darkerG << 5) | darkerB;
intensityColorTable[cc][index] = _colorTable[darkerColor];
v6 = v1 + (((0x1F - v1) * v5) >> 16);
v7 = v2 + (((0x1F - v2) * v5) >> 16);
v8 = v3 + (((0x1F - v3) * v5) >> 16);
int lighterR = r + (((0x1F - r) * shift) >> 16);
int lighterG = g + (((0x1F - g) * shift) >> 16);
int lighterB = b + (((0x1F - b) * shift) >> 16);
int lighterColor = (lighterR << 10) | (lighterG << 5) | lighterB;
intensityColorTable[cc][128 + index] = _colorTable[lighterColor];
v9 = (v6 << 10) | (v7 << 5) | v8;
_intensityColorTable[0x7F + index + 1 + v10] = _colorTable[v9];
v5 += 0x200;
shift += 512;
}
}
@@ -270,7 +295,7 @@ static void _setIntensityTables()
if (_mappedColor[index] != 0) {
_setIntensityTableColor(index);
} else {
memset(_intensityColorTable + index * 256, 0, 256);
memset(intensityColorTable[index], 0, 256);
}
}
}
@@ -279,20 +304,18 @@ static void _setIntensityTables()
static 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 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);
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);
v5 = (Color2RGB(i) & 0x7C00) >> 10;
v6 = (Color2RGB(i) & 0x3E0) >> 5;
v7 = (Color2RGB(i) & 0x1F);
v8 = v2 + v5;
v9 = v3 + v6;
@@ -337,29 +360,29 @@ static void _setMixTableColor(int a1)
v12 = _calculateColor(v19, v18);
}
_colorMixAddTable[v1 + i] = v12;
colorMixAddTable[a1][i] = v12;
v20 = (_Color2RGB_(a1) & 0x7C00) >> 10;
v21 = (_Color2RGB_(a1) & 0x3E0) >> 5;
v22 = (_Color2RGB_(a1) & 0x1F);
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);
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];
colorMixMulTable[a1][i] = _colorTable[v29];
} else {
if (_mappedColor[i]) {
_colorMixAddTable[v1 + i] = i;
_colorMixMulTable[v1 + i] = i;
colorMixAddTable[a1][i] = i;
colorMixMulTable[a1][i] = i;
} else {
_colorMixAddTable[v1 + i] = a1;
_colorMixMulTable[v1 + i] = a1;
colorMixAddTable[a1][i] = a1;
colorMixMulTable[a1][i] = a1;
}
}
}
@@ -416,15 +439,15 @@ bool colorPaletteLoad(const char* path)
// NOTE: The value is "NEWC". Original code uses cmp opcode, not stricmp,
// or comparing characters one-by-one.
if (type == 0x4E455743) {
if (type == 'NEWC') {
// NOTE: Uninline.
colorPaletteFileRead(fd, _intensityColorTable, 0x10000);
colorPaletteFileRead(fd, intensityColorTable, sizeof(intensityColorTable));
// NOTE: Uninline.
colorPaletteFileRead(fd, _colorMixAddTable, 0x10000);
colorPaletteFileRead(fd, colorMixAddTable, sizeof(colorMixAddTable));
// NOTE: Uninline.
colorPaletteFileRead(fd, _colorMixMulTable, 0x10000);
colorPaletteFileRead(fd, colorMixMulTable, sizeof(colorMixMulTable));
} else {
_setIntensityTables();
@@ -457,9 +480,9 @@ static void _buildBlendTable(unsigned char* ptr, unsigned char ch)
beg = ptr;
r = (_Color2RGB_(ch) & 0x7C00) >> 10;
g = (_Color2RGB_(ch) & 0x3E0) >> 5;
b = (_Color2RGB_(ch) & 0x1F);
r = (Color2RGB(ch) & 0x7C00) >> 10;
g = (Color2RGB(ch) & 0x3E0) >> 5;
b = (Color2RGB(ch) & 0x1F);
for (i = 0; i < 256; i++) {
ptr[i] = i;
@@ -478,9 +501,9 @@ static void _buildBlendTable(unsigned char* ptr, unsigned char ch)
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);
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;
@@ -572,6 +595,60 @@ void colorSetBrightness(double value)
_setSystemPalette(_systemCmap);
}
// NOTE: Unused.
//
// 0x4C8828
bool colorPushColorPalette()
{
if (gColorPaletteStackSize >= COLOR_PALETTE_STACK_CAPACITY) {
_errorStr = _aColor_cColorpa;
return false;
}
ColorPaletteStackEntry* entry = (ColorPaletteStackEntry*)malloc(sizeof(*entry));
gColorPaletteStack[gColorPaletteStackSize] = entry;
memcpy(entry->mappedColors, _mappedColor, sizeof(_mappedColor));
memcpy(entry->cmap, _cmap, sizeof(_cmap));
memcpy(entry->colorTable, _colorTable, sizeof(_colorTable));
gColorPaletteStackSize++;
return true;
}
// NOTE: Unused.
//
// 0x4C88E0
bool colorPopColorPalette()
{
if (gColorPaletteStackSize == 0) {
_errorStr = aColor_cColor_0;
return false;
}
gColorPaletteStackSize--;
ColorPaletteStackEntry* entry = gColorPaletteStack[gColorPaletteStackSize];
memcpy(_mappedColor, entry->mappedColors, sizeof(_mappedColor));
memcpy(_cmap, entry->cmap, sizeof(_cmap));
memcpy(_colorTable, entry->colorTable, sizeof(_colorTable));
free(entry);
gColorPaletteStack[gColorPaletteStackSize] = NULL;
_setIntensityTables();
for (int index = 0; index < 256; index++) {
_setMixTableColor(index);
}
_rebuildColorBlendTables();
return true;
}
// 0x4C89CC
bool _initColors()
{
@@ -599,5 +676,11 @@ void _colorsClose()
_freeColorBlendTable(index);
}
// TODO: Incomplete.
for (int index = 0; index < gColorPaletteStackSize; index++) {
free(gColorPaletteStack[index]);
}
gColorPaletteStackSize = 0;
}
} // namespace fallout

View File

@@ -3,6 +3,9 @@
#include "memory_defs.h"
namespace fallout {
typedef unsigned char Color;
typedef const char*(ColorFileNameManger)(const char*);
typedef void(ColorTransitionCallback)();
@@ -16,14 +19,14 @@ 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 Color colorMixAddTable[256][256];
extern Color intensityColorTable[256][256];
extern Color colorMixMulTable[256][256];
extern unsigned char _colorTable[32768];
void colorPaletteSetFileIO(ColorPaletteFileOpenProc* openProc, ColorPaletteFileReadProc* readProc, ColorPaletteCloseProc* closeProc);
int _calculateColor(int a1, int a2);
int _Color2RGB_(int a1);
int _calculateColor(int intensity, Color color);
int Color2RGB(Color c);
void colorPaletteFadeBetween(unsigned char* oldPalette, unsigned char* newPalette, int steps);
void colorPaletteSetTransitionCallback(ColorTransitionCallback* callback);
void _setSystemPalette(unsigned char* palette);
@@ -35,7 +38,11 @@ unsigned char* _getColorBlendTable(int ch);
void _freeColorBlendTable(int a1);
void colorPaletteSetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc);
void colorSetBrightness(double value);
bool colorPushColorPalette();
bool colorPopColorPalette();
bool _initColors();
void _colorsClose();
} // namespace fallout
#endif /* COLOR_H */

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,13 @@
#ifndef COMBAT_H
#define COMBAT_H
#include "db.h"
#include "combat_defs.h"
#include "db.h"
#include "obj_types.h"
#include "proto_types.h"
namespace fallout {
extern int _combatNumTurns;
extern unsigned int gCombatState;
@@ -17,16 +19,16 @@ 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 _combatTestIncidentalHit(Object* a1, Object* a2, Object* a3, Object* a4);
bool _combat_safety_invalidate_weapon(Object* attacker, Object* weapon, int hitMode, Object* defender, int* safeDistancePtr);
bool _combatTestIncidentalHit(Object* attacker, Object* defender, Object* attackerFriend, Object* weapon);
Object* _combat_whose_turn();
void _combat_data_init(Object* obj);
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);
Object* aiInfoGetFriendlyDead(Object* obj);
int aiInfoSetFriendlyDead(Object* a1, Object* a2);
Object* aiInfoGetLastTarget(Object* obj);
int aiInfoSetLastTarget(Object* a1, Object* a2);
Object* aiInfoGetLastItem(Object* obj);
int aiInfoSetLastItem(Object* obj, Object* a2);
void _combat_update_critter_outline_for_los(Object* critter, bool a2);
void _combat_over_from_load();
void _combat_give_exps(int exp_points);
@@ -35,7 +37,7 @@ 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);
void _compute_explosion_on_extras(Attack* attack, int a2, int a3, int a4);
void _compute_explosion_on_extras(Attack* attack, int a2, bool isGrenade, int a4);
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);
@@ -56,9 +58,32 @@ int _combat_explode_scenery(Object* a1, Object* a2);
void _combat_delete_critter(Object* obj);
void _combatKillCritterOutsideCombat(Object* critter_obj, char* msg);
int combatGetTargetHighlight();
int criticalsGetValue(int killType, int hitLocation, int effect, int dataMember);
void criticalsSetValue(int killType, int hitLocation, int effect, int dataMember, int value);
void criticalsResetValue(int killType, int hitLocation, int effect, int dataMember);
int unarmedGetDamage(int hitMode, int* minDamagePtr, int* maxDamagePtr);
int unarmedGetBonusCriticalChance(int hitMode);
int unarmedGetActionPointCost(int hitMode);
bool unarmedIsPenetrating(int hitMode);
int unarmedGetPunchHitMode(bool isSecondary);
int unarmedGetKickHitMode(bool isSecondary);
bool unarmedIsPenetrating(int hitMode);
bool damageModGetBonusHthDamageFix();
bool damageModGetDisplayBonusDamage();
static inline bool isInCombat()
{
return (gCombatState & COMBAT_STATE_0x01) != 0;
}
static inline bool isUnarmedHitMode(int hitMode)
{
return hitMode == HIT_MODE_PUNCH
|| hitMode == HIT_MODE_KICK
|| (hitMode >= FIRST_ADVANCED_UNARMED_HIT_MODE && hitMode <= LAST_ADVANCED_UNARMED_HIT_MODE);
}
} // namespace fallout
#endif /* COMBAT_H */

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,8 @@
#include "db.h"
#include "obj_types.h"
namespace fallout {
typedef enum AiMessageType {
AI_MESSAGE_TYPE_RUN,
AI_MESSAGE_TYPE_MOVE,
@@ -42,13 +44,13 @@ int aiSetAttackWho(Object* critter, int attackWho);
int aiSetChemUse(Object* critter, int chemUse);
int aiGetDisposition(Object* obj);
int aiSetDisposition(Object* obj, int a2);
int _caiSetupTeamCombat(Object* a1, Object* a2);
int _caiTeamCombatInit(Object** a1, int a2);
int _caiSetupTeamCombat(Object* attackerTeam, Object* defenderTeam);
int _caiTeamCombatInit(Object** crittersList, int crittersListLength);
void _caiTeamCombatExit();
Object* _ai_search_inven_weap(Object* critter, int a2, Object* a3);
Object* _ai_search_inven_weap(Object* critter, bool checkRequiredActionPoints, Object* defender);
Object* _ai_search_inven_armor(Object* critter);
int _cAIPrepWeaponItem(Object* critter, Object* item);
void _cai_attempt_w_reload(Object* critter_obj, int a2);
void aiAttemptWeaponReload(Object* critter, int animate);
void _combat_ai_begin(int a1, void* a2);
void _combat_ai_over();
int _cai_perform_distance_prefs(Object* a1, Object* a2);
@@ -57,13 +59,15 @@ 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 _combatai_msg(Object* critter, Attack* attack, int type, int delay);
Object* _combat_ai_random_target(Attack* attack);
int _combatai_check_retaliation(Object* a1, Object* a2);
bool objectCanHearObject(Object* a1, Object* a2);
void _combatai_check_retaliation(Object* a1, Object* a2);
bool isWithinPerception(Object* a1, Object* a2);
void aiMessageListReloadIfNeeded();
void _combatai_notify_onlookers(Object* a1);
void _combatai_notify_friends(Object* a1);
void _combatai_delete_critter(Object* obj);
} // namespace fallout
#endif /* COMBAT_AI_H */

View File

@@ -1,6 +1,8 @@
#ifndef COMBAT_AI_DEFS_H
#define COMBAT_AI_DEFS_H
namespace fallout {
typedef enum AreaAttackMode {
AREA_ATTACK_MODE_ALWAYS,
AREA_ATTACK_MODE_SOMETIMES,
@@ -79,4 +81,6 @@ typedef enum HurtTooMuch {
HURT_COUNT,
} HurtTooMuch;
} // namespace fallout
#endif /* COMBAT_AI_DEFS_H */

View File

@@ -10,6 +10,8 @@
#define WEAPON_CRITICAL_FAILURE_TYPE_COUNT (7)
#define WEAPON_CRITICAL_FAILURE_EFFECT_COUNT (5)
namespace fallout {
typedef enum CombatState {
COMBAT_STATE_0x01 = 0x01,
COMBAT_STATE_0x02 = 0x02,
@@ -84,13 +86,6 @@ typedef enum HitLocation {
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;
@@ -128,25 +123,54 @@ typedef struct Attack {
int extrasKnockback[EXPLOSION_TARGET_COUNT];
} Attack;
typedef enum CriticalHitDescriptionDataMember {
CRIT_DATA_MEMBER_DAMAGE_MULTIPLIER,
CRIT_DATA_MEMBER_FLAGS,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_STAT_MODIFIER,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_FLAGS,
CRIT_DATA_MEMBER_MESSAGE_ID,
CRIT_DATA_MEMBER_MASSIVE_CRITICAL_MESSAGE_ID,
CRIT_DATA_MEMBER_COUNT,
} CriticalHitDescriptionDataMember;
// Provides metadata about critical hit effect.
typedef struct CriticalHitDescription {
int damageMultiplier;
typedef union CriticalHitDescription {
struct {
int damageMultiplier;
// Damage flags that will be applied to defender.
int flags;
// 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;
// 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;
// Bonus/penalty to massive critical stat.
int massiveCriticalStatModifier;
// Additional damage flags if this critical hit become massive critical.
int massiveCriticalFlags;
// Additional damage flags if this critical hit become massive critical.
int massiveCriticalFlags;
int messageId;
int massiveCriticalMessageId;
int messageId;
int massiveCriticalMessageId;
};
// SFALL: Allow indexed access to the data above.
int values[CRIT_DATA_MEMBER_COUNT];
} CriticalHitDescription;
typedef enum CombatBadShot {
COMBAT_BAD_SHOT_OK = 0,
COMBAT_BAD_SHOT_NO_AMMO = 1,
COMBAT_BAD_SHOT_OUT_OF_RANGE = 2,
COMBAT_BAD_SHOT_NOT_ENOUGH_AP = 3,
COMBAT_BAD_SHOT_ALREADY_DEAD = 4,
COMBAT_BAD_SHOT_AIM_BLOCKED = 5,
COMBAT_BAD_SHOT_ARM_CRIPPLED = 6,
COMBAT_BAD_SHOT_BOTH_ARMS_CRIPPLED = 7,
} CombatBadShot;
} // namespace fallout
#endif /* COMBAT_DEFS_H */

View File

@@ -1,9 +1,5 @@
#include "config.h"
#include "db.h"
#include "memory.h"
#include "platform_compat.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
@@ -11,6 +7,12 @@
#include <stdlib.h>
#include <string.h>
#include "db.h"
#include "memory.h"
#include "platform_compat.h"
namespace fallout {
#define CONFIG_FILE_MAX_LINE_LENGTH (256)
// The initial number of sections (or key-value) pairs in the config.
@@ -81,25 +83,20 @@ bool configParseCommandLineArguments(Config* config, int argc, char** argv)
}
for (int arg = 0; arg < argc; arg++) {
char* pch = argv[arg];
char* pch;
char* string = argv[arg];
// Find opening bracket.
while (*pch != '\0' && *pch != '[') {
pch++;
}
if (*pch == '\0') {
pch = strchr(string, '[');
if (pch == NULL) {
continue;
}
char* sectionKey = pch + 1;
// Find closing bracket.
while (*pch != '\0' && *pch != ']') {
pch++;
}
if (*pch == '\0') {
pch = strchr(sectionKey, ']');
if (pch == NULL) {
continue;
}
@@ -189,7 +186,7 @@ bool configSetString(Config* config, const char* sectionKey, const char* key, co
}
// 0x42C05C
bool configGetInt(Config* config, const char* sectionKey, const char* key, int* valuePtr, unsigned char base /* = 0 */ )
bool configGetInt(Config* config, const char* sectionKey, const char* key, int* valuePtr, unsigned char base /* = 0 */)
{
if (valuePtr == NULL) {
return false;
@@ -233,36 +230,32 @@ bool configGetIntList(Config* config, const char* sectionKey, const char* key, i
}
char temp[CONFIG_FILE_MAX_LINE_LENGTH];
strncpy(temp, string, CONFIG_FILE_MAX_LINE_LENGTH - 1);
string = 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;
}
while (1) {
char* pch = strchr(string, ',');
if (pch == NULL) {
break;
}
pch++;
count--;
if (count == 0) {
break;
}
*pch = '\0';
*arr++ = atoi(string);
string = pch + 1;
}
if (count <= 1) {
*arr = atoi(beginning);
// SFALL: Fix getting last item in a list if the list has less than the
// requested number of values (for `chem_primary_desire`).
if (count > 0) {
*arr = atoi(string);
count--;
}
return true;
return count == 0;
}
// 0x42C160
@@ -383,30 +376,34 @@ static 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 = strchr(string, ';');
if (pch != NULL) {
*pch = '\0';
}
// Find opening bracket.
pch = string;
while (*pch != '\0' && *pch != '[') {
pch++;
// CE: Original implementation treats any line with brackets as section key.
// The problem can be seen when loading Olympus settings (ddraw.ini), which
// contains the following line:
//
// ```ini
// VersionString=Olympus 2207 [Complete].
// ```
//
// It thinks that [Complete] is a start of new section, and puts remaining
// keys there.
// Skip leading whitespace.
while (isspace(*string)) {
string++;
}
if (*pch == '[') {
char* sectionKey = pch + 1;
// Check if it's a section key.
if (*string == '[') {
char* sectionKey = string + 1;
// Find closing bracket.
while (*pch != '\0' && *pch != ']') {
pch++;
}
if (*pch == ']') {
pch = strchr(sectionKey, ']');
if (pch != NULL) {
*pch = '\0';
strcpy(gConfigLastSectionKey, sectionKey);
return configTrimString(gConfigLastSectionKey);
@@ -435,12 +432,8 @@ static bool configParseKeyValue(char* string, char* key, char* value)
}
// Find equals character.
char* pch = string;
while (*pch != '\0' && *pch != '=') {
pch++;
}
if (*pch == '\0') {
char* pch = strchr(string, '=');
if (pch == NULL) {
return false;
}
@@ -495,7 +488,7 @@ static bool configTrimString(char* string)
return false;
}
int length = strlen(string);
size_t length = strlen(string);
if (length == 0) {
return true;
}
@@ -546,7 +539,7 @@ bool configGetDouble(Config* config, const char* sectionKey, const char* key, do
bool configSetDouble(Config* config, const char* sectionKey, const char* key, double value)
{
char stringValue[32];
sprintf(stringValue, "%.6f", value);
snprintf(stringValue, sizeof(stringValue), "%.6f", value);
return configSetString(config, sectionKey, key, stringValue);
}
@@ -573,3 +566,5 @@ bool configSetBool(Config* config, const char* sectionKey, const char* key, bool
{
return configSetInt(config, sectionKey, key, value ? 1 : 0);
}
} // namespace fallout

View File

@@ -3,6 +3,8 @@
#include "dictionary.h"
namespace fallout {
// A representation of .INI file.
//
// It's implemented as a [Dictionary] whos keys are section names of .INI file,
@@ -31,4 +33,6 @@ bool configSetDouble(Config* config, const char* sectionKey, const char* key, do
bool configGetBool(Config* config, const char* sectionKey, const char* key, bool* valuePtr);
bool configSetBool(Config* config, const char* sectionKey, const char* key, bool value);
} // namespace fallout
#endif /* CONFIG_H */

View File

@@ -1,639 +0,0 @@
#ifndef CORE_H
#define CORE_H
#include "db.h"
#include "dinput.h"
#include "geometry.h"
#include "window.h"
#include <SDL.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;
typedef struct LogicalKeyEntry {
short field_0;
short unmodified;
short shift;
short lmenu;
short rmenu;
short ctrl;
} LogicalKeyEntry;
typedef struct KeyboardEvent {
int 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 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 int gNormalizedQwertyKeys[SDL_NUM_SCANCODES];
extern InputEvent gInputEventQueue[40];
extern STRUCT_6ABF50 _GNW95_key_time_stamps[SDL_NUM_SCANCODES];
extern int _input_mx;
extern int _input_my;
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[SDL_NUM_SCANCODES];
extern unsigned char gPressedPhysicalKeys[SDL_NUM_SCANCODES];
extern unsigned int _kb_idle_start_time;
extern KeyboardEvent gLastKeyboardEvent;
extern int gKeyboardLayout;
extern unsigned char gPressedPhysicalKeysCount;
extern SDL_Window* gSdlWindow;
extern SDL_Surface* gSdlSurface;
extern SDL_Renderer* gSdlRenderer;
extern SDL_Texture* gSdlTexture;
extern SDL_Surface* gSdlTextureSurface;
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();
int _GNW95_input_init();
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 width, int height, bool fullscreen);
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(KeyboardData* data);
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);
void _vcr_stop();
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);
int screenGetWidth();
int screenGetHeight();
int screenGetVisibleHeight();
void mouseGetPositionInWindow(int win, int* x, int* y);
bool mouseHitTestInWindow(int win, int left, int top, int right, int bottom);
#endif /* CORE_H */

View File

@@ -1,25 +1,29 @@
#include "credits.h"
#include <string.h>
#include <algorithm>
#include "art.h"
#include "color.h"
#include "core.h"
#include "cycle.h"
#include "db.h"
#include "debug.h"
#include "draw.h"
#include "game_mouse.h"
#include "input.h"
#include "memory.h"
#include "message.h"
#include "mouse.h"
#include "palette.h"
#include "platform_compat.h"
#include "sound.h"
#include "svga.h"
#include "text_font.h"
#include "window_manager.h"
#include <string.h>
namespace fallout {
#define CREDITS_WINDOW_WIDTH (640)
#define CREDITS_WINDOW_HEIGHT (480)
#define CREDITS_WINDOW_SCROLLING_DELAY (38)
static bool creditsFileParseNextLine(char* dest, int* font, int* color);
@@ -61,7 +65,7 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
soundContinueAll();
char localizedPath[COMPAT_MAX_PATH];
if (_message_make_path(localizedPath, filePath)) {
if (_message_make_path(localizedPath, sizeof(localizedPath), filePath)) {
gCreditsFile = fileOpen(localizedPath, "rt");
if (gCreditsFile != NULL) {
soundContinueAll();
@@ -74,39 +78,35 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
mouseShowCursor();
}
int creditsWindowX = (screenGetWidth() - CREDITS_WINDOW_WIDTH) / 2;
int creditsWindowY = (screenGetHeight() - CREDITS_WINDOW_HEIGHT) / 2;
int window = windowCreate(creditsWindowX, creditsWindowY, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_HEIGHT, _colorTable[0], 20);
int windowWidth = screenGetWidth();
int windowHeight = screenGetHeight();
int window = windowCreate(0, 0, windowWidth, windowHeight, _colorTable[0], 20);
soundContinueAll();
if (window != -1) {
unsigned char* windowBuffer = windowGetBuffer(window);
if (windowBuffer != NULL) {
unsigned char* backgroundBuffer = (unsigned char*)internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
unsigned char* backgroundBuffer = (unsigned char*)internal_malloc(windowWidth * windowHeight);
if (backgroundBuffer) {
soundContinueAll();
memset(backgroundBuffer, _colorTable[0], CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
memset(backgroundBuffer, _colorTable[0], windowWidth * windowHeight);
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);
FrmImage backgroundFrmImage;
if (backgroundFrmImage.lock(backgroundFid)) {
blitBufferToBuffer(backgroundFrmImage.getData(),
backgroundFrmImage.getWidth(),
backgroundFrmImage.getHeight(),
backgroundFrmImage.getWidth(),
backgroundBuffer + windowWidth * ((windowHeight - backgroundFrmImage.getHeight()) / 2) + (windowWidth - backgroundFrmImage.getWidth()) / 2,
windowWidth);
backgroundFrmImage.unlock();
}
}
unsigned char* intermediateBuffer = (unsigned char*)internal_malloc(CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
unsigned char* intermediateBuffer = (unsigned char*)internal_malloc(windowWidth * windowHeight);
if (intermediateBuffer != NULL) {
memset(intermediateBuffer, 0, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT);
memset(intermediateBuffer, 0, windowWidth * windowHeight);
fontSetCurrent(gCreditsWindowTitleFont);
int titleFontLineHeight = fontGetLineHeight();
@@ -114,22 +114,21 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
fontSetCurrent(gCreditsWindowNameFont);
int nameFontLineHeight = fontGetLineHeight();
int lineHeight = nameFontLineHeight + (titleFontLineHeight >= nameFontLineHeight ? titleFontLineHeight - nameFontLineHeight : 0);
int stringBufferSize = CREDITS_WINDOW_WIDTH * lineHeight;
int lineHeight = std::max(titleFontLineHeight, nameFontLineHeight);
int stringBufferSize = windowWidth * lineHeight;
unsigned char* stringBuffer = (unsigned char*)internal_malloc(stringBufferSize);
if (stringBuffer != NULL) {
blitBufferToBuffer(backgroundBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowWidth,
windowHeight,
windowWidth,
windowBuffer,
CREDITS_WINDOW_WIDTH);
windowWidth);
windowRefresh(window);
paletteFadeTo(_cmap);
unsigned char* v40 = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH;
char str[260];
int font;
int color;
@@ -138,47 +137,52 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
while (creditsFileParseNextLine(str, &font, &color)) {
fontSetCurrent(font);
int v19 = fontGetStringWidth(str);
if (v19 >= CREDITS_WINDOW_WIDTH) {
int stringWidth = fontGetStringWidth(str);
if (stringWidth >= windowWidth) {
continue;
}
memset(stringBuffer, 0, stringBufferSize);
fontDrawText(stringBuffer, str, CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH, color);
fontDrawText(stringBuffer, str, windowWidth, windowWidth, color);
unsigned char* dest = intermediateBuffer + CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH + (CREDITS_WINDOW_WIDTH - v19) / 2;
unsigned char* dest = intermediateBuffer + windowWidth * windowHeight - windowWidth + (windowWidth - stringWidth) / 2;
unsigned char* src = stringBuffer;
for (int index = 0; index < lineHeight; index++) {
if (_get_input() != -1) {
sharedFpsLimiter.mark();
if (inputGetInput() != -1) {
stop = true;
break;
}
memmove(intermediateBuffer, intermediateBuffer + CREDITS_WINDOW_WIDTH, CREDITS_WINDOW_WIDTH * CREDITS_WINDOW_HEIGHT - CREDITS_WINDOW_WIDTH);
memcpy(dest, src, v19);
memmove(intermediateBuffer, intermediateBuffer + windowWidth, windowWidth * windowHeight - windowWidth);
memcpy(dest, src, stringWidth);
blitBufferToBuffer(backgroundBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowWidth,
windowHeight,
windowWidth,
windowBuffer,
CREDITS_WINDOW_WIDTH);
windowWidth);
blitBufferToBufferTrans(intermediateBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowWidth,
windowHeight,
windowWidth,
windowBuffer,
CREDITS_WINDOW_WIDTH);
windowWidth);
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
}
tick = _get_time();
tick = getTicks();
windowRefresh(window);
src += CREDITS_WINDOW_WIDTH;
src += windowWidth;
sharedFpsLimiter.throttle();
renderPresent();
}
if (stop) {
@@ -187,34 +191,39 @@ void creditsOpen(const char* filePath, int backgroundFid, bool useReversedStyle)
}
if (!stop) {
for (int index = 0; index < CREDITS_WINDOW_HEIGHT; index++) {
if (_get_input() != -1) {
for (int index = 0; index < windowHeight; index++) {
sharedFpsLimiter.mark();
if (inputGetInput() != -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);
memmove(intermediateBuffer, intermediateBuffer + windowWidth, windowWidth * windowHeight - windowWidth);
memset(intermediateBuffer + windowWidth * windowHeight - windowWidth, 0, windowWidth);
blitBufferToBuffer(backgroundBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowWidth,
windowHeight,
windowWidth,
windowBuffer,
CREDITS_WINDOW_WIDTH);
windowWidth);
blitBufferToBufferTrans(intermediateBuffer,
CREDITS_WINDOW_WIDTH,
CREDITS_WINDOW_HEIGHT,
CREDITS_WINDOW_WIDTH,
windowWidth,
windowHeight,
windowWidth,
windowBuffer,
CREDITS_WINDOW_WIDTH);
windowWidth);
while (getTicksSince(tick) < CREDITS_WINDOW_SCROLLING_DELAY) {
}
tick = _get_time();
tick = getTicks();
windowRefresh(window);
sharedFpsLimiter.throttle();
renderPresent();
}
}
@@ -274,3 +283,5 @@ static bool creditsFileParseNextLine(char* dest, int* font, int* color)
return false;
}
} // namespace fallout

View File

@@ -1,6 +1,10 @@
#ifndef CREDITS_H
#define CREDITS_H
namespace fallout {
void creditsOpen(const char* path, int fid, bool useReversedStyle);
} // namespace fallout
#endif /* CREDITS_H */

View File

@@ -1,5 +1,8 @@
#include "critter.h"
#include <stdio.h>
#include <string.h>
#include "animation.h"
#include "art.h"
#include "character_editor.h"
@@ -26,10 +29,9 @@
#include "stat.h"
#include "tile.h"
#include "trait.h"
#include "world_map.h"
#include "worldmap.h"
#include <stdio.h>
#include <string.h>
namespace fallout {
// Maximum length of dude's name length.
#define DUDE_NAME_MAX_LENGTH (32)
@@ -68,6 +70,7 @@ typedef enum RadiationLevel {
} RadiationLevel;
static int _get_rad_damage_level(Object* obj, void* data);
static int critter_kill_count_clear();
static int _critterClearObjDrugs(Object* obj, void* data);
// 0x50141C
@@ -159,7 +162,8 @@ int critterInit()
{
dudeResetName();
memset(gKillsByType, 0, sizeof(gKillsByType));
// NOTE: Uninline;
critter_kill_count_clear();
if (!messageListInit(&gCritterMessageList)) {
debugPrint("\nError: Initing critter name message file!");
@@ -167,13 +171,15 @@ int critterInit()
}
char path[COMPAT_MAX_PATH];
sprintf(path, "%sscrname.msg", asc_5186C8);
snprintf(path, sizeof(path), "%sscrname.msg", asc_5186C8);
if (!messageListLoad(&gCritterMessageList, path)) {
debugPrint("\nError: Loading critter name message file!");
return -1;
}
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRNAME, &gCritterMessageList);
return 0;
}
@@ -181,12 +187,15 @@ int critterInit()
void critterReset()
{
dudeResetName();
memset(gKillsByType, 0, sizeof(gKillsByType));
// NOTE: Uninline;
critter_kill_count_clear();
}
// 0x42D004
void critterExit()
{
messageListRepositorySetStandardMessageList(STANDARD_MESSAGE_LIST_SCRNAME, nullptr);
messageListFree(&gCritterMessageList);
}
@@ -276,18 +285,18 @@ void dudeResetName()
// 0x42D18C
int critterGetHitPoints(Object* critter)
{
return (critter->pid >> 24) == OBJ_TYPE_CRITTER ? critter->data.critter.hp : 0;
return PID_TYPE(critter->pid) == OBJ_TYPE_CRITTER ? critter->data.critter.hp : 0;
}
// 0x42D1A4
int critterAdjustHitPoints(Object* critter, int a2)
int critterAdjustHitPoints(Object* critter, int hp)
{
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return 0;
}
int maximumHp = critterGetStat(critter, STAT_MAXIMUM_HIT_POINTS);
int newHp = critter->data.critter.hp + a2;
int newHp = critter->data.critter.hp + hp;
critter->data.critter.hp = newHp;
if (maximumHp >= newHp) {
@@ -304,7 +313,7 @@ int critterAdjustHitPoints(Object* critter, int a2)
// 0x42D1F8
int critterGetPoison(Object* critter)
{
return (critter->pid >> 24) == OBJ_TYPE_CRITTER ? critter->data.critter.poison : 0;
return PID_TYPE(critter->pid) == OBJ_TYPE_CRITTER ? critter->data.critter.poison : 0;
}
// Adjust critter's current poison by specified amount.
@@ -396,7 +405,7 @@ int poisonEventProcess(Object* obj, void* data)
// 0x42D38C
int critterGetRadiation(Object* obj)
{
return (obj->pid >> 24) == OBJ_TYPE_CRITTER ? obj->data.critter.radiation : 0;
return PID_TYPE(obj->pid) == OBJ_TYPE_CRITTER ? obj->data.critter.radiation : 0;
}
// 0x42D3A4
@@ -416,7 +425,7 @@ int critterAdjustRadiation(Object* obj, int amount)
}
if (amount > 0) {
proto->critter.data.flags |= 0x02;
proto->critter.data.flags |= CRITTER_RADIATED;
}
if (amount > 0) {
@@ -483,7 +492,7 @@ int _critter_check_rads(Object* obj)
Proto* proto;
protoGetProto(obj->pid, &proto);
if ((proto->critter.data.flags & 0x02) == 0) {
if ((proto->critter.data.flags & CRITTER_RADIATED) == 0) {
return 0;
}
@@ -521,10 +530,10 @@ int _critter_check_rads(Object* obj)
radiationEvent->radiationLevel = radiationLevel;
radiationEvent->isHealing = 0;
queueAddEvent(36000 * randomBetween(4, 18), obj, radiationEvent, EVENT_TYPE_RADIATION);
queueAddEvent(GAME_TIME_TICKS_PER_HOUR * randomBetween(4, 18), obj, radiationEvent, EVENT_TYPE_RADIATION);
}
proto->critter.data.flags &= ~(0x02);
proto->critter.data.flags &= ~CRITTER_RADIATED;
return 0;
}
@@ -568,6 +577,13 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing)
if (obj == gDude) {
// Radiation level message, higher is worse.
messageListItem.num = 1000 + radiationLevelIndex;
// SFALL: Fix radiation message when removing radiation effects.
if (isHealing) {
// You feel better.
messageListItem.num = 3003;
}
if (messageListGetItem(&gMiscMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
}
@@ -579,15 +595,18 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing)
critterSetBonusStat(obj, gRadiationEffectStats[effect], value);
}
if ((obj->data.critter.combat.results & DAM_DEAD) == 0) {
// Loop thru effects affecting primary stats. If any of the primary stat
// dropped below minimal value, kill it.
for (int effect = 0; effect < RADIATION_EFFECT_PRIMARY_STAT_COUNT; effect++) {
int base = critterGetBaseStatWithTraitModifier(obj, gRadiationEffectStats[effect]);
int bonus = critterGetBonusStat(obj, gRadiationEffectStats[effect]);
if (base + bonus < PRIMARY_STAT_MIN) {
critterKill(obj, -1, 1);
break;
// SFALL: Prevent death when removing radiation effects.
if (!isHealing) {
if ((obj->data.critter.combat.results & DAM_DEAD) == 0) {
// Loop thru effects affecting primary stats. If any of the primary stat
// dropped below minimal value, kill it.
for (int effect = 0; effect < RADIATION_EFFECT_PRIMARY_STAT_COUNT; effect++) {
int base = critterGetBaseStatWithTraitModifier(obj, gRadiationEffectStats[effect]);
int bonus = critterGetBonusStat(obj, gRadiationEffectStats[effect]);
if (base + bonus < PRIMARY_STAT_MIN) {
critterKill(obj, -1, 1);
break;
}
}
}
}
@@ -597,7 +616,8 @@ void _process_rads(Object* obj, int radiationLevel, bool isHealing)
// You have died from radiation sickness.
messageListItem.num = 1006;
if (messageListGetItem(&gMiscMessageList, &messageListItem)) {
displayMonitorAddMessage(messageListItem.text);
// SFALL: Display a pop-up message box about death from radiation.
gameShowDeathDialog(messageListItem.text);
}
}
}
@@ -657,7 +677,7 @@ int radiationEventWrite(File* stream, void* data)
// 0x42D82C
int critterGetDamageType(Object* obj)
{
if ((obj->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) {
return 0;
}
@@ -669,6 +689,15 @@ int critterGetDamageType(Object* obj)
return proto->critter.data.damageType;
}
// NOTE: Inlined.
//
// 0x42D860
static int critter_kill_count_clear()
{
memset(gKillsByType, 0, sizeof(gKillsByType));
return 0;
}
// 0x42D878
int killsIncByType(int killType)
{
@@ -723,7 +752,7 @@ int critterGetKillType(Object* obj)
return KILL_TYPE_MAN;
}
if ((obj->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) {
return -1;
}
@@ -766,7 +795,7 @@ char* killTypeGetDescription(int killType)
// 0x42D9F4
int _critter_heal_hours(Object* critter, int a2)
{
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return -1;
}
@@ -788,7 +817,7 @@ static int _critterClearObjDrugs(Object* obj, void* data)
// 0x42DA64
void critterKill(Object* critter, int anim, bool a3)
{
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return;
}
@@ -800,20 +829,20 @@ void critterKill(Object* critter, int anim, bool a3)
bool shouldChangeFid = false;
int fid;
if (_critter_is_prone(critter)) {
int current = (critter->fid & 0xFF0000) >> 16;
int current = FID_ANIM_TYPE(critter->fid);
if (current == ANIM_FALL_BACK || current == ANIM_FALL_FRONT) {
bool back = false;
if (current == ANIM_FALL_BACK) {
back = true;
} else {
fid = buildFid(1, critter->fid & 0xFFF, ANIM_FALL_FRONT_SF, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, ANIM_FALL_FRONT_SF, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
if (!artExists(fid)) {
back = true;
}
}
if (back) {
fid = buildFid(1, critter->fid & 0xFFF, ANIM_FALL_BACK_SF, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, ANIM_FALL_BACK_SF, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
}
shouldChangeFid = true;
@@ -828,12 +857,12 @@ void critterKill(Object* critter, int anim, bool a3)
anim = LAST_SF_DEATH_ANIM;
}
fid = buildFid(1, critter->fid & 0xFFF, anim, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, anim, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
_obj_fix_violence_settings(&fid);
if (!artExists(fid)) {
debugPrint("\nError: Critter Kill: Can't match fid!");
fid = buildFid(1, critter->fid & 0xFFF, ANIM_FALL_BACK_BLOOD_SF, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
fid = buildFid(OBJ_TYPE_CRITTER, critter->fid & 0xFFF, ANIM_FALL_BACK_BLOOD_SF, (critter->fid & 0xF000) >> 12, critter->rotation + 1);
_obj_fix_violence_settings(&fid);
}
@@ -850,7 +879,7 @@ void critterKill(Object* critter, int anim, bool a3)
rectUnion(&updatedRect, &tempRect, &updatedRect);
}
if (!_critter_flag_check(critter->pid, 2048)) {
if (!_critter_flag_check(critter->pid, CRITTER_FLAT)) {
critter->flags |= OBJECT_NO_BLOCK;
_obj_toggle_flat(critter, &tempRect);
}
@@ -873,7 +902,7 @@ void critterKill(Object* critter, int anim, bool a3)
_critterClearObj = critter;
_queue_clear_type(EVENT_TYPE_DRUG, _critterClearObjDrugs);
_item_destroy_all_hidden(critter);
itemDestroyAllHidden(critter);
if (a3) {
tileWindowRefreshRect(&updatedRect, elevation);
@@ -902,7 +931,7 @@ bool critterIsActive(Object* critter)
return false;
}
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return false;
}
@@ -924,7 +953,7 @@ bool critterIsDead(Object* critter)
return false;
}
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return false;
}
@@ -946,7 +975,7 @@ bool critterIsCrippled(Object* critter)
return false;
}
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return false;
}
@@ -960,15 +989,15 @@ bool _critter_is_prone(Object* critter)
return false;
}
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return false;
}
int anim = (critter->fid & 0xFF0000) >> 16;
int anim = FID_ANIM_TYPE(critter->fid);
return (critter->data.critter.combat.results & (DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN)) != 0
|| anim >= FIRST_KNOCKDOWN_AND_DEATH_ANIM && anim <= LAST_KNOCKDOWN_AND_DEATH_ANIM
|| anim >= FIRST_SF_DEATH_ANIM && anim <= LAST_SF_DEATH_ANIM;
|| (anim >= FIRST_KNOCKDOWN_AND_DEATH_ANIM && anim <= LAST_KNOCKDOWN_AND_DEATH_ANIM)
|| (anim >= FIRST_SF_DEATH_ANIM && anim <= LAST_SF_DEATH_ANIM);
}
// critter_body_type
@@ -980,7 +1009,7 @@ int critterGetBodyType(Object* critter)
return 0;
}
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return 0;
}
@@ -1225,7 +1254,7 @@ int knockoutEventProcess(Object* obj, void* data)
obj->data.critter.combat.results |= DAM_KNOCKED_DOWN;
if (isInCombat()) {
obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_0x01;
obj->data.critter.combat.maneuver |= CRITTER_MANEUVER_ENGAGING;
} else {
_dude_standup(obj);
}
@@ -1236,7 +1265,7 @@ int knockoutEventProcess(Object* obj, void* data)
// 0x42E460
int _critter_wake_clear(Object* obj, void* data)
{
if ((obj->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(obj->pid) != OBJ_TYPE_CRITTER) {
return 0;
}
@@ -1246,7 +1275,7 @@ int _critter_wake_clear(Object* obj, void* data)
obj->data.critter.combat.results &= ~(DAM_KNOCKED_OUT | DAM_KNOCKED_DOWN);
int fid = buildFid((obj->fid & 0xF000000) >> 24, obj->fid & 0xFFF, ANIM_STAND, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
int fid = buildFid(FID_TYPE(obj->fid), obj->fid & 0xFFF, ANIM_STAND, (obj->fid & 0xF000) >> 12, obj->rotation + 1);
objectSetFid(obj, fid, 0);
return 0;
@@ -1259,12 +1288,12 @@ int _critter_set_who_hit_me(Object* a1, Object* a2)
return -1;
}
if (a2 != NULL && ((a2->fid & 0xF000000) >> 24) != OBJ_TYPE_CRITTER) {
if (a2 != NULL && FID_TYPE(a2->fid) != OBJ_TYPE_CRITTER) {
return -1;
}
if ((a1->pid >> 24) == OBJ_TYPE_CRITTER) {
if (a2 == NULL || a1->data.critter.combat.team != a2->data.critter.combat.team || statRoll(a1, STAT_INTELLIGENCE, -1, NULL) < 2 && (!objectIsPartyMember(a1) || !objectIsPartyMember(a2))) {
if (PID_TYPE(a1->pid) == OBJ_TYPE_CRITTER) {
if (a2 == NULL || a1->data.critter.combat.team != a2->data.critter.combat.team || (statRoll(a1, STAT_INTELLIGENCE, -1, NULL) < 2 && (!objectIsPartyMember(a1) || !objectIsPartyMember(a2)))) {
a1->data.critter.combat.whoHitMe = a2;
if (a2 == gDude) {
reactionSetValue(a1, -3);
@@ -1279,7 +1308,7 @@ int _critter_set_who_hit_me(Object* a1, Object* a2)
bool _critter_can_obj_dude_rest()
{
bool v1 = false;
if (!_wmMapCanRestHere(gElevation)) {
if (!wmMapCanRestHere(gElevation)) {
v1 = true;
}
@@ -1319,7 +1348,7 @@ bool _critter_can_obj_dude_rest()
// 0x42E62C
int critterGetMovementPointCostAdjustedForCrippledLegs(Object* critter, int actionPoints)
{
if ((critter->pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(critter->pid) != OBJ_TYPE_CRITTER) {
return 0;
}
@@ -1358,7 +1387,7 @@ bool _critter_flag_check(int pid, int flag)
return false;
}
if ((pid >> 24) != OBJ_TYPE_CRITTER) {
if (PID_TYPE(pid) != OBJ_TYPE_CRITTER) {
return false;
}
@@ -1366,3 +1395,5 @@ bool _critter_flag_check(int pid, int flag)
protoGetProto(pid, &proto);
return (proto->critter.data.flags & flag) != 0;
}
} // namespace fallout

View File

@@ -5,6 +5,8 @@
#include "obj_types.h"
#include "proto_types.h"
namespace fallout {
typedef enum DudeState {
DUDE_STATE_SNEAKING = 0,
DUDE_STATE_LEVEL_UP_AVAILABLE = 3,
@@ -21,7 +23,7 @@ void critterProtoDataCopy(CritterProtoData* dest, CritterProtoData* src);
int dudeSetName(const char* name);
void dudeResetName();
int critterGetHitPoints(Object* critter);
int critterAdjustHitPoints(Object* critter, int amount);
int critterAdjustHitPoints(Object* critter, int hp);
int critterGetPoison(Object* critter);
int critterAdjustPoison(Object* obj, int amount);
int poisonEventProcess(Object* obj, void* data);
@@ -69,4 +71,6 @@ bool critterIsEncumbered(Object* critter);
bool critterIsFleeing(Object* a1);
bool _critter_flag_check(int pid, int flag);
} // namespace fallout
#endif /* CRITTER_H */

View File

@@ -1,14 +1,17 @@
#include "cycle.h"
#include "color.h"
#include "core.h"
#include "game_config.h"
#include "input.h"
#include "palette.h"
#include "settings.h"
#include "svga.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)
namespace fallout {
static constexpr unsigned int kSlowCyclePeriod = 1000 / 5;
static constexpr unsigned int kMediumCyclePeriod = 1000 / 7;
static constexpr unsigned int kFastCyclePeriod = 1000 / 10;
static constexpr unsigned int kVeryFastCyclePeriod = 1000 / 30;
// 0x51843C
static int gColorCycleSpeedFactor = 1;
@@ -19,7 +22,7 @@ static int gColorCycleSpeedFactor = 1;
// Green.
//
// 0x518440
static unsigned char _slime[12] = {
static unsigned char slime[12] = {
0, 108, 0,
11, 115, 7,
27, 123, 15,
@@ -29,7 +32,7 @@ static unsigned char _slime[12] = {
// Light gray?
//
// 0x51844C
static unsigned char _shoreline[18] = {
static unsigned char shoreline[18] = {
83, 63, 43,
75, 59, 43,
67, 55, 39,
@@ -41,7 +44,7 @@ static unsigned char _shoreline[18] = {
// Orange.
//
// 0x51845E
static unsigned char _fire_slow[15] = {
static unsigned char fire_slow[15] = {
255, 0, 0,
215, 0, 0,
147, 43, 11,
@@ -52,7 +55,7 @@ static unsigned char _fire_slow[15] = {
// Red.
//
// 0x51846D
static unsigned char _fire_fast[15] = {
static unsigned char fire_fast[15] = {
71, 0, 0,
123, 0, 0,
179, 0, 0,
@@ -63,7 +66,7 @@ static unsigned char _fire_fast[15] = {
// Light blue.
//
// 0x51847C
static unsigned char _monitors[15] = {
static unsigned char monitors[15] = {
107, 107, 111,
99, 103, 127,
87, 107, 143,
@@ -79,38 +82,17 @@ static bool gColorCycleInitialized = false;
// 0x518490
static bool gColorCycleEnabled = false;
// 0x518494
static int _slime_start = 0;
// 0x518498
static int _shoreline_start = 0;
// 0x51849C
static int _fire_slow_start = 0;
// 0x5184A0
static int _fire_fast_start = 0;
// 0x5184A4
static int _monitors_start = 0;
// 0x5184A8
static unsigned char _bobber_red = 0;
// 0x5184A9
static signed char _bobber_diff = -4;
// 0x56D7D0
static unsigned int gColorCycleTimestamp3;
static unsigned int last_cycle_fast;
// 0x56D7D4
static unsigned int gColorCycleTimestamp1;
static unsigned int last_cycle_slow;
// 0x56D7D8
static unsigned int gColorCycleTimestamp2;
static unsigned int last_cycle_medium;
// 0x56D7DC
static unsigned int gColorCycleTimestamp4;
static unsigned int last_cycle_very_fast;
// 0x42E780
void colorCycleInit()
@@ -119,33 +101,28 @@ void colorCycleInit()
return;
}
bool colorCycling;
if (!configGetBool(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_COLOR_CYCLING_KEY, &colorCycling)) {
colorCycling = true;
}
if (!colorCycling) {
if (!settings.system.color_cycling) {
return;
}
for (int index = 0; index < 12; index++) {
_slime[index] >>= 2;
slime[index] >>= 2;
}
for (int index = 0; index < 18; index++) {
_shoreline[index] >>= 2;
shoreline[index] >>= 2;
}
for (int index = 0; index < 15; index++) {
_fire_slow[index] >>= 2;
fire_slow[index] >>= 2;
}
for (int index = 0; index < 15; index++) {
_fire_fast[index] >>= 2;
fire_fast[index] >>= 2;
}
for (int index = 0; index < 15; index++) {
_monitors[index] >>= 2;
monitors[index] >>= 2;
}
tickersAdd(colorCycleTicker);
@@ -153,22 +130,17 @@ void colorCycleInit()
gColorCycleInitialized = true;
gColorCycleEnabled = true;
int cycleSpeedFactor;
if (!configGetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, &cycleSpeedFactor)) {
cycleSpeedFactor = 1;
}
cycleSetSpeedFactor(cycleSpeedFactor);
cycleSetSpeedFactor(settings.system.cycle_speed_factor);
}
// 0x42E8CC
void colorCycleReset()
{
if (gColorCycleInitialized) {
gColorCycleTimestamp1 = 0;
gColorCycleTimestamp2 = 0;
gColorCycleTimestamp3 = 0;
gColorCycleTimestamp4 = 0;
last_cycle_slow = 0;
last_cycle_medium = 0;
last_cycle_fast = 0;
last_cycle_very_fast = 0;
tickersAdd(colorCycleTicker);
gColorCycleEnabled = true;
}
@@ -206,12 +178,33 @@ bool colorCycleEnabled()
void cycleSetSpeedFactor(int value)
{
gColorCycleSpeedFactor = value;
configSetInt(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_CYCLE_SPEED_FACTOR_KEY, value);
settings.system.cycle_speed_factor = value;
}
// 0x42E97C
void colorCycleTicker()
{
// 0x518494
static int slime_start = 0;
// 0x518498
static int shoreline_start = 0;
// 0x51849C
static int fire_slow_start = 0;
// 0x5184A0
static int fire_fast_start = 0;
// 0x5184A4
static int monitors_start = 0;
// 0x5184A8
static unsigned char bobber_red = 0;
// 0x5184A9
static signed char bobber_diff = -4;
if (!gColorCycleEnabled) {
return;
}
@@ -219,111 +212,111 @@ void colorCycleTicker()
bool changed = false;
unsigned char* palette = _getSystemPalette();
unsigned int time = _get_time();
unsigned int time = getTicks();
if (getTicksBetween(time, gColorCycleTimestamp1) >= COLOR_CYCLE_PERIOD_1 * gColorCycleSpeedFactor) {
if (getTicksBetween(time, last_cycle_slow) >= kSlowCyclePeriod * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp1 = time;
last_cycle_slow = time;
int paletteIndex = 229 * 3;
for (int index = _slime_start; index < 12; index++) {
palette[paletteIndex++] = _slime[index];
for (int index = slime_start; index < 12; index++) {
palette[paletteIndex++] = slime[index];
}
for (int index = 0; index < _slime_start; 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;
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 = shoreline_start; index < 18; index++) {
palette[paletteIndex++] = shoreline[index];
}
for (int index = 0; index < _shoreline_start; 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;
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 = 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];
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;
fire_slow_start -= 3;
if (fire_slow_start < 0) {
fire_slow_start = 12;
}
}
if (getTicksBetween(time, gColorCycleTimestamp2) >= COLOR_CYCLE_PERIOD_2 * gColorCycleSpeedFactor) {
if (getTicksBetween(time, last_cycle_medium) >= kMediumCyclePeriod * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp2 = time;
last_cycle_medium = time;
int paletteIndex = 243 * 3;
for (int index = _fire_fast_start; index < 15; index++) {
palette[paletteIndex++] = _fire_fast[index];
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];
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;
fire_fast_start -= 3;
if (fire_fast_start < 0) {
fire_fast_start = 12;
}
}
if (getTicksBetween(time, gColorCycleTimestamp3) >= COLOR_CYCLE_PERIOD_3 * gColorCycleSpeedFactor) {
if (getTicksBetween(time, last_cycle_fast) >= kFastCyclePeriod * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp3 = time;
last_cycle_fast = time;
int paletteIndex = 233 * 3;
for (int index = _monitors_start; index < 15; index++) {
palette[paletteIndex++] = _monitors[index];
for (int index = monitors_start; index < 15; index++) {
palette[paletteIndex++] = monitors[index];
}
for (int index = 0; index < _monitors_start; index++) {
palette[paletteIndex++] = _monitors[index];
for (int index = 0; index < monitors_start; index++) {
palette[paletteIndex++] = monitors[index];
}
_monitors_start -= 3;
monitors_start -= 3;
if (_monitors_start < 0) {
_monitors_start = 12;
if (monitors_start < 0) {
monitors_start = 12;
}
}
if (getTicksBetween(time, gColorCycleTimestamp4) >= COLOR_CYCLE_PERIOD_4 * gColorCycleSpeedFactor) {
if (getTicksBetween(time, last_cycle_very_fast) >= kVeryFastCyclePeriod * gColorCycleSpeedFactor) {
changed = true;
gColorCycleTimestamp4 = time;
last_cycle_very_fast = time;
if (_bobber_red == 0 || _bobber_red == 60) {
_bobber_diff = -_bobber_diff;
if (bobber_red == 0 || bobber_red == 60) {
bobber_diff = -bobber_diff;
}
_bobber_red += _bobber_diff;
bobber_red += bobber_diff;
int paletteIndex = 254 * 3;
palette[paletteIndex++] = _bobber_red;
palette[paletteIndex++] = bobber_red;
palette[paletteIndex++] = 0;
palette[paletteIndex++] = 0;
}
@@ -332,3 +325,5 @@ void colorCycleTicker()
paletteSetEntriesInRange(palette + 229 * 3, 229, 255);
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef CYCLE_H
#define CYCLE_H
namespace fallout {
void colorCycleInit();
void colorCycleReset();
void colorCycleFree();
@@ -10,4 +12,6 @@ bool colorCycleEnabled();
void cycleSetSpeedFactor(int value);
void colorCycleTicker();
} // namespace fallout
#endif /* CYCLE_H */

View File

@@ -1,10 +1,199 @@
#include "datafile.h"
#include <string.h>
#include "color.h"
#include "db.h"
#include "memory_manager.h"
#include "pcx.h"
#include "platform_compat.h"
namespace fallout {
// 0x5184AC
DatafileLoader* gDatafileLoader = NULL;
// 0x5184B0
DatafileNameMangler* gDatafileNameMangler = datafileDefaultNameManglerImpl;
// 0x56D7E0
unsigned char _pal[768];
unsigned char gDatafilePalette[768];
// 0x42EE70
char* datafileDefaultNameManglerImpl(char* path)
{
return path;
}
// NOTE: Unused.
//
// 0x42EE74
void datafileSetNameMangler(DatafileNameMangler* mangler)
{
gDatafileNameMangler = mangler;
}
// NOTE: Unused.
//
// 0x42EE7C
void datafileSetLoader(DatafileLoader* loader)
{
gDatafileLoader = loader;
}
// 0x42EE84
void sub_42EE84(unsigned char* data, unsigned char* palette, int width, int height)
{
unsigned char indexedPalette[256];
indexedPalette[0] = 0;
for (int index = 1; index < 256; index++) {
// TODO: Check.
int r = palette[index * 3 + 2] >> 3;
int g = palette[index * 3 + 1] >> 3;
int b = palette[index * 3] >> 3;
int colorTableIndex = (r << 10) | (g << 5) | b;
indexedPalette[index] = _colorTable[colorTableIndex];
}
int size = width * height;
for (int index = 0; index < size; index++) {
data[index] = indexedPalette[data[index]];
}
}
// NOTE: Unused.
//
// 0x42EEF8
void sub_42EEF8(unsigned char* data, unsigned char* palette, int width, int height)
{
unsigned char indexedPalette[256];
indexedPalette[0] = 0;
for (int index = 1; index < 256; index++) {
// TODO: Check.
int r = palette[index * 3 + 2] >> 1;
int g = palette[index * 3 + 1] >> 1;
int b = palette[index * 3] >> 1;
int colorTableIndex = (r << 10) | (g << 5) | b;
indexedPalette[index] = _colorTable[colorTableIndex];
}
int size = width * height;
for (int index = 0; index < size; index++) {
data[index] = indexedPalette[data[index]];
}
}
// 0x42EF60
unsigned char* datafileReadRaw(char* path, int* widthPtr, int* heightPtr)
{
char* mangledPath = gDatafileNameMangler(path);
char* dot = strrchr(mangledPath, '.');
if (dot != NULL) {
if (compat_stricmp(dot + 1, "pcx") == 0) {
return pcxRead(mangledPath, widthPtr, heightPtr, gDatafilePalette);
}
}
if (gDatafileLoader != NULL) {
return gDatafileLoader(mangledPath, gDatafilePalette, widthPtr, heightPtr);
}
return NULL;
}
// 0x42EFCC
unsigned char* datafileRead(char* path, int* widthPtr, int* heightPtr)
{
unsigned char* v1 = datafileReadRaw(path, widthPtr, heightPtr);
if (v1 != NULL) {
sub_42EE84(v1, gDatafilePalette, *widthPtr, *heightPtr);
}
return v1;
}
// NOTE: Unused
//
// 0x42EFF4
unsigned char* sub_42EFF4(char* path)
{
int width;
int height;
unsigned char* v3 = datafileReadRaw(path, &width, &height);
if (v3 != NULL) {
internal_free_safe(v3, __FILE__, __LINE__); // "..\\int\\DATAFILE.C", 148
return gDatafilePalette;
}
return NULL;
}
// NOTE: Unused.
//
// 0x42F024
void sub_42F024(unsigned char* data, int* widthPtr, int* heightPtr)
{
int width = *widthPtr;
int height = *heightPtr;
unsigned char* temp = (unsigned char*)internal_malloc_safe(width * height, __FILE__, __LINE__); // "..\\int\\DATAFILE.C", 157
// NOTE: Original code does not initialize `x`.
int y = 0;
int x = 0;
unsigned char* src1 = data;
for (y = 0; y < height; y++) {
if (*src1 == 0) {
break;
}
unsigned char* src2 = src1;
for (x = 0; x < width; x++) {
if (*src2 == 0) {
break;
}
*temp++ = *src2++;
}
src1 += width;
}
memcpy(data, temp, x * y);
internal_free_safe(temp, __FILE__, __LINE__); // // "..\\int\\DATAFILE.C", 171
}
// 0x42F0E4
unsigned char* _datafileGetPalette()
unsigned char* datafileGetPalette()
{
return _pal;
return gDatafilePalette;
}
// NOTE: Unused.
//
// 0x42F0EC
unsigned char* datafileLoad(char* path, int* sizePtr)
{
const char* mangledPath = gDatafileNameMangler(path);
File* stream = fileOpen(mangledPath, "rb");
if (stream == NULL) {
return NULL;
}
int size = fileGetSize(stream);
unsigned char* data = (unsigned char*)internal_malloc_safe(size, __FILE__, __LINE__); // "..\\int\\DATAFILE.C", 185
if (data == NULL) {
// NOTE: This code is unreachable, internal_malloc_safe never fails.
// Otherwise it leaks stream.
*sizePtr = 0;
return NULL;
}
fileRead(data, 1, size, stream);
fileClose(stream);
*sizePtr = size;
return data;
}
} // namespace fallout

View File

@@ -1,8 +1,28 @@
#ifndef DATAFILE_H
#define DATAFILE_H
extern unsigned char _pal[768];
namespace fallout {
unsigned char* _datafileGetPalette();
typedef unsigned char*(DatafileLoader)(char* path, unsigned char* palette, int* widthPtr, int* heightPtr);
typedef char*(DatafileNameMangler)(char* path);
extern DatafileLoader* gDatafileLoader;
extern DatafileNameMangler* gDatafileNameMangler;
extern unsigned char gDatafilePalette[768];
char* datafileDefaultNameManglerImpl(char* path);
void datafileSetNameMangler(DatafileNameMangler* mangler);
void datafileSetLoader(DatafileLoader* loader);
void sub_42EE84(unsigned char* data, unsigned char* palette, int width, int height);
void sub_42EEF8(unsigned char* data, unsigned char* palette, int width, int height);
unsigned char* datafileReadRaw(char* path, int* widthPtr, int* heightPtr);
unsigned char* datafileRead(char* path, int* widthPtr, int* heightPtr);
unsigned char* sub_42EFF4(char* path);
void sub_42F024(unsigned char* data, int* widthPtr, int* heightPtr);
unsigned char* datafileGetPalette();
unsigned char* datafileLoad(char* path, int* sizePtr);
} // namespace fallout
#endif /* DATAFILE_H */

View File

@@ -1,12 +1,14 @@
#include "db.h"
#include "platform_compat.h"
#include "xfile.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "platform_compat.h"
#include "xfile.h"
namespace fallout {
typedef struct FileList {
XList xlist;
struct FileList* next;
@@ -61,22 +63,12 @@ int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4)
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)
// 0x4C5D58
int _db_total()
{
return 0;
}
// 0x4C5D58
bool _db_total()
{
return true;
}
// 0x4C5D60
void dbExit()
{
@@ -334,7 +326,7 @@ int fileReadInt32(File* stream, int* valuePtr)
return -1;
}
*valuePtr = ((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000);
*valuePtr = ((value & 0xFF000000) >> 24) | ((value & 0xFF0000) >> 8) | ((value & 0xFF00) << 8) | ((value & 0xFF) << 24);
return 0;
}
@@ -507,7 +499,7 @@ int fileReadInt32List(File* stream, int* arr, int count)
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);
arr[index] = ((value & 0xFF000000) >> 24) | ((value & 0xFF0000) >> 8) | ((value & 0xFF00) << 8) | ((value & 0xFF) << 24);
}
return 0;
@@ -638,31 +630,23 @@ int fileNameListInit(const char* pattern, char*** fileNameListPtr, int a3, int a
}
}
bool v1 = *pattern == '*';
bool isWildcard = *pattern == '*';
for (int index = 0; index < fileNamesLength; index += 1) {
const char* name = xlist->fileNames[index];
char* name = xlist->fileNames[index];
char dir[COMPAT_MAX_DIR];
char fileName[COMPAT_MAX_FNAME];
char extension[COMPAT_MAX_EXT];
compat_windows_path_to_native(name);
compat_splitpath(name, NULL, dir, fileName, extension);
bool v2 = false;
if (v1) {
char* pch = dir;
while (*pch != '\0' && *pch != '\\') {
pch++;
}
v2 = *pch != '\0';
}
if (!v2) {
if (!isWildcard || *dir == '\0' || (strchr(dir, '\\') == NULL && strchr(dir, '/') == NULL)) {
// NOTE: Quick and dirty fix to buffer overflow. See RE to
// understand the problem.
char path[COMPAT_MAX_PATH];
sprintf(path, "%s%s", fileName, extension);
snprintf(path, sizeof(path), "%s%s", fileName, extension);
free(xlist->fileNames[length]);
xlist->fileNames[length] = strdup(path);
xlist->fileNames[length] = compat_strdup(path);
length++;
}
}
@@ -704,14 +688,6 @@ void fileNameListFree(char*** fileNameListPtr, int a2)
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
@@ -732,16 +708,10 @@ void fileSetReadProgressHandler(FileReadProgressHandler* handler, int size)
}
}
// 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 compat_stricmp(*(const char**)p1, *(const char**)p2);
}
} // namespace fallout

View File

@@ -1,18 +1,18 @@
#ifndef DB_H
#define DB_H
#include "memory_defs.h"
#include <stddef.h>
#include "xfile.h"
#include <stddef.h>
namespace fallout {
typedef XFile File;
typedef void FileReadProgressHandler();
typedef char* StrdupProc(const char* string);
int dbOpen(const char* filePath1, int a2, const char* filePath2, int a4);
int _db_current(int a1);
bool _db_total();
int _db_total();
void dbExit();
int dbGetFileSize(const char* filePath, int* sizePtr);
int dbGetFileContents(const char* filePath, void* ptr);
@@ -60,9 +60,9 @@ 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_();
} // namespace fallout
#endif /* DB_H */

View File

@@ -1,24 +1,29 @@
#include "dbox.h"
#include "art.h"
#include "character_editor.h"
#include "color.h"
#include "core.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_sound.h"
#include "message.h"
#include "platform_compat.h"
#include "text_font.h"
#include "window_manager.h"
#include "word_wrap.h"
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include "art.h"
#include "character_editor.h"
#include "color.h"
#include "debug.h"
#include "draw.h"
#include "game.h"
#include "game_sound.h"
#include "input.h"
#include "kb.h"
#include "message.h"
#include "mouse.h"
#include "platform_compat.h"
#include "svga.h"
#include "text_font.h"
#include "window_manager.h"
#include "word_wrap.h"
namespace fallout {
#define FILE_DIALOG_LINE_COUNT 12
#define FILE_DIALOG_DOUBLE_CLICK_DELAY 32
@@ -173,7 +178,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
int linesCount = 0;
for (int index = 0; index < bodyLength; index++) {
// NOTE: Calls [fontGetStringWidth] twice because of [max] macro.
// NOTE: Originally there is no `max` macro.
maximumLineWidth = std::max(fontGetStringWidth(body[index]), maximumLineWidth);
linesCount++;
}
@@ -193,94 +198,76 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
: 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) {
FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, gDialogBoxBackgroundFrmIds[dialogType], 0, 0, 0);
if (!backgroundFrmImage.lock(backgroundFid)) {
fontSetCurrent(savedFont);
return -1;
}
// Maintain original position in original resolution, otherwise center it.
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundFrmImage.getWidth()) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundFrmImage.getHeight()) / 2;
int win = windowCreate(x,
y,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getHeight(),
256,
WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
if (win == -1) {
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
return -1;
}
unsigned char* windowBuf = windowGetBuffer(win);
memcpy(windowBuf, background, backgroundWidth * backgroundHeight);
memcpy(windowBuf, backgroundFrmImage.getData(), backgroundFrmImage.getWidth() * backgroundFrmImage.getHeight());
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;
FrmImage doneBoxFrmImage;
FrmImage buttonNormalFrmImage;
FrmImage buttonPressedFrmImage;
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);
int doneBoxFid = buildFid(OBJ_TYPE_INTERFACE, 209, 0, 0, 0);
if (!doneBoxFrmImage.lock(doneBoxFid)) {
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);
int pressedFid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!buttonPressedFrmImage.lock(pressedFid)) {
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);
int normalFid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!buttonNormalFrmImage.lock(normalFid)) {
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);
int v27 = hasTwoButtons
? _doneX[dialogType]
: (backgroundFrmImage.getWidth() - doneBoxFrmImage.getWidth()) / 2;
blitBufferToBuffer(doneBoxFrmImage.getData(),
doneBoxFrmImage.getWidth(),
doneBoxFrmImage.getHeight(),
doneBoxFrmImage.getWidth(),
windowBuf + backgroundFrmImage.getWidth() * _doneY[dialogType] + v27,
backgroundFrmImage.getWidth());
if (!messageListInit(&messageList)) {
artUnlock(upButtonHandle);
artUnlock(downButtonHandle);
artUnlock(doneBoxHandle);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
windowDestroy(win);
return -1;
}
char path[COMPAT_MAX_PATH];
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
snprintf(path, sizeof(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;
@@ -292,10 +279,26 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
// 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]);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * (_doneY[dialogType] + 3) + v27 + 35,
messageListItem.text,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
_colorTable[18979]);
}
int btn = buttonCreate(win, v27 + 13, _doneY[dialogType] + 4, downButtonWidth, downButtonHeight, -1, -1, -1, 500, upButton, downButton, NULL, BUTTON_FLAG_TRANSPARENT);
int btn = buttonCreate(win,
v27 + 13,
_doneY[dialogType] + 4,
buttonPressedFrmImage.getWidth(),
buttonPressedFrmImage.getHeight(),
-1,
-1,
-1,
500,
buttonNormalFrmImage.getData(),
buttonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
buttonSetCallbacks(btn, _gsound_red_butt_press, _gsound_red_butt_release);
}
@@ -311,102 +314,95 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
fontSetCurrent(103);
blitBufferToBufferTrans(doneBox,
doneBoxWidth,
doneBoxHeight,
doneBoxWidth,
windowBuf + backgroundWidth * _doneY[dialogType] + _doneX[dialogType] + doneBoxWidth + 24,
backgroundWidth);
blitBufferToBufferTrans(doneBoxFrmImage.getData(),
doneBoxFrmImage.getWidth(),
doneBoxFrmImage.getHeight(),
doneBoxFrmImage.getWidth(),
windowBuf + backgroundFrmImage.getWidth() * _doneY[dialogType] + _doneX[dialogType] + doneBoxFrmImage.getWidth() + 24,
backgroundFrmImage.getWidth());
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + _doneX[dialogType] + doneBoxWidth + 59,
a8, backgroundWidth, backgroundWidth, _colorTable[18979]);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * (_doneY[dialogType] + 3) + _doneX[dialogType] + doneBoxFrmImage.getWidth() + 59,
a8,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
_colorTable[18979]);
int btn = buttonCreate(win,
doneBoxWidth + _doneX[dialogType] + 37,
doneBoxFrmImage.getWidth() + _doneX[dialogType] + 37,
_doneY[dialogType] + 4,
downButtonWidth,
downButtonHeight,
-1, -1, -1, 501, upButton, downButton, 0, BUTTON_FLAG_TRANSPARENT);
buttonPressedFrmImage.getWidth(),
buttonPressedFrmImage.getHeight(),
-1,
-1,
-1,
501,
buttonNormalFrmImage.getData(),
buttonPressedFrmImage.getData(),
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);
int doneBoxFid = buildFid(OBJ_TYPE_INTERFACE, 209, 0, 0, 0);
if (!doneBoxFrmImage.lock(doneBoxFid)) {
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);
int pressedFid = buildFid(OBJ_TYPE_INTERFACE, 9, 0, 0, 0);
if (!buttonPressedFrmImage.lock(pressedFid)) {
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);
int normalFid = buildFid(OBJ_TYPE_INTERFACE, 8, 0, 0, 0);
if (!buttonNormalFrmImage.lock(normalFid)) {
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[COMPAT_MAX_PATH];
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
snprintf(path, sizeof(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);
blitBufferToBufferTrans(doneBoxFrmImage.getData(),
doneBoxFrmImage.getWidth(),
doneBoxFrmImage.getHeight(),
doneBoxFrmImage.getWidth(),
windowBuf + backgroundFrmImage.getWidth() * _doneY[dialogType] + _doneX[dialogType],
backgroundFrmImage.getWidth());
fontSetCurrent(103);
fontDrawText(windowBuf + backgroundWidth * (_doneY[dialogType] + 3) + _doneX[dialogType] + 35,
a8, backgroundWidth, backgroundWidth, _colorTable[18979]);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * (_doneY[dialogType] + 3) + _doneX[dialogType] + 35,
a8, backgroundFrmImage.getWidth(), backgroundFrmImage.getWidth(), _colorTable[18979]);
int btn = buttonCreate(win,
_doneX[dialogType] + 13,
_doneY[dialogType] + 4,
downButtonWidth,
downButtonHeight,
buttonPressedFrmImage.getWidth(),
buttonPressedFrmImage.getHeight(),
-1,
-1,
-1,
501,
upButton,
downButton,
buttonNormalFrmImage.getData(),
buttonPressedFrmImage.getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (btn != -1) {
@@ -419,57 +415,107 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
fontSetCurrent(101);
int v23 = _ytable[dialogType];
int nextY = _ytable[dialogType];
int maxY = _ytable[dialogType] + _dblines[dialogType] * fontGetLineHeight();
if ((flags & DIALOG_BOX_NO_VERTICAL_CENTERING) == 0) {
int v41 = _dblines[dialogType] * fontGetLineHeight() / 2 + v23;
v23 = v41 - ((bodyLength + 1) * fontGetLineHeight() / 2);
int numberOfLines = 0;
if (hasTitle) {
numberOfLines++;
}
for (int index = 0; index < bodyLength; index++) {
short beginnings[WORD_WRAP_MAX_COUNT];
short subLineCount;
int maxWidth = backgroundFrmImage.getWidth() - _xtable[dialogType] * 2;
if (wordWrap(body[index], maxWidth, beginnings, &subLineCount) == 0) {
numberOfLines += subLineCount - 1;
}
}
if (numberOfLines > _dblines[dialogType]) {
numberOfLines = _dblines[dialogType];
}
nextY += (_dblines[dialogType] - numberOfLines) * fontGetLineHeight() / 2;
}
if (hasTitle) {
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], title, backgroundWidth, backgroundWidth, titleColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + _xtable[dialogType],
title,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
titleColor);
} else {
int length = fontGetStringWidth(title);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, title, backgroundWidth, backgroundWidth, titleColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + (backgroundFrmImage.getWidth() - length) / 2,
title,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
titleColor);
}
v23 += fontGetLineHeight();
nextY += fontGetLineHeight();
}
for (int v94 = 0; v94 < bodyLength; v94++) {
int len = fontGetStringWidth(body[v94]);
if (len <= backgroundWidth - 26) {
for (int index = 0; index < bodyLength && nextY < maxY; index++) {
int width = fontGetStringWidth(body[index]);
int maxWidth = backgroundFrmImage.getWidth() - _xtable[dialogType] * 2;
if (width <= maxWidth) {
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], body[v94], backgroundWidth, backgroundWidth, bodyColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + _xtable[dialogType],
body[index],
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
} else {
int length = fontGetStringWidth(body[v94]);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, body[v94], backgroundWidth, backgroundWidth, bodyColor);
int length = fontGetStringWidth(body[index]);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + (backgroundFrmImage.getWidth() - length) / 2,
body[index],
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
}
v23 += fontGetLineHeight();
nextY += fontGetLineHeight();
} else {
short beginnings[WORD_WRAP_MAX_COUNT];
short count;
if (wordWrap(body[v94], backgroundWidth - 26, beginnings, &count) != 0) {
if (wordWrap(body[index], maxWidth, 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;
for (int beginningIndex = 1; beginningIndex < count && nextY < maxY; beginningIndex++) {
int subLineLength = beginnings[beginningIndex] - beginnings[beginningIndex - 1];
if (subLineLength >= 260) {
subLineLength = 259;
}
char string[260];
strncpy(string, body[v94] + beginnings[v48 - 1], v51);
string[v51] = '\0';
strncpy(string, body[index] + beginnings[beginningIndex - 1], subLineLength);
string[subLineLength] = '\0';
// Remove trailing space as it affects width calculation.
if (subLineLength > 0 && string[subLineLength - 1] == ' ') {
string[subLineLength - 1] = '\0';
subLineLength -= 1;
}
if ((flags & DIALOG_BOX_NO_HORIZONTAL_CENTERING) != 0) {
fontDrawText(windowBuf + backgroundWidth * v23 + _xtable[dialogType], string, backgroundWidth, backgroundWidth, bodyColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + _xtable[dialogType],
string,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
} else {
int length = fontGetStringWidth(string);
fontDrawText(windowBuf + backgroundWidth * v23 + (backgroundWidth - length) / 2, string, backgroundWidth, backgroundWidth, bodyColor);
fontDrawText(windowBuf + backgroundFrmImage.getWidth() * nextY + (backgroundFrmImage.getWidth() - length) / 2,
string,
backgroundFrmImage.getWidth(),
backgroundFrmImage.getWidth(),
bodyColor);
}
v23 += fontGetLineHeight();
nextY += fontGetLineHeight();
}
}
}
@@ -478,7 +524,9 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
int rc = -1;
while (rc == -1) {
int keyCode = _get_input();
sharedFpsLimiter.mark();
int keyCode = inputGetInput();
if (keyCode == 500) {
rc = 1;
@@ -500,16 +548,15 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
if (_game_user_wants_to_quit != 0) {
rc = 1;
}
renderPresent();
sharedFpsLimiter.throttle();
}
windowDestroy(win);
artUnlock(backgroundHandle);
fontSetCurrent(savedFont);
if (v86) {
artUnlock(doneBoxHandle);
artUnlock(downButtonHandle);
artUnlock(upButtonHandle);
messageListFree(&messageList);
}
@@ -517,7 +564,7 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
}
// 0x41DE90
int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLength, int x, int y, int flags)
int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLength, int x, int y, int flags)
{
int oldFont = fontGetCurrent();
@@ -536,62 +583,42 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
}
}
unsigned char* frmBuffers[FILE_DIALOG_FRM_COUNT];
CacheEntry* frmHandles[FILE_DIALOG_FRM_COUNT];
Size frmSizes[FILE_DIALOG_FRM_COUNT];
FrmImage frmImages[FILE_DIALOG_FRM_COUNT];
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
int fid = buildFid(6, gLoadFileDialogFrmIds[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]);
}
int fid = buildFid(OBJ_TYPE_INTERFACE, gLoadFileDialogFrmIds[index], 0, 0, 0);
if (!frmImages[index].lock(fid)) {
return -1;
}
}
int backgroundWidth = frmSizes[FILE_DIALOG_FRM_BACKGROUND].width;
int backgroundHeight = frmSizes[FILE_DIALOG_FRM_BACKGROUND].height;
int backgroundWidth = frmImages[FILE_DIALOG_FRM_BACKGROUND].getWidth();
int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight();
// Maintain original position in original resolution, otherwise center it.
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
if (win == -1) {
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
unsigned char* windowBuffer = windowGetBuffer(win);
memcpy(windowBuffer, frmBuffers[FILE_DIALOG_FRM_BACKGROUND], backgroundWidth * backgroundHeight);
memcpy(windowBuffer, frmImages[FILE_DIALOG_FRM_BACKGROUND].getData(), backgroundWidth * backgroundHeight);
MessageList messageList;
MessageListItem messageListItem;
if (!messageListInit(&messageList)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
char path[COMPAT_MAX_PATH];
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "DBOX.MSG");
if (!messageListLoad(&messageList, path)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
@@ -608,14 +635,14 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
int doneBtn = buttonCreate(win,
LOAD_FILE_DIALOG_DONE_BUTTON_X,
LOAD_FILE_DIALOG_DONE_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
500,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (doneBtn != -1) {
@@ -625,14 +652,14 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
int cancelBtn = buttonCreate(win,
LOAD_FILE_DIALOG_CANCEL_BUTTON_X,
LOAD_FILE_DIALOG_CANCEL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
501,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
@@ -642,14 +669,14 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
int scrollUpBtn = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
-1,
505,
506,
505,
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -658,15 +685,15 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
int scrollDownButton = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y + frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].height,
FILE_DIALOG_SCROLL_BUTTON_Y + frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getHeight(),
-1,
503,
504,
503,
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -702,12 +729,16 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
int rc = -1;
while (rc == -1) {
unsigned int tick = _get_time();
int keyCode = _get_input();
sharedFpsLimiter.mark();
unsigned int tick = getTicks();
int keyCode = inputGetInput();
int scrollDirection = FILE_DIALOG_SCROLL_DIRECTION_NONE;
int scrollCounter = 0;
bool isScrolling = false;
convertMouseWheelToArrowKey(&keyCode);
if (keyCode == 500) {
if (fileListLength != 0) {
strncpy(dest, fileList[selectedFileIndex + pageOffset], 16);
@@ -810,7 +841,7 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
unsigned int scrollDelay = 4;
doubleClickSelectedFileIndex = -2;
while (1) {
unsigned int scrollTick = _get_time();
unsigned int scrollTick = getTicks();
scrollCounter += 1;
if ((!isScrolling && scrollCounter == 1) || (isScrolling && scrollCounter > 14.4)) {
isScrolling = true;
@@ -862,10 +893,12 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
break;
}
int keyCode = _get_input();
int keyCode = inputGetInput();
if (keyCode == 505 || keyCode == 503) {
break;
}
renderPresent();
}
} else {
windowRefresh(win);
@@ -883,14 +916,13 @@ int showLoadFileDialog(char *title, char** fileList, char* dest, int fileListLen
if (_game_user_wants_to_quit) {
rc = 1;
}
renderPresent();
sharedFpsLimiter.throttle();
}
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
messageListFree(&messageList);
fontSetCurrent(oldFont);
@@ -917,61 +949,42 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
}
}
unsigned char* frmBuffers[FILE_DIALOG_FRM_COUNT];
CacheEntry* frmHandles[FILE_DIALOG_FRM_COUNT];
Size frmSizes[FILE_DIALOG_FRM_COUNT];
FrmImage frmImages[FILE_DIALOG_FRM_COUNT];
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
int fid = buildFid(6, gSaveFileDialogFrmIds[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]);
}
int fid = buildFid(OBJ_TYPE_INTERFACE, gSaveFileDialogFrmIds[index], 0, 0, 0);
if (!frmImages[index].lock(fid)) {
return -1;
}
}
int backgroundWidth = frmSizes[FILE_DIALOG_FRM_BACKGROUND].width;
int backgroundHeight = frmSizes[FILE_DIALOG_FRM_BACKGROUND].height;
int backgroundWidth = frmImages[FILE_DIALOG_FRM_BACKGROUND].getWidth();
int backgroundHeight = frmImages[FILE_DIALOG_FRM_BACKGROUND].getHeight();
// Maintain original position in original resolution, otherwise center it.
if (screenGetWidth() != 640) x = (screenGetWidth() - backgroundWidth) / 2;
if (screenGetHeight() != 480) y = (screenGetHeight() - backgroundHeight) / 2;
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_FLAG_0x10 | WINDOW_FLAG_0x04);
int win = windowCreate(x, y, backgroundWidth, backgroundHeight, 256, WINDOW_MODAL | WINDOW_MOVE_ON_TOP);
if (win == -1) {
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
unsigned char* windowBuffer = windowGetBuffer(win);
memcpy(windowBuffer, frmBuffers[FILE_DIALOG_FRM_BACKGROUND], backgroundWidth * backgroundHeight);
memcpy(windowBuffer, frmImages[FILE_DIALOG_FRM_BACKGROUND].getData(), backgroundWidth * backgroundHeight);
MessageList messageList;
MessageListItem messageListItem;
if (!messageListInit(&messageList)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
char path[COMPAT_MAX_PATH];
sprintf(path, "%s%s", asc_5186C8, "DBOX.MSG");
snprintf(path, sizeof(path), "%s%s", asc_5186C8, "DBOX.MSG");
if (!messageListLoad(&messageList, path)) {
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
return -1;
}
@@ -988,14 +1001,14 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int doneBtn = buttonCreate(win,
SAVE_FILE_DIALOG_DONE_BUTTON_X,
SAVE_FILE_DIALOG_DONE_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
500,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (doneBtn != -1) {
@@ -1005,14 +1018,14 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int cancelBtn = buttonCreate(win,
SAVE_FILE_DIALOG_CANCEL_BUTTON_X,
SAVE_FILE_DIALOG_CANCEL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].height,
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getHeight(),
-1,
-1,
-1,
501,
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL],
frmBuffers[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED],
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_LITTLE_RED_BUTTON_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (cancelBtn != -1) {
@@ -1022,14 +1035,14 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollUpBtn = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
-1,
505,
506,
505,
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -1038,15 +1051,15 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int scrollDownButton = buttonCreate(win,
FILE_DIALOG_SCROLL_BUTTON_X,
FILE_DIALOG_SCROLL_BUTTON_Y + frmSizes[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].height,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].width,
frmSizes[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].height,
FILE_DIALOG_SCROLL_BUTTON_Y + frmImages[FILE_DIALOG_FRM_SCROLL_UP_ARROW_PRESSED].getHeight(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getWidth(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getHeight(),
-1,
503,
504,
503,
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL],
frmBuffers[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED],
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_NORMAL].getData(),
frmImages[FILE_DIALOG_FRM_SCROLL_DOWN_ARROW_PRESSED].getData(),
NULL,
BUTTON_FLAG_TRANSPARENT);
if (scrollUpBtn != -1) {
@@ -1090,8 +1103,8 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
char fileNameCopy[32];
strncpy(fileNameCopy, dest, 32);
int fileNameCopyLength = strlen(fileNameCopy);
size_t fileNameCopyLength = strlen(fileNameCopy);
fileNameCopy[fileNameCopyLength + 1] = '\0';
fileNameCopy[fileNameCopyLength] = ' ';
@@ -1102,6 +1115,8 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
windowRefresh(win);
beginTextInput();
int blinkingCounter = 3;
bool blink = false;
@@ -1110,12 +1125,16 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
int rc = -1;
while (rc == -1) {
unsigned int tick = _get_time();
int keyCode = _get_input();
sharedFpsLimiter.mark();
unsigned int tick = getTicks();
int keyCode = inputGetInput();
int scrollDirection = FILE_DIALOG_SCROLL_DIRECTION_NONE;
int scrollCounter = 0;
bool isScrolling = false;
convertMouseWheelToArrowKey(&keyCode);
if (keyCode == 500) {
rc = 0;
} else if (keyCode == KEY_RETURN) {
@@ -1255,7 +1274,7 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
unsigned int scrollDelay = 4;
doubleClickSelectedFileIndex = -2;
while (1) {
unsigned int scrollTick = _get_time();
unsigned int scrollTick = getTicks();
scrollCounter += 1;
if ((!isScrolling && scrollCounter == 1) || (isScrolling && scrollCounter > 14.4)) {
isScrolling = true;
@@ -1324,10 +1343,12 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
break;
}
int key = _get_input();
int key = inputGetInput();
if (key == 505 || key == 503) {
break;
}
renderPresent();
}
} else {
blinkingCounter -= 1;
@@ -1355,8 +1376,13 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
if (_game_user_wants_to_quit != 0) {
rc = 1;
}
renderPresent();
sharedFpsLimiter.throttle();
}
endTextInput();
if (rc == 0) {
if (fileNameCopyLength != 0) {
fileNameCopy[fileNameCopyLength] = '\0';
@@ -1371,11 +1397,6 @@ int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLen
}
windowDestroy(win);
for (int index = 0; index < FILE_DIALOG_FRM_COUNT; index++) {
artUnlock(frmHandles[index]);
}
messageListFree(&messageList);
fontSetCurrent(oldFont);
@@ -1395,8 +1416,10 @@ static void fileDialogRenderFileList(unsigned char* buffer, char** fileList, int
for (int index = 0; index < fileListLength; index++) {
int color = index == selectedIndex ? _colorTable[32747] : _colorTable[992];
fontDrawText(buffer + pitch * y + FILE_DIALOG_FILE_LIST_X, fileList[pageOffset + index], pitch, pitch, color);
fontDrawText(buffer + pitch * y + FILE_DIALOG_FILE_LIST_X, fileList[pageOffset + index], FILE_DIALOG_FILE_LIST_WIDTH, pitch, color);
y += lineHeight;
}
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DBOX_H
#define DBOX_H
namespace fallout {
typedef enum DialogBoxOptions {
DIALOG_BOX_LARGE = 0x01,
DIALOG_BOX_MEDIUM = 0x02,
@@ -14,4 +16,6 @@ int showDialogBox(const char* title, const char** body, int bodyLength, int x, i
int showLoadFileDialog(char* title, char** fileList, char* dest, int fileListLength, int x, int y, int flags);
int showSaveFileDialog(char* title, char** fileList, char* dest, int fileListLength, int x, int y, int flags);
} // namespace fallout
#endif /* DBOX_H */

View File

@@ -1,25 +1,25 @@
#include "debug.h"
#include "memory.h"
#include "platform_compat.h"
#include "window_manager_private.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <SDL.h>
#include "memory.h"
#include "platform_compat.h"
#include "window_manager_private.h"
namespace fallout {
static int _debug_puts(char* string);
static void _debug_clear();
static int _debug_mono(char* string);
static int _debug_log(char* string);
static int _debug_screen(char* string);
static void _debug_putc(char ch);
static void _debug_putc(int ch);
static void _debug_scroll();
// 0x51DEF8
static FILE* _fd = NULL;
@@ -140,18 +140,12 @@ int debugPrint(const char* format, ...)
if (gDebugPrintProc != NULL) {
char string[260];
vsprintf(string, format, args);
vsnprintf(string, sizeof(string), format, args);
rc = gDebugPrintProc(string);
} else {
#ifdef _DEBUG
char string[260];
vsprintf(string, format, args);
#ifdef _WIN32
OutputDebugStringA(string);
#else
printf("%s", string);
#endif
SDL_LogMessageV(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, format, args);
#endif
rc = -1;
}
@@ -174,7 +168,28 @@ static int _debug_puts(char* string)
// 0x4C6FAC
static void _debug_clear()
{
// TODO: Something with segments.
char* buffer;
int x;
int y;
buffer = NULL;
if (gDebugPrintProc == _debug_mono) {
buffer = (char*)0xB0000;
} else if (gDebugPrintProc == _debug_screen) {
buffer = (char*)0xB8000;
}
if (buffer != NULL) {
for (y = 0; y < 25; y++) {
for (x = 0; x < 80; x++) {
*buffer++ = ' ';
*buffer++ = 7;
}
}
_cury = 0;
_curx = 0;
}
}
// 0x4C7004
@@ -220,15 +235,72 @@ static int _debug_screen(char* string)
}
// 0x4C709C
static void _debug_putc(char ch)
static void _debug_putc(int ch)
{
// TODO: Something with segments.
char* buffer;
buffer = (char*)0xB0000;
switch (ch) {
case 7:
printf("\x07");
return;
case 8:
if (_curx > 0) {
_curx--;
buffer += 2 * _curx + 2 * 80 * _cury;
*buffer++ = ' ';
*buffer = 7;
}
return;
case 9:
do {
_debug_putc(' ');
} while ((_curx - 1) % 4 != 0);
return;
case 13:
_curx = 0;
return;
default:
buffer += 2 * _curx + 2 * 80 * _cury;
*buffer++ = ch;
*buffer = 7;
_curx++;
if (_curx < 80) {
return;
}
// FALLTHROUGH
case 10:
_curx = 0;
_cury++;
if (_cury > 24) {
_cury = 24;
_debug_scroll();
}
return;
}
}
// 0x4C71AC
void _debug_scroll()
static void _debug_scroll()
{
// TODO: Something with segments.
char* buffer;
int x;
int y;
buffer = (char*)0xB0000;
for (y = 0; y < 24; y++) {
for (x = 0; x < 80 * 2; x++) {
buffer[0] = buffer[80 * 2];
buffer++;
}
}
for (x = 0; x < 80; x++) {
*buffer++ = ' ';
*buffer++ = 7;
}
}
// 0x4C71E8
@@ -238,3 +310,5 @@ void _debug_exit(void)
fclose(_fd);
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DEBUG_H
#define DEBUG_H
namespace fallout {
typedef int(DebugPrintProc)(char* string);
void _GNW_debug_init();
@@ -12,4 +14,6 @@ void _debug_register_func(DebugPrintProc* proc);
int debugPrint(const char* format, ...);
void _debug_exit(void);
} // namespace fallout
#endif /* DEBUG_H */

View File

@@ -1,14 +1,18 @@
#include "dfile.h"
#include "platform_compat.h"
#include <fpattern.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <fpattern.h>
#include "platform_compat.h"
namespace fallout {
// The size of decompression buffer for reading compressed [DFile]s.
#define DFILE_DECOMPRESSION_BUFFER_SIZE (0x400)
@@ -61,7 +65,7 @@ DBase* dbaseOpen(const char* filePath)
// Get file size, and reposition stream to read footer, which contains two
// 32-bits ints.
int fileSize = compat_filelength(fileno(stream));
int fileSize = getFileSize(stream);
if (fseek(stream, fileSize - sizeof(int) * 2, SEEK_SET) != 0) {
goto err;
}
@@ -141,7 +145,7 @@ DBase* dbaseOpen(const char* filePath)
goto err;
}
dbase->path = strdup(filePath);
dbase->path = compat_strdup(filePath);
dbase->dataOffset = fileSize - dbaseDataSize;
fclose(stream);
@@ -816,10 +820,7 @@ static bool dfileReadCompressed(DFile* stream, void* ptr, size_t size)
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;
}
size_t bytesToRead = std::min(DFILE_DECOMPRESSION_BUFFER_SIZE, stream->entry->dataSize - stream->compressedBytesRead);
if (fread(stream->decompressionBuffer, bytesToRead, 1, stream->stream) != 1) {
break;
@@ -852,3 +853,5 @@ static void dfileUngetCompressed(DFile* stream, int ch)
stream->flags |= DFILE_HAS_COMPRESSED_UNGETC;
stream->position--;
}
} // namespace fallout

View File

@@ -1,10 +1,13 @@
#ifndef DFILE_H
#define DFILE_H
#include <stdio.h>
#include <zlib.h>
#include "platform_compat.h"
#include <stdio.h>
#include <zlib.h>
namespace fallout {
typedef struct DBase DBase;
typedef struct DBaseEntry DBaseEntry;
@@ -127,4 +130,6 @@ long dfileTell(DFile* stream);
void dfileRewind(DFile* stream);
int dfileEof(DFile* stream);
} // namespace fallout
#endif /* DFILE_H */

View File

@@ -1,14 +1,16 @@
#include "dialog.h"
#include "core.h"
#include <string.h>
#include "memory_manager.h"
#include "mouse.h"
#include "movie.h"
#include "platform_compat.h"
#include "svga.h"
#include "text_font.h"
#include "widget.h"
#include "window_manager.h"
#include <string.h>
namespace fallout {
// 0x501623
const float flt_501623 = 31.0;
@@ -76,7 +78,7 @@ int dword_56DB6C;
int dword_56DB70;
// 0x56DB74
int _rand2plus;
char* off_56DB74;
// 0x56DB7C
int dword_56DB7C;
@@ -91,7 +93,7 @@ int dword_56DB84;
int dword_56DB88;
// 0x56DB8C
int dword_56DB8C;
char* off_56DB8C;
// 0x56DB90
int _replyPlaying;
@@ -127,16 +129,16 @@ int dword_56DBB8;
int dword_56DBBC;
// 0x56DBC0
void* off_56DBC0;
char* off_56DBC0;
// 0x56DBC4
void* off_56DBC4;
char* off_56DBC4;
// 0x56DBC8
void* off_56DBC8;
char* off_56DBC8;
// 0x56DBCC
void* off_56DBCC;
char* off_56DBCC;
// 0x56DBD0
char* gDialogReplyTitle;
@@ -151,16 +153,16 @@ int dword_56DBD8;
int dword_56DBDC;
// 0x56DBE0
void* off_56DBE0;
char* off_56DBE0;
// 0x56DBE4
void* off_56DBE4;
char* off_56DBE4;
// 0x56DBE8
void* off_56DBE8;
char* off_56DBE8;
// 0x56DBEC
void* off_56DBEC;
char* off_56DBEC;
// 0x42F434
STRUCT_56DAE0_FIELD_4* _getReply()
@@ -191,7 +193,7 @@ void _replyAddOption(const char* a1, const char* a2, int a3)
v18 = _getReply();
v17 = v18->field_14 - 1;
v18->field_C[v17].field_8 = 2;
v18->field_C[v17].kind = 2;
if (a1 != NULL) {
v14 = (char*)internal_malloc_safe(strlen(a1) + 1, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 805
@@ -204,18 +206,18 @@ void _replyAddOption(const char* a1, const char* a2, int a3)
if (a2 != NULL) {
v15 = (char*)internal_malloc_safe(strlen(a2) + 1, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 810
strcpy(v15, a2);
v18->field_C[v17].field_4 = v15;
v18->field_C[v17].string = v15;
} else {
v18->field_C[v17].field_4 = NULL;
v18->field_C[v17].string = NULL;
}
v18->field_C[v17].field_18 = widgetGetFont();
v18->field_C[v17].field_18 = windowGetFont();
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)
void _replyAddOptionProc(const char* a1, int a2, int a3)
{
STRUCT_56DAE0_FIELD_4* v5;
int v13;
@@ -224,7 +226,7 @@ void _replyAddOptionProc(const char* a1, const char* a2, int a3)
v5 = _getReply();
v13 = v5->field_14 - 1;
v5->field_C[v13].field_8 = 1;
v5->field_C[v13].kind = 1;
if (a1 != NULL) {
v11 = (char*)internal_malloc_safe(strlen(a1) + 1, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 830
@@ -234,9 +236,9 @@ void _replyAddOptionProc(const char* a1, const char* a2, int a3)
v5->field_C[v13].field_0 = NULL;
}
v5->field_C[v13].field_4 = (char*)a2;
v5->field_C[v13].proc = a2;
v5->field_C[v13].field_18 = widgetGetFont();
v5->field_C[v13].field_18 = windowGetFont();
v5->field_C[v13].field_1A = word_56DB60;
v5->field_C[v13].field_14 = a3;
}
@@ -248,9 +250,9 @@ void _optionFree(STRUCT_56DAE0_FIELD_4_FIELD_C* a1)
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
if (a1->kind == 2) {
if (a1->string != NULL) {
internal_free_safe(a1->string, __FILE__, __LINE__); // "..\\int\\DIALOG.C", 846
}
}
}
@@ -388,8 +390,8 @@ void _drawStr(int win, char* str, int font, int width, int height, int left, int
int old_font;
Rect rect;
old_font = widgetGetFont();
widgetSetFont(font);
old_font = windowGetFont();
windowSetFont(font);
_printStr(win, str, width, height, left, top, a8, a9, a10);
@@ -398,7 +400,7 @@ void _drawStr(int win, char* str, int font, int width, int height, int left, int
rect.right = width + left;
rect.bottom = height + top;
windowRefreshRect(win, &rect);
widgetSetFont(old_font);
windowSetFont(old_font);
}
// 0x430D40
@@ -503,7 +505,7 @@ int _dialogOption(const char* a1, const char* a2)
}
// 0x430F38
int _dialogOptionProc(const char* a1, const char* a2)
int _dialogOptionProc(const char* a1, int a2)
{
if (_dialog[_tods].field_C == -1) {
return 1;
@@ -514,6 +516,20 @@ int _dialogOptionProc(const char* a1, const char* a2)
return 0;
}
// 0x430FD4
int sub_430FD4(const char* a1, const char* a2, int timeout)
{
// TODO: Incomplete.
return -1;
}
// 0x431088
int sub_431088(int a1)
{
// TODO: Incomplete.
return -1;
}
// 0x431184
int _dialogGetExitPoint()
{
@@ -533,24 +549,24 @@ int _dialogQuit()
}
// 0x4311B8
int dialogSetOptionWindow(int a1, int a2, int a3, int a4, int a5)
int dialogSetOptionWindow(int a1, int a2, int a3, int a4, char* a5)
{
dword_56DB6C = a1;
dword_56DB70 = a2;
dword_56DB64 = a3;
dword_56DB68 = a4;
_rand2plus = a5;
off_56DB74 = a5;
return 0;
}
// 0x4311E0
int dialogSetReplyWindow(int a1, int a2, int a3, int a4, int a5)
int dialogSetReplyWindow(int a1, int a2, int a3, int a4, char* a5)
{
dword_56DB84 = a1;
dword_56DB88 = a2;
dword_56DB7C = a3;
dword_56DB80 = a4;
dword_56DB8C = a5;
off_56DB8C = a5;
return 0;
}
@@ -565,7 +581,7 @@ int dialogSetBorder(int a1, int a2)
}
// 0x431218
int _dialogSetScrollUp(int a1, int a2, void* a3, void* a4, void* a5, void* a6, int a7)
int _dialogSetScrollUp(int a1, int a2, char* a3, char* a4, char* a5, char* a6, int a7)
{
_upButton = a1;
dword_56DBD8 = a2;
@@ -596,7 +612,7 @@ int _dialogSetScrollUp(int a1, int a2, void* a3, void* a4, void* a5, void* a6, i
}
// 0x4312C0
int _dialogSetScrollDown(int a1, int a2, void* a3, void* a4, void* a5, void* a6, int a7)
int _dialogSetScrollDown(int a1, int a2, char* a3, char* a4, char* a5, char* a6, int a7)
{
_downButton = a1;
dword_56DBB8 = a2;
@@ -666,6 +682,11 @@ int _dialogSetOptionFlags(int flags)
return 1;
}
// 0x431430
void dialogInit()
{
}
// 0x431434
void _dialogClose()
{
@@ -732,3 +753,5 @@ int _dialogGetMediaFlag()
{
return _mediaFlag;
}
} // namespace fallout

View File

@@ -3,13 +3,18 @@
#include "interpreter.h"
namespace fallout {
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;
union {
int proc;
char* string;
};
int kind;
int field_C;
int field_10;
int field_14;
@@ -60,12 +65,12 @@ extern int dword_56DB64;
extern int dword_56DB68;
extern int dword_56DB6C;
extern int dword_56DB70;
extern int _rand2plus;
extern char* off_56DB74;
extern int dword_56DB7C;
extern int dword_56DB80;
extern int dword_56DB84;
extern int dword_56DB88;
extern int dword_56DB8C;
extern char* off_56DB8C;
extern int _replyPlaying;
extern int _replyWin;
extern int gDialogReplyColorG;
@@ -77,22 +82,22 @@ 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* off_56DBC0;
extern char* off_56DBC4;
extern char* off_56DBC8;
extern char* 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;
extern char* off_56DBE0;
extern char* off_56DBE4;
extern char* off_56DBE8;
extern char* 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 _replyAddOptionProc(const char* a1, int a2, int a3);
void _optionFree(STRUCT_56DAE0_FIELD_4_FIELD_C* a1);
void _replyFree();
int _endDialog();
@@ -107,22 +112,27 @@ 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 _dialogOptionProc(const char* a1, int a2);
int sub_430FD4(const char* a1, const char* a2, int timeout);
int sub_431088(int a1);
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 dialogSetOptionWindow(int a1, int a2, int a3, int a4, char* a5);
int dialogSetReplyWindow(int a1, int a2, int a3, int a4, char* 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 _dialogSetScrollUp(int a1, int a2, char* a3, char* a4, char* a5, char* a6, int a7);
int _dialogSetScrollDown(int a1, int a2, char* a3, char* a4, char* a5, char* 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 dialogInit();
void _dialogClose();
int _dialogGetDialogDepth();
void _dialogRegisterWinDrawCallbacks(DialogFunc1* a1, DialogFunc2* a2);
int _dialogToggleMediaFlag(int a1);
int _dialogGetMediaFlag();
} // namespace fallout
#endif /* DIALOG_H */

View File

@@ -1,11 +1,13 @@
#include "dictionary.h"
#include "platform_compat.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "platform_compat.h"
namespace fallout {
// 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.
@@ -44,24 +46,19 @@ static void dictionaryFreeDefaultImpl(void* ptr)
}
// 0x4D9BA8
int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize, void* a4)
int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize, DictionaryIO* io)
{
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");
if (io != NULL) {
memcpy(&(dictionary->io), io, sizeof(*io));
} else {
dictionary->field_10 = 0;
dictionary->field_14 = 0;
dictionary->field_18 = 0;
dictionary->field_1C = 0;
dictionary->io.readProc = NULL;
dictionary->io.writeProc = NULL;
dictionary->io.field_8 = 0;
dictionary->io.field_C = 0;
}
int rc = 0;
@@ -302,6 +299,248 @@ int dictionaryRemoveValue(Dictionary* dictionary, const char* key)
return 0;
}
// NOTE: Unused.
//
// 0x4D9F84
int dictionaryCopy(Dictionary* dest, Dictionary* src)
{
if (src->marker != DICTIONARY_MARKER) {
return -1;
}
if (dictionaryInit(dest, src->entriesCapacity, src->valueSize, &(src->io)) != 0) {
// FIXME: Should return -1, as we were unable to initialize dictionary.
return 0;
}
for (int index = 0; index < src->entriesLength; index++) {
DictionaryEntry* entry = &(src->entries[index]);
if (dictionaryAddValue(dest, entry->key, entry->value) == -1) {
return -1;
}
}
return 0;
}
// NOTE: Unused.
//
// 0x4DA090
int dictionaryReadInt(FILE* stream, int* valuePtr)
{
int ch;
int value;
ch = fgetc(stream);
if (ch == -1) {
return -1;
}
value = (ch & 0xFF);
ch = fgetc(stream);
if (ch == -1) {
return -1;
}
value = (value << 8) | (ch & 0xFF);
ch = fgetc(stream);
if (ch == -1) {
return -1;
}
value = (value << 8) | (ch & 0xFF);
ch = fgetc(stream);
if (ch == -1) {
return -1;
}
value = (value << 8) | (ch & 0xFF);
*valuePtr = value;
return 0;
}
// NOTE: Unused.
//
// 0x4DA0F4
int dictionaryReadHeader(FILE* stream, Dictionary* dictionary)
{
int value;
if (dictionaryReadInt(stream, &value) != 0) return -1;
dictionary->entriesLength = value;
if (dictionaryReadInt(stream, &value) != 0) return -1;
dictionary->entriesCapacity = value;
if (dictionaryReadInt(stream, &value) != 0) return -1;
dictionary->valueSize = value;
// NOTE: Originally reads `values` pointer.
if (dictionaryReadInt(stream, &value) != 0) return -1;
return 0;
}
// NOTE: Unused.
//
// 0x4DA158
int dictionaryLoad(FILE* stream, Dictionary* dictionary, int a3)
{
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);
}
if (dictionaryReadHeader(stream, dictionary) != 0) {
return -1;
}
dictionary->entries = NULL;
if (dictionary->entriesCapacity <= 0) {
return 0;
}
dictionary->entries = (DictionaryEntry*)gDictionaryMallocProc(sizeof(*dictionary->entries) * dictionary->entriesCapacity);
if (dictionary->entries == NULL) {
return -1;
}
for (int index = 0; index < dictionary->entriesLength; index++) {
DictionaryEntry* entry = &(dictionary->entries[index]);
entry->key = NULL;
entry->value = NULL;
}
if (dictionary->entriesLength <= 0) {
return 0;
}
for (int index = 0; index < dictionary->entriesLength; index++) {
DictionaryEntry* entry = &(dictionary->entries[index]);
int keyLength = fgetc(stream);
if (keyLength == -1) {
return -1;
}
entry->key = (char*)gDictionaryMallocProc(keyLength + 1);
if (entry->key == NULL) {
return -1;
}
if (fgets(entry->key, keyLength + 1, stream) == NULL) {
return -1;
}
if (dictionary->valueSize != 0) {
entry->value = gDictionaryMallocProc(dictionary->valueSize);
if (entry->value == NULL) {
return -1;
}
if (dictionary->io.readProc != NULL) {
if (dictionary->io.readProc(stream, entry->value, dictionary->valueSize, a3) != 0) {
return -1;
}
} else {
if (fread(entry->value, dictionary->valueSize, 1, stream) != 1) {
return -1;
}
}
}
}
return 0;
}
// NOTE: Unused.
//
// 0x4DA2EC
int dictionaryWriteInt(FILE* stream, int value)
{
if (fputc((value >> 24) & 0xFF, stream) == -1) return -1;
if (fputc((value >> 16) & 0xFF, stream) == -1) return -1;
if (fputc((value >> 8) & 0xFF, stream) == -1) return -1;
if (fputc(value & 0xFF, stream) == -1) return -1;
return 0;
}
// NOTE: Unused.
//
// 0x4DA360
int dictionaryWriteHeader(FILE* stream, Dictionary* dictionary)
{
if (dictionaryWriteInt(stream, dictionary->entriesLength) != 0) return -1;
if (dictionaryWriteInt(stream, dictionary->entriesCapacity) != 0) return -1;
if (dictionaryWriteInt(stream, dictionary->valueSize) != 0) return -1;
// NOTE: Originally writes `entries` pointer.
if (dictionaryWriteInt(stream, 0) != 0) return -1;
return 0;
}
// NOTE: Unused.
//
// 0x4DA3A4
int dictionaryWrite(FILE* stream, Dictionary* dictionary, int a3)
{
if (dictionary->marker != DICTIONARY_MARKER) {
return -1;
}
if (dictionaryWriteHeader(stream, dictionary) != 0) {
return -1;
}
for (int index = 0; index < dictionary->entriesLength; index++) {
DictionaryEntry* entry = &(dictionary->entries[index]);
int keyLength = strlen(entry->key);
if (fputc(keyLength, stream) == -1) {
return -1;
}
if (fputs(entry->key, stream) == -1) {
return -1;
}
if (dictionary->io.writeProc != NULL) {
if (dictionary->valueSize != 0) {
if (dictionary->io.writeProc(stream, entry->value, dictionary->valueSize, a3) != 0) {
return -1;
}
}
} else {
if (dictionary->valueSize != 0) {
if (fwrite(entry->value, dictionary->valueSize, 1, stream) != 1) {
return -1;
}
}
}
}
return 0;
}
// 0x4DA498
void dictionarySetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc)
{
@@ -315,3 +554,5 @@ void dictionarySetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc,
gDictionaryFreeProc = dictionaryFreeDefaultImpl;
}
}
} // namespace fallout

View File

@@ -1,8 +1,24 @@
#ifndef DICTIONARY_H
#define DICTIONARY_H
#include <stdio.h>
#include "memory_defs.h"
namespace fallout {
typedef int(DictionaryReadProc)(FILE* stream, void* buffer, unsigned int size, int a4);
typedef int(DictionaryWriteProc)(FILE* stream, void* buffer, unsigned int size, int a4);
// NOTE: Last unnamed fields are likely seek, tell, and filelength.
typedef struct DictionaryIO {
DictionaryReadProc* readProc;
DictionaryWriteProc* writeProc;
int field_8;
int field_C;
int field_10;
} DictionaryIO;
// A tuple containing individual key-value pair of a dictionary.
typedef struct DictionaryEntry {
char* key;
@@ -27,22 +43,28 @@ typedef struct Dictionary {
// 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;
// IO callbacks.
DictionaryIO io;
// The array of key-value pairs.
DictionaryEntry* entries;
} Dictionary;
int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize, void* a4);
int dictionaryInit(Dictionary* dictionary, int initialCapacity, size_t valueSize, DictionaryIO* io);
int dictionarySetCapacity(Dictionary* dictionary, int newCapacity);
int dictionaryFree(Dictionary* dictionary);
int dictionaryGetIndexByKey(Dictionary* dictionary, const char* key);
int dictionaryAddValue(Dictionary* dictionary, const char* key, const void* value);
int dictionaryRemoveValue(Dictionary* dictionary, const char* key);
int dictionaryCopy(Dictionary* dest, Dictionary* src);
int dictionaryReadInt(FILE* stream, int* valuePtr);
int dictionaryReadHeader(FILE* stream, Dictionary* dictionary);
int dictionaryLoad(FILE* stream, Dictionary* dictionary, int a3);
int dictionaryWriteInt(FILE* stream, int value);
int dictionaryWriteHeader(FILE* stream, Dictionary* dictionary);
int dictionaryWrite(FILE* stream, Dictionary* dictionary, int a3);
void dictionarySetMemoryProcs(MallocProc* mallocProc, ReallocProc* reallocProc, FreeProc* freeProc);
} // namespace fallout
#endif /* DICTIONARY_H */

View File

@@ -1,6 +1,30 @@
#include "dinput.h"
#include <SDL.h>
namespace fallout {
enum InputType {
INPUT_TYPE_MOUSE,
INPUT_TYPE_TOUCH,
} InputType;
static int gLastInputType = INPUT_TYPE_MOUSE;
static int gTouchMouseLastX = 0;
static int gTouchMouseLastY = 0;
static int gTouchMouseDeltaX = 0;
static int gTouchMouseDeltaY = 0;
static int gTouchFingers = 0;
static unsigned int gTouchGestureLastTouchDownTimestamp = 0;
static unsigned int gTouchGestureLastTouchUpTimestamp = 0;
static int gTouchGestureTaps = 0;
static bool gTouchGestureHandled = false;
static int gMouseWheelDeltaX = 0;
static int gMouseWheelDeltaY = 0;
extern int screenGetWidth();
extern int screenGetHeight();
// 0x4E0400
bool directInputInit()
@@ -47,9 +71,49 @@ bool mouseDeviceUnacquire()
// 0x4E053C
bool mouseDeviceGetData(MouseData* mouseState)
{
Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y));
mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
if (gLastInputType == INPUT_TYPE_TOUCH) {
mouseState->x = gTouchMouseDeltaX;
mouseState->y = gTouchMouseDeltaY;
mouseState->buttons[0] = 0;
mouseState->buttons[1] = 0;
mouseState->wheelX = 0;
mouseState->wheelY = 0;
gTouchMouseDeltaX = 0;
gTouchMouseDeltaY = 0;
if (gTouchFingers == 0) {
if (SDL_GetTicks() - gTouchGestureLastTouchUpTimestamp > 150) {
if (!gTouchGestureHandled) {
if (gTouchGestureTaps == 2) {
mouseState->buttons[0] = 1;
gTouchGestureHandled = true;
} else if (gTouchGestureTaps == 3) {
mouseState->buttons[1] = 1;
gTouchGestureHandled = true;
}
}
}
} else if (gTouchFingers == 1) {
if (SDL_GetTicks() - gTouchGestureLastTouchDownTimestamp > 150) {
if (gTouchGestureTaps == 1) {
mouseState->buttons[0] = 1;
gTouchGestureHandled = true;
} else if (gTouchGestureTaps == 2) {
mouseState->buttons[1] = 1;
gTouchGestureHandled = true;
}
}
}
} else {
Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y));
mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
mouseState->wheelX = gMouseWheelDeltaX;
mouseState->wheelY = gMouseWheelDeltaY;
gMouseWheelDeltaX = 0;
gMouseWheelDeltaY = 0;
}
return true;
}
@@ -100,3 +164,80 @@ bool keyboardDeviceInit()
void keyboardDeviceFree()
{
}
void handleMouseEvent(SDL_Event* event)
{
// Mouse movement and buttons are accumulated in SDL itself and will be
// processed later in `mouseDeviceGetData` via `SDL_GetRelativeMouseState`.
if (event->type == SDL_MOUSEWHEEL) {
gMouseWheelDeltaX += event->wheel.x;
gMouseWheelDeltaY += event->wheel.y;
}
if (gLastInputType != INPUT_TYPE_MOUSE) {
// Reset touch data.
gTouchMouseLastX = 0;
gTouchMouseLastY = 0;
gTouchMouseDeltaX = 0;
gTouchMouseDeltaY = 0;
gTouchFingers = 0;
gTouchGestureLastTouchDownTimestamp = 0;
gTouchGestureLastTouchUpTimestamp = 0;
gTouchGestureTaps = 0;
gTouchGestureHandled = false;
gLastInputType = INPUT_TYPE_MOUSE;
}
}
void handleTouchEvent(SDL_Event* event)
{
int windowWidth = screenGetWidth();
int windowHeight = screenGetHeight();
if (event->tfinger.type == SDL_FINGERDOWN) {
gTouchFingers++;
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
gTouchMouseDeltaX = 0;
gTouchMouseDeltaY = 0;
if (event->tfinger.timestamp - gTouchGestureLastTouchDownTimestamp > 250) {
gTouchGestureTaps = 0;
gTouchGestureHandled = false;
}
gTouchGestureLastTouchDownTimestamp = event->tfinger.timestamp;
} else if (event->tfinger.type == SDL_FINGERMOTION) {
int prevX = gTouchMouseLastX;
int prevY = gTouchMouseLastY;
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
} else if (event->tfinger.type == SDL_FINGERUP) {
gTouchFingers--;
int prevX = gTouchMouseLastX;
int prevY = gTouchMouseLastY;
gTouchMouseLastX = (int)(event->tfinger.x * windowWidth);
gTouchMouseLastY = (int)(event->tfinger.y * windowHeight);
gTouchMouseDeltaX += gTouchMouseLastX - prevX;
gTouchMouseDeltaY += gTouchMouseLastY - prevY;
gTouchGestureTaps++;
gTouchGestureLastTouchUpTimestamp = event->tfinger.timestamp;
}
if (gLastInputType != INPUT_TYPE_TOUCH) {
// Reset mouse data.
SDL_GetRelativeMouseState(NULL, NULL);
gLastInputType = INPUT_TYPE_TOUCH;
}
}
} // namespace fallout

View File

@@ -1,10 +1,16 @@
#ifndef DINPUT_H
#define DINPUT_H
#include <SDL.h>
namespace fallout {
typedef struct MouseData {
int x;
int y;
unsigned char buttons[2];
int wheelX;
int wheelY;
} MouseData;
typedef struct KeyboardData {
@@ -26,4 +32,9 @@ void mouseDeviceFree();
bool keyboardDeviceInit();
void keyboardDeviceFree();
void handleMouseEvent(SDL_Event* event);
void handleTouchEvent(SDL_Event* event);
} // namespace fallout
#endif /* DINPUT_H */

View File

@@ -1,19 +1,25 @@
#include "display_monitor.h"
#include <string.h>
#include <fstream>
#include "art.h"
#include "color.h"
#include "combat.h"
#include "core.h"
#include "draw.h"
#include "game_mouse.h"
#include "game_sound.h"
#include "geometry.h"
#include "input.h"
#include "interface.h"
#include "memory.h"
#include "sfall_config.h"
#include "svga.h"
#include "text_font.h"
#include "window_manager.h"
#include <string.h>
namespace fallout {
// The maximum number of lines display monitor can hold. Once this value
// is reached earlier messages are thrown away.
@@ -24,7 +30,7 @@
#define DISPLAY_MONITOR_X (23)
#define DISPLAY_MONITOR_Y (24)
#define DISPLAY_MONITOR_WIDTH (167)
#define DISPLAY_MONITOR_WIDTH (167 + gInterfaceBarContentOffset)
#define DISPLAY_MONITOR_HEIGHT (60)
#define DISPLAY_MONITOR_HALF_HEIGHT (DISPLAY_MONITOR_HEIGHT / 2)
@@ -33,6 +39,7 @@
#define DISPLAY_MONITOR_BEEP_DELAY (500U)
static void display_clear();
static void displayMonitorRefresh();
static void displayMonitorScrollUpOnMouseDown(int btn, int keyCode);
static void displayMonitorScrollDownOnMouseDown(int btn, int keyCode);
@@ -40,18 +47,19 @@ static void displayMonitorScrollUpOnMouseEnter(int btn, int keyCode);
static void displayMonitorScrollDownOnMouseEnter(int btn, int keyCode);
static void displayMonitorOnMouseExit(int btn, int keyCode);
static void consoleFileInit();
static void consoleFileReset();
static void consoleFileExit();
static void consoleFileAddMessage(const char* message);
static void consoleFileFlush();
// 0x51850C
static bool gDisplayMonitorInitialized = false;
// The rectangle that display monitor occupies in the main interface window.
//
// 0x518510
static const Rect gDisplayMonitorRect = {
DISPLAY_MONITOR_X,
DISPLAY_MONITOR_Y,
DISPLAY_MONITOR_X + DISPLAY_MONITOR_WIDTH - 1,
DISPLAY_MONITOR_Y + DISPLAY_MONITOR_HEIGHT - 1,
};
static Rect gDisplayMonitorRect;
// 0x518520
static int gDisplayMonitorScrollDownButton = -1;
@@ -86,10 +94,20 @@ static int _disp_start;
// 0x56FB58
static unsigned int gDisplayMonitorLastBeepTimestamp;
static std::ofstream gConsoleFileStream;
static int gConsoleFilePrintCount = 0;
// 0x431610
int displayMonitorInit()
{
if (!gDisplayMonitorInitialized) {
gDisplayMonitorRect = {
DISPLAY_MONITOR_X,
DISPLAY_MONITOR_Y,
DISPLAY_MONITOR_X + DISPLAY_MONITOR_WIDTH - 1,
DISPLAY_MONITOR_Y + DISPLAY_MONITOR_HEIGHT - 1,
};
int oldFont = fontGetCurrent();
fontSetCurrent(DISPLAY_MONITOR_FONT);
@@ -104,25 +122,33 @@ int displayMonitorInit()
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;
if (gInterfaceBarIsCustom) {
_intface_full_width = gInterfaceBarWidth;
blitBufferToBuffer(customInterfaceBarGetBackgroundImageData() + gInterfaceBarWidth * DISPLAY_MONITOR_Y + DISPLAY_MONITOR_X,
DISPLAY_MONITOR_WIDTH,
DISPLAY_MONITOR_HEIGHT,
gInterfaceBarWidth,
gDisplayMonitorBackgroundFrmData,
DISPLAY_MONITOR_WIDTH);
} else {
FrmImage backgroundFrmImage;
int backgroundFid = buildFid(OBJ_TYPE_INTERFACE, 16, 0, 0, 0);
if (!backgroundFrmImage.lock(backgroundFid)) {
internal_free(gDisplayMonitorBackgroundFrmData);
return -1;
}
unsigned char* backgroundFrmData = backgroundFrmImage.getData();
_intface_full_width = backgroundFrmImage.getWidth();
blitBufferToBuffer(backgroundFrmData + _intface_full_width * DISPLAY_MONITOR_Y + DISPLAY_MONITOR_X,
DISPLAY_MONITOR_WIDTH,
DISPLAY_MONITOR_HEIGHT,
_intface_full_width,
gDisplayMonitorBackgroundFrmData,
DISPLAY_MONITOR_WIDTH);
}
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,
@@ -168,14 +194,11 @@ int displayMonitorInit()
gDisplayMonitorEnabled = true;
gDisplayMonitorInitialized = true;
for (int index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
// NOTE: Uninline.
display_clear();
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
// SFALL
consoleFileInit();
}
return 0;
@@ -184,15 +207,12 @@ int displayMonitorInit()
// 0x431800
int displayMonitorReset()
{
if (gDisplayMonitorInitialized) {
for (int index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
// NOTE: Uninline.
display_clear();
// SFALL
consoleFileReset();
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
}
return 0;
}
@@ -200,6 +220,9 @@ int displayMonitorReset()
void displayMonitorExit()
{
if (gDisplayMonitorInitialized) {
// SFALL
consoleFileExit();
internal_free(gDisplayMonitorBackgroundFrmData);
gDisplayMonitorInitialized = false;
}
@@ -212,6 +235,9 @@ void displayMonitorAddMessage(char* str)
return;
}
// SFALL
consoleFileAddMessage(str);
int oldFont = fontGetCurrent();
fontSetCurrent(DISPLAY_MONITOR_FONT);
@@ -295,6 +321,24 @@ void displayMonitorAddMessage(char* str)
displayMonitorRefresh();
}
// NOTE: Inlined.
//
// 0x431A2C
static void display_clear()
{
int index;
if (gDisplayMonitorInitialized) {
for (index = 0; index < gDisplayMonitorLinesCapacity; index++) {
gDisplayMonitorLines[index][0] = '\0';
}
_disp_start = 0;
_disp_curr = 0;
displayMonitorRefresh();
}
}
// 0x431A78
static void displayMonitorRefresh()
{
@@ -389,3 +433,53 @@ void displayMonitorEnable()
gDisplayMonitorEnabled = true;
}
}
static void consoleFileInit()
{
char* consoleFilePath;
configGetString(&gSfallConfig, SFALL_CONFIG_MISC_KEY, SFALL_CONFIG_CONSOLE_OUTPUT_FILE_KEY, &consoleFilePath);
if (consoleFilePath != NULL && *consoleFilePath == '\0') {
consoleFilePath = NULL;
}
if (consoleFilePath != NULL) {
gConsoleFileStream.open(consoleFilePath);
}
}
static void consoleFileReset()
{
if (gConsoleFileStream.is_open()) {
gConsoleFilePrintCount = 0;
gConsoleFileStream.flush();
}
}
static void consoleFileExit()
{
if (gConsoleFileStream.is_open()) {
gConsoleFileStream.close();
}
}
static void consoleFileAddMessage(const char* message)
{
if (gConsoleFileStream.is_open()) {
gConsoleFileStream << message << '\n';
gConsoleFilePrintCount++;
if (gConsoleFilePrintCount >= 20) {
consoleFileFlush();
}
}
}
static void consoleFileFlush()
{
if (gConsoleFileStream.is_open()) {
gConsoleFilePrintCount = 0;
gConsoleFileStream.flush();
}
}
} // namespace fallout

View File

@@ -1,6 +1,8 @@
#ifndef DISPLAY_MONITOR_H
#define DISPLAY_MONITOR_H
namespace fallout {
int displayMonitorInit();
int displayMonitorReset();
void displayMonitorExit();
@@ -8,4 +10,6 @@ void displayMonitorAddMessage(char* string);
void displayMonitorDisable();
void displayMonitorEnable();
} // namespace fallout
#endif /* DISPLAY_MONITOR_H */

View File

@@ -1,11 +1,13 @@
#include "draw.h"
#include "color.h"
#include "core.h"
#include "mmx.h"
#include <string.h>
#include "color.h"
#include "mmx.h"
#include "svga.h"
namespace fallout {
// 0x4D2FC0
void bufferDrawLine(unsigned char* buf, int pitch, int x1, int y1, int x2, int y2, int color)
{
@@ -150,74 +152,56 @@ void bufferDrawRectShadowed(unsigned char* buf, int pitch, int left, int top, in
// 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 stepX = (destWidth << 16) / srcWidth;
int stepY = (destHeight << 16) / srcHeight;
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;
int startDestY = (srcY * stepY) >> 16;
int endDestY = ((srcY + 1) * stepY) >> 16;
unsigned char* c = src + v1;
unsigned char* currSrc = src + srcPitch * srcY;
for (int srcX = 0; srcX < srcWidth; srcX += 1) {
int v7 = v3 >> 16;
int v8 = v6 >> 16;
int startDestX = (srcX * stepX) >> 16;
int endDestX = ((srcX + 1) * stepX) >> 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;
for (int destY = startDestY; destY < endDestY; destY += 1) {
unsigned char* currDest = dest + destPitch * destY + startDestX;
for (int destX = startDestX; destX < endDestX; destX += 1) {
*currDest++ = *currSrc;
}
v9 += destPitch;
}
v3 += widthRatio;
c++;
v6 += widthRatio;
currSrc++;
}
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 stepX = (destWidth << 16) / srcWidth;
int stepY = (destHeight << 16) / srcHeight;
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;
int startDestY = (srcY * stepY) >> 16;
int endDestY = ((srcY + 1) * stepY) >> 16;
unsigned char* c = src + v1;
unsigned char* currSrc = src + srcPitch * srcY;
for (int srcX = 0; srcX < srcWidth; srcX += 1) {
int v7 = v3 >> 16;
int v8 = v6 >> 16;
int startDestX = (srcX * stepX) >> 16;
int endDestX = ((srcX + 1) * stepX) >> 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;
if (*currSrc != 0) {
for (int destY = startDestY; destY < endDestY; destY += 1) {
unsigned char* currDest = dest + destPitch * destY + startDestX;
for (int destX = startDestX; destX < endDestX; destX += 1) {
*currDest++ = *currSrc;
}
v9 += destPitch;
}
}
v3 += widthRatio;
c++;
v6 += widthRatio;
currSrc++;
}
v1 += srcPitch;
v2 += heightRatio;
}
}
@@ -257,8 +241,8 @@ void _lighten_buf(unsigned char* buf, int width, int height, int pitch)
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
unsigned char p = *buf;
*buf++ = _intensityColorTable[(p << 8) + 147];
unsigned char color = *buf;
*buf++ = intensityColorTable[color][147];
}
buf += skip;
}
@@ -326,3 +310,5 @@ void bufferOutline(unsigned char* buf, int width, int height, int pitch, int col
}
}
}
} // namespace fallout

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