commit
3b6fa9b6d3
36
.github/labeler.yml
vendored
Normal file
36
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
"part: game client":
|
||||
- src/Components/Modules/Stats*
|
||||
|
||||
"part: game server":
|
||||
- src/Components/Modules/Dedicated*
|
||||
|
||||
"part: zonebuilder":
|
||||
- src/Components/Modules/ZoneBuilder*
|
||||
|
||||
"area: menus":
|
||||
- src/Components/Modules/Menus*
|
||||
|
||||
"area: anticheat":
|
||||
- src/Components/Modules/AntiCheat*
|
||||
|
||||
"area: serverlist":
|
||||
- src/Components/Modules/ServerList*
|
||||
|
||||
"area: weapons":
|
||||
- src/Components/Modules/Weapon*
|
||||
|
||||
"area: networking":
|
||||
- src/Components/Modules/Auth*
|
||||
- src/Components/Modules/Network*
|
||||
- src/Components/Modules/Node*
|
||||
- src/Components/Modules/PlayerName*
|
||||
- src/Components/Modules/RCon*
|
||||
- src/Components/Modules/Session*
|
||||
|
||||
"area: continuous integration":
|
||||
- "*appveyor*"
|
||||
- ".github/workflows/**"
|
||||
- Jenkinsfile
|
||||
|
||||
"status: work in progress":
|
||||
- "**"
|
11
.github/workflows/labeler.yml
vendored
Normal file
11
.github/workflows/labeler.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
name: "Pull Request Labeler"
|
||||
on:
|
||||
- pull_request
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v2
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -143,6 +143,7 @@ UpgradeLog*.htm
|
||||
*.til
|
||||
*.idb
|
||||
*.i64
|
||||
ida/*
|
||||
|
||||
### Custom user files
|
||||
# User scripts
|
||||
|
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -25,7 +25,10 @@
|
||||
[submodule "deps/protobuf"]
|
||||
path = deps/protobuf
|
||||
url = https://github.com/google/protobuf.git
|
||||
branch = 3.6.x
|
||||
branch = 3.11.x
|
||||
[submodule "deps/udis86"]
|
||||
path = deps/udis86
|
||||
url = https://github.com/vmt/udis86.git
|
||||
[submodule "deps/dxsdk"]
|
||||
path = deps/dxsdk
|
||||
url = https://github.com/devKlausS/dxsdk.git
|
||||
|
853
CHANGELOG.md
853
CHANGELOG.md
@ -1,403 +1,450 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [0.6.0] - 2018-12-30
|
||||
|
||||
### Added
|
||||
|
||||
- Implement unbanclient command.
|
||||
- Implement /serverlist api endpoint on dedicated servers
|
||||
- Add dvar to control the server query rate (net_serverQueryLimit & net_serverFrames)
|
||||
|
||||
### Changed
|
||||
|
||||
- Update dependencies
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix mods not working in private matches.
|
||||
- Fix multiple game structures (map tools)
|
||||
- Fix multiple vulnerability's
|
||||
- Fix lag spikes on lower end PCs
|
||||
- Fix invalid name check
|
||||
- Fix openLink command crash issue
|
||||
- Fix lobby server map downloading
|
||||
- Fix steam integration
|
||||
|
||||
### Known issues
|
||||
|
||||
- HTTPS is not supported for fast downloads at the moment.
|
||||
|
||||
## [0.5.4] - 2017-07-09
|
||||
|
||||
### Added
|
||||
|
||||
- Integrate IW4MVM by luckyy.
|
||||
|
||||
### Changed
|
||||
|
||||
- Displayed stats for Dragunov have been changed, has no effect on actual game play.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix fast download failing when the target host is missing a trailing slash.
|
||||
- Fix servers not being listed in the server browser.
|
||||
- Fix some FPS drop issues caused by compression code.
|
||||
|
||||
### Known issues
|
||||
|
||||
- HTTPS is not supported for fast downloads at the moment.
|
||||
|
||||
## [0.5.3] - 2017-07-02
|
||||
|
||||
### Added
|
||||
|
||||
- Increase webserver security.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reduce lags introduced by nodes.
|
||||
- Fix modlist download.
|
||||
|
||||
## [0.5.2] - 2017-07-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix dedicated server crash.
|
||||
|
||||
## [0.5.1] - 2017-07-02
|
||||
|
||||
### Added
|
||||
|
||||
- Add fast download option for custom mods/maps based on Call of Duty 4.
|
||||
- Display a toast when an update is available.
|
||||
- Use the hourglass cursor while loading assets (with the native cursor feature).
|
||||
- Show bots in parenthesis after the number of players in the serverlist (request).
|
||||
- Add GSC event `level waittill("say", string, player);` (request).
|
||||
- Restrict unauthorized mod download for password protected servers.
|
||||
- Add OMA support for 15 classes.
|
||||
|
||||
### Changed
|
||||
|
||||
- Show friend avatars when they play IW4x (request).
|
||||
- Rewrite and optimize the entire node system.
|
||||
- Remove syncnode command for node system.
|
||||
- Remove steam start.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix lags and frame drops caused by server sorting.
|
||||
- Fix demos on custom maps.
|
||||
- Can no longer join a lobby or server with an incorrect password.
|
||||
- Fix crashes caused by a bug in file/data compression.
|
||||
- Improve overall stability.
|
||||
|
||||
## [0.5.0] - 2017-06-04
|
||||
|
||||
### Added
|
||||
|
||||
- Add GSC functionality to download files via HTTP(S) (request).
|
||||
- Implement preliminary custom map support.
|
||||
|
||||
### Changed
|
||||
|
||||
- Add new nicknames for bots.
|
||||
- Bumped Fastfile version. If you built fastfiles with the zone builder (e.g. mod.ff) you have to rebuild them!
|
||||
- Default `sv_network_fps` to `1000` on dedicated game servers.
|
||||
- Increase maximum FOV to 120.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `iw4x_onelog` dvar.
|
||||
- Fix server list only showing local servers by default instead of Internet servers.
|
||||
- Fix some deadlock situations on game shutdown.
|
||||
|
||||
## [0.4.2] - 2017-03-16
|
||||
|
||||
### Changed
|
||||
|
||||
- Disable unnecessary dvar update in server browser.
|
||||
- Update bot names.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix process permissions.
|
||||
- Fix classic AK-47 color bleedthrough.
|
||||
- Re-aligned the MG4's Raffica iron sights.
|
||||
|
||||
## [0.4.1] - 2017-03-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix command line argument parsing.
|
||||
|
||||
## [0.4.0] - 2017-03-10
|
||||
|
||||
### Added
|
||||
|
||||
- Set played with status.
|
||||
- Add support for 15 classes.
|
||||
- Add `iw4x_onelog` dvar.
|
||||
- Add show server/playercount in server browser.
|
||||
|
||||
### Changed
|
||||
|
||||
- Do not show friend status notifications with stream friendly UI.
|
||||
- Reduce loaded modules for dedis.
|
||||
- Use joystick emblem for friend status.
|
||||
- Disable XP bar when max level.
|
||||
- Change fs_game display postition.
|
||||
- Replace Painkiller with Juiced from IW5.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix AK weaponfiles.
|
||||
- Fix brightness slider.
|
||||
- Fix text length for column mod in server browser.
|
||||
- Changed the L86 LSW to use the correct HUD icon.
|
||||
- Re-aligned the M93 Raffica's iron sights.
|
||||
- Re-aligned the Desert Eagle's iron sights.
|
||||
|
||||
## [0.3.3] - 2017-02-14
|
||||
|
||||
### Added
|
||||
|
||||
- Add mapname to friend status (request).
|
||||
- Add option to toggle notify friend state.
|
||||
- Add support for mod.ff.
|
||||
|
||||
### Changed
|
||||
|
||||
- Disabled big minidump message box.
|
||||
- Limit dedicated servers to 15 instances per IP.
|
||||
- Move build number location.
|
||||
- Remove news ticker and friends button from theater.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix audio bug in options menu.
|
||||
- Fix crash caused by faulty file download requests to game hosts.
|
||||
- Fix friend sorting.
|
||||
- Fix game not starting issue under certain circumstances.
|
||||
- Fix menu crash.
|
||||
- Fix typo in security increase popmenu.
|
||||
- Fix vid_restart crash with connect protocol.
|
||||
- Fix weapon crash issue.
|
||||
- Potentially fix no-ammo bug.
|
||||
|
||||
## [0.3.2] - 2017-02-12
|
||||
|
||||
This is the third public Beta version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add working friend system.
|
||||
- Add colored pings in the server list.
|
||||
- Add credits.
|
||||
- Add first launch menu.
|
||||
- Add AK-47 (Classic) attachments.
|
||||
- Add HUD icon for night vision goggles.
|
||||
|
||||
### Changed
|
||||
|
||||
- Join when pressing enter in server list (request).
|
||||
- Redesign and refactor all fullscreen menus.
|
||||
- Increase weapon and configstring limit.
|
||||
- Allow creating full crash dumps if wanted.
|
||||
- Set default name from steam.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix missing models on village.
|
||||
- Fix custom server motd (request).
|
||||
- Fix various memory leaks.
|
||||
- Fix mouse pitch (request).
|
||||
- Fix compatibility with B3 (request).
|
||||
- Fix RCon bug (request).
|
||||
- Fix dedicated server crash on linux.
|
||||
- Fix crash in mod download.
|
||||
- Fix peacekeeper reload sound.
|
||||
- Fix cl_maxpackets 125 in settings (request).
|
||||
- Fix deserteaglegold_mp icon.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.3.1] - 2017-01-21
|
||||
|
||||
This is the second public Beta version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add classic AK-47 to CAC.
|
||||
- Add servers to favorites when ingame.
|
||||
- Add delete favorites button in the serverlist.
|
||||
|
||||
### Changed
|
||||
|
||||
- Change maplist to a dynamic list.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix list focus.
|
||||
- Fix mod restart loop.
|
||||
- Fix mod download status.
|
||||
- Fix modelsurf crash.
|
||||
- Fix floating AK-74u.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.3.0] - 2017-01-14
|
||||
|
||||
This is the first public Beta version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add com_logFilter dvar.
|
||||
- Add peacekeeper.
|
||||
- Add support for maps from DLC 8 (Recycled Pack)
|
||||
|
||||
- Chemical Plant (mp_storm_spring)
|
||||
- Crash: Tropical (mp_crash_tropical)
|
||||
- Estate: Tropical (mp_estate_tropical)
|
||||
- Favela: Tropical (mp_fav_tropical)
|
||||
- Forgotten City (mp_bloc_sh)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve node synchronization handling.
|
||||
- Improve security by modifying GUIDs to allow 64-bit certificate fingerprints.
|
||||
- Optimize fastfiles, they are now a lot smaller.
|
||||
- Replace intro.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix concurrent image loading bug.
|
||||
- Fix issues when spawning more than one bot.
|
||||
- Fix no ammo bug.
|
||||
- Fix server crash on startup.
|
||||
- Fix splash screen hang.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.2.1] - 2016-12-14
|
||||
|
||||
This is the second public Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for CoD:Online maps.
|
||||
|
||||
- Firing Range (mp_firingrange)
|
||||
- Rust (mp_rust_long)
|
||||
- Shipment (mp_shipment/mp_shipment_long)
|
||||
|
||||
- Add `sv_motd` Dvar for server owners to set custom motd (will be visible in the loadscreen).
|
||||
- Add Zonebuilder support for sounds and fx.
|
||||
- Add command setviewpos.
|
||||
- Add high-definition loadscreens.
|
||||
|
||||
### Changed
|
||||
|
||||
- Rename Arctic Wet Work map to it's official name (Freighter).
|
||||
- Complete redesign of the main menus.
|
||||
- Allow `cl_maxpackets` to be set up to 125.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix crash when using the Harrier killstreak.
|
||||
- Disable code that downloads news/changelog when in zonebuilder mode.
|
||||
- Fix freeze on game shutdown.
|
||||
- Disable unlockstats while ingame to prevent a crash.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.2.0] - 2016-10-09
|
||||
|
||||
This is the first public Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Support for CoD:Online maps.
|
||||
|
||||
- Arctic Wet Work (mp_cargoship_sh)
|
||||
- Bloc (mp_bloc)
|
||||
- Bog (mp_bog_sh)
|
||||
- Crossfire (mp_cross_fire)
|
||||
- Killhouse (mp_killhouse)
|
||||
- Nuketown (mp_nuked)
|
||||
- Wet Work (mp_cargoship)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix techniques in Zonebuilder.
|
||||
- Fix possible memory leak.
|
||||
- Fix timeout bug when connecting to server via iw4x link.
|
||||
- Partially fix deadlock in decentralized networking code.
|
||||
|
||||
### Known issues
|
||||
|
||||
- Running IW4x on Linux currently requires gnutls to be installed additional to Wine as it needs to access the Tor service via HTTPS.
|
||||
|
||||
## [0.1.1] - 2016-09-19
|
||||
|
||||
This version is an internal Pre-Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add IW5 material embedding system.
|
||||
|
||||
### Changed
|
||||
|
||||
- Enhance mod download with detailed progress display.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix and optimize network logging commands.
|
||||
- Fix console not displaying command inputs properly.
|
||||
- Fix crash when running multiple instances of the IW4x client from the same directory.
|
||||
- Fix crash when the `securityLevel` command is used on a game server.
|
||||
- Fix possible data corruption in code for decentralized networking.
|
||||
- Fix possible deadlock during client shutdown.
|
||||
|
||||
## [0.1.0] - 2016-09-03
|
||||
|
||||
This version is an internal Pre-Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add `banclient` command which will permanently ban a client from a server. The ban will persist across restarts.
|
||||
- Add capabilities to save played games and replay them ("Theater").
|
||||
- Add code for generating and sending minidumps for debugging purposes. This feature is meant to be used only during the Open Beta and will be removed once the code goes to stable release.
|
||||
- Add commands that allow forwarding console and games log via UDP to other computers ("network logging").
|
||||
- Add D3D9Ex.
|
||||
- Add filters for server list.
|
||||
- Add handling for `iw4x://` URLs ("connect protocol"). For example, if IW4x is properly registered in Windows as URL handler for `iw4x://` URLs you can type `iw4x://ip:port`. If possible, this will connect to the server in an already running IW4x client.
|
||||
- Add lean support through new key bindings.
|
||||
- Add native cursor as replacement for the sometimes laggy in-game cursor. This change can be reverted in the settings menu.
|
||||
- Add news ticker.
|
||||
- Add remote console ("RCon").
|
||||
- Add support for BigBrotherBot.
|
||||
- Add support for hosting game mods in order to allow players to just join modded servers out of the box ("mod download").
|
||||
- Add Warfare2 text coloring.
|
||||
- Add zone builder. For more information see the respective documentation.
|
||||
- Implement a completely decentralized peering network.
|
||||
- Implement playlists which can be used for flexible map and gametype rotation.
|
||||
- Introduce security levels. This ensures that you need to "pay" with CPU power to verify your identity once before being able to join a server which reduces the interval at which people who get banned can circumvent server bans through using new identities. The default security level is 23.
|
||||
- Move IW4x resource files into their own folder to prevent clogging up the main game directories.
|
||||
- Reintroduce parties, now also available for dedicated servers ("lobby servers").
|
||||
|
||||
### Changed
|
||||
|
||||
- Move logs to `userraw` folder.
|
||||
- Replace main menu background music.
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog v0.3.0](http://keepachangelog.com/en/0.3.0/) and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [0.6.1] - 2020-12-23
|
||||
|
||||
### Added
|
||||
|
||||
- Add host information to /info endpoint (request)
|
||||
- Add fileWrite GSC Function (#36)
|
||||
- Add fileRead GSC Function (#36)
|
||||
- Add fileExists GSC Function (#36)
|
||||
- Add fileRemove GSC Function (#36)
|
||||
- Add botMovement GSC Function (#46)
|
||||
- Add botAction GSC Function (#46)
|
||||
- Add botWeapon GSC Function (#46)
|
||||
- Add botStop GSC Function (#46)
|
||||
- Add isBot GSC Function (#46)
|
||||
- Add setPing GSC Function (#46)
|
||||
- Add GetSystemTime and GetSystemTimeMilliseconds GSC Functions (#46)
|
||||
- Add PrintConsole GSC Function (#46)
|
||||
- Add Exec GSC Function (#46)
|
||||
- Add getIP GSC Method (#36)
|
||||
- Add getPing GSC Method (#36)
|
||||
- Add scr_intermissionTime GSC Function (#25)
|
||||
- Add g_playerCollision Dvar (#36)
|
||||
- Add g_playerEjection Dvar (#36)
|
||||
- Add r_specularCustomMaps Dvar (#36)
|
||||
- Unlock safeArea_horizontal and safeArea_vertical Dvars (#42)
|
||||
- Unlock cg_fovscale Dvar (#47)
|
||||
- Added g_antilag Dvar (#61)
|
||||
|
||||
### Changed
|
||||
|
||||
- Stats are now separate for each mod (#6). Player stats are copied to `fs_game` folder if no stats exist for this mod yet. Keep in mind this also means that level, XP and classes will not be synchronized with the main stats file after this point.
|
||||
- Reduced duration of toasts (#48)
|
||||
- Removed old updater functionality (#54)
|
||||
- Use old bot names if bots.txt is not found (#46)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed a node system related crash (#45)
|
||||
- Fixed an issue that made dedicated servers crash when info was requested during map rotation (#43)
|
||||
- Fixed an issue where the game was trying to decrypt gsc files which caused it to crash when loading mods (#35)
|
||||
- Fixed an issue causing the game to crash when Steam was running in the background (#56)
|
||||
- Fixed slow download speed when using fast download
|
||||
|
||||
### Known issues
|
||||
|
||||
- HTTPS is not supported for fast downloads at the moment.
|
||||
|
||||
## [0.6.0] - 2018-12-30
|
||||
|
||||
### Added
|
||||
|
||||
- Implement unbanclient command.
|
||||
- Implement /serverlist api endpoint on dedicated servers
|
||||
- Add dvar to control the server query rate (net_serverQueryLimit & net_serverFrames)
|
||||
|
||||
### Changed
|
||||
|
||||
- Update dependencies
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix mods not working in private matches.
|
||||
- Fix multiple game structures (map tools)
|
||||
- Fix multiple vulnerability's
|
||||
- Fix lag spikes on lower end PCs
|
||||
- Fix invalid name check
|
||||
- Fix openLink command crash issue
|
||||
- Fix lobby server map downloading
|
||||
- Fix steam integration
|
||||
|
||||
### Known issues
|
||||
|
||||
- HTTPS is not supported for fast downloads at the moment.
|
||||
|
||||
## [0.5.4] - 2017-07-09
|
||||
|
||||
### Added
|
||||
|
||||
- Integrate IW4MVM by luckyy.
|
||||
|
||||
### Changed
|
||||
|
||||
- Displayed stats for Dragunov have been changed, has no effect on actual game play.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix fast download failing when the target host is missing a trailing slash.
|
||||
- Fix servers not being listed in the server browser.
|
||||
- Fix some FPS drop issues caused by compression code.
|
||||
|
||||
### Known issues
|
||||
|
||||
- HTTPS is not supported for fast downloads at the moment.
|
||||
|
||||
## [0.5.3] - 2017-07-02
|
||||
|
||||
### Added
|
||||
|
||||
- Increase webserver security.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Reduce lags introduced by nodes.
|
||||
- Fix modlist download.
|
||||
|
||||
## [0.5.2] - 2017-07-02
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix dedicated server crash.
|
||||
|
||||
## [0.5.1] - 2017-07-02
|
||||
|
||||
### Added
|
||||
|
||||
- Add fast download option for custom mods/maps based on Call of Duty 4.
|
||||
- Display a toast when an update is available.
|
||||
- Use the hourglass cursor while loading assets (with the native cursor feature).
|
||||
- Show bots in parenthesis after the number of players in the serverlist (request).
|
||||
- Add GSC event `level waittill("say", string, player);` (request).
|
||||
- Restrict unauthorized mod download for password protected servers.
|
||||
- Add OMA support for 15 classes.
|
||||
|
||||
### Changed
|
||||
|
||||
- Show friend avatars when they play IW4x (request).
|
||||
- Rewrite and optimize the entire node system.
|
||||
- Remove syncnode command for node system.
|
||||
- Remove steam start.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix lags and frame drops caused by server sorting.
|
||||
- Fix demos on custom maps.
|
||||
- Can no longer join a lobby or server with an incorrect password.
|
||||
- Fix crashes caused by a bug in file/data compression.
|
||||
- Improve overall stability.
|
||||
|
||||
## [0.5.0] - 2017-06-04
|
||||
|
||||
### Added
|
||||
|
||||
- Add GSC functionality to download files via HTTP(S) (request).
|
||||
- Implement preliminary custom map support.
|
||||
|
||||
### Changed
|
||||
|
||||
- Add new nicknames for bots.
|
||||
- Bumped Fastfile version. If you built fastfiles with the zone builder (e.g. mod.ff) you have to rebuild them!
|
||||
- Default `sv_network_fps` to `1000` on dedicated game servers.
|
||||
- Increase maximum FOV to 120.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix `iw4x_onelog` dvar.
|
||||
- Fix server list only showing local servers by default instead of Internet servers.
|
||||
- Fix some deadlock situations on game shutdown.
|
||||
|
||||
## [0.4.2] - 2017-03-16
|
||||
|
||||
### Changed
|
||||
|
||||
- Disable unnecessary dvar update in server browser.
|
||||
- Update bot names.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix process permissions.
|
||||
- Fix classic AK-47 color bleedthrough.
|
||||
- Re-aligned the MG4's Raffica iron sights.
|
||||
|
||||
## [0.4.1] - 2017-03-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix command line argument parsing.
|
||||
|
||||
## [0.4.0] - 2017-03-10
|
||||
|
||||
### Added
|
||||
|
||||
- Set played with status.
|
||||
- Add support for 15 classes.
|
||||
- Add `iw4x_onelog` dvar.
|
||||
- Add show server/playercount in server browser.
|
||||
|
||||
### Changed
|
||||
|
||||
- Do not show friend status notifications with stream friendly UI.
|
||||
- Reduce loaded modules for dedis.
|
||||
- Use joystick emblem for friend status.
|
||||
- Disable XP bar when max level.
|
||||
- Change fs_game display postition.
|
||||
- Replace Painkiller with Juiced from IW5.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix AK weaponfiles.
|
||||
- Fix brightness slider.
|
||||
- Fix text length for column mod in server browser.
|
||||
- Changed the L86 LSW to use the correct HUD icon.
|
||||
- Re-aligned the M93 Raffica's iron sights.
|
||||
- Re-aligned the Desert Eagle's iron sights.
|
||||
|
||||
## [0.3.3] - 2017-02-14
|
||||
|
||||
### Added
|
||||
|
||||
- Add mapname to friend status (request).
|
||||
- Add option to toggle notify friend state.
|
||||
- Add support for mod.ff.
|
||||
|
||||
### Changed
|
||||
|
||||
- Disabled big minidump message box.
|
||||
- Limit dedicated servers to 15 instances per IP.
|
||||
- Move build number location.
|
||||
- Remove news ticker and friends button from theater.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix audio bug in options menu.
|
||||
- Fix crash caused by faulty file download requests to game hosts.
|
||||
- Fix friend sorting.
|
||||
- Fix game not starting issue under certain circumstances.
|
||||
- Fix menu crash.
|
||||
- Fix typo in security increase popmenu.
|
||||
- Fix vid_restart crash with connect protocol.
|
||||
- Fix weapon crash issue.
|
||||
- Potentially fix no-ammo bug.
|
||||
|
||||
## [0.3.2] - 2017-02-12
|
||||
|
||||
This is the third public Beta version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add working friend system.
|
||||
- Add colored pings in the server list.
|
||||
- Add credits.
|
||||
- Add first launch menu.
|
||||
- Add AK-47 (Classic) attachments.
|
||||
- Add HUD icon for night vision goggles.
|
||||
|
||||
### Changed
|
||||
|
||||
- Join when pressing enter in server list (request).
|
||||
- Redesign and refactor all fullscreen menus.
|
||||
- Increase weapon and configstring limit.
|
||||
- Allow creating full crash dumps if wanted.
|
||||
- Set default name from steam.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix missing models on village.
|
||||
- Fix custom server motd (request).
|
||||
- Fix various memory leaks.
|
||||
- Fix mouse pitch (request).
|
||||
- Fix compatibility with B3 (request).
|
||||
- Fix RCon bug (request).
|
||||
- Fix dedicated server crash on linux.
|
||||
- Fix crash in mod download.
|
||||
- Fix peacekeeper reload sound.
|
||||
- Fix cl_maxpackets 125 in settings (request).
|
||||
- Fix deserteaglegold_mp icon.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.3.1] - 2017-01-21
|
||||
|
||||
This is the second public Beta version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add classic AK-47 to CAC.
|
||||
- Add servers to favorites when ingame.
|
||||
- Add delete favorites button in the serverlist.
|
||||
|
||||
### Changed
|
||||
|
||||
- Change maplist to a dynamic list.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix list focus.
|
||||
- Fix mod restart loop.
|
||||
- Fix mod download status.
|
||||
- Fix modelsurf crash.
|
||||
- Fix floating AK-74u.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.3.0] - 2017-01-14
|
||||
|
||||
This is the first public Beta version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add com_logFilter dvar.
|
||||
- Add peacekeeper.
|
||||
- Add support for maps from DLC 8 (Recycled Pack)
|
||||
|
||||
- Chemical Plant (mp_storm_spring)
|
||||
- Crash: Tropical (mp_crash_tropical)
|
||||
- Estate: Tropical (mp_estate_tropical)
|
||||
- Favela: Tropical (mp_fav_tropical)
|
||||
- Forgotten City (mp_bloc_sh)
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve node synchronization handling.
|
||||
- Improve security by modifying GUIDs to allow 64-bit certificate fingerprints.
|
||||
- Optimize fastfiles, they are now a lot smaller.
|
||||
- Replace intro.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix concurrent image loading bug.
|
||||
- Fix issues when spawning more than one bot.
|
||||
- Fix no ammo bug.
|
||||
- Fix server crash on startup.
|
||||
- Fix splash screen hang.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.2.1] - 2016-12-14
|
||||
|
||||
This is the second public Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for CoD:Online maps.
|
||||
|
||||
- Firing Range (mp_firingrange)
|
||||
- Rust (mp_rust_long)
|
||||
- Shipment (mp_shipment/mp_shipment_long)
|
||||
|
||||
- Add `sv_motd` Dvar for server owners to set custom motd (will be visible in the loadscreen).
|
||||
- Add Zonebuilder support for sounds and fx.
|
||||
- Add command setviewpos.
|
||||
- Add high-definition loadscreens.
|
||||
|
||||
### Changed
|
||||
|
||||
- Rename Arctic Wet Work map to it's official name (Freighter).
|
||||
- Complete redesign of the main menus.
|
||||
- Allow `cl_maxpackets` to be set up to 125.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix crash when using the Harrier killstreak.
|
||||
- Disable code that downloads news/changelog when in zonebuilder mode.
|
||||
- Fix freeze on game shutdown.
|
||||
- Disable unlockstats while ingame to prevent a crash.
|
||||
|
||||
### Known issues
|
||||
|
||||
- IW4x on Linux currently requires gnutls to be installed to access the Tor service via HTTPS.
|
||||
|
||||
## [0.2.0] - 2016-10-09
|
||||
|
||||
This is the first public Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Support for CoD:Online maps.
|
||||
|
||||
- Arctic Wet Work (mp_cargoship_sh)
|
||||
- Bloc (mp_bloc)
|
||||
- Bog (mp_bog_sh)
|
||||
- Crossfire (mp_cross_fire)
|
||||
- Killhouse (mp_killhouse)
|
||||
- Nuketown (mp_nuked)
|
||||
- Wet Work (mp_cargoship)
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix techniques in Zonebuilder.
|
||||
- Fix possible memory leak.
|
||||
- Fix timeout bug when connecting to server via iw4x link.
|
||||
- Partially fix deadlock in decentralized networking code.
|
||||
|
||||
### Known issues
|
||||
|
||||
- Running IW4x on Linux currently requires gnutls to be installed additional to Wine as it needs to access the Tor service via HTTPS.
|
||||
|
||||
## [0.1.1] - 2016-09-19
|
||||
|
||||
This version is an internal Pre-Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add IW5 material embedding system.
|
||||
|
||||
### Changed
|
||||
|
||||
- Enhance mod download with detailed progress display.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix and optimize network logging commands.
|
||||
- Fix console not displaying command inputs properly.
|
||||
- Fix crash when running multiple instances of the IW4x client from the same directory.
|
||||
- Fix crash when the `securityLevel` command is used on a game server.
|
||||
- Fix possible data corruption in code for decentralized networking.
|
||||
- Fix possible deadlock during client shutdown.
|
||||
|
||||
## [0.1.0] - 2016-09-03
|
||||
|
||||
This version is an internal Pre-Alpha version.
|
||||
|
||||
### Added
|
||||
|
||||
- Add `banclient` command which will permanently ban a client from a server. The ban will persist across restarts.
|
||||
- Add capabilities to save played games and replay them ("Theater").
|
||||
- Add code for generating and sending minidumps for debugging purposes. This feature is meant to be used only during the Open Beta and will be removed once the code goes to stable release.
|
||||
- Add commands that allow forwarding console and games log via UDP to other computers ("network logging").
|
||||
- Add D3D9Ex.
|
||||
- Add filters for server list.
|
||||
- Add handling for `iw4x://` URLs ("connect protocol"). For example, if IW4x is properly registered in Windows as URL handler for `iw4x://` URLs you can type `iw4x://ip:port`. If possible, this will connect to the server in an already running IW4x client.
|
||||
- Add lean support through new key bindings.
|
||||
- Add native cursor as replacement for the sometimes laggy in-game cursor. This change can be reverted in the settings menu.
|
||||
- Add news ticker.
|
||||
- Add remote console ("RCon").
|
||||
- Add support for BigBrotherBot.
|
||||
- Add support for hosting game mods in order to allow players to just join modded servers out of the box ("mod download").
|
||||
- Add Warfare2 text coloring.
|
||||
- Add zone builder. For more information see the respective documentation.
|
||||
- Implement a completely decentralized peering network.
|
||||
- Implement playlists which can be used for flexible map and gametype rotation.
|
||||
- Introduce security levels. This ensures that you need to "pay" with CPU power to verify your identity once before being able to join a server which reduces the interval at which people who get banned can circumvent server bans through using new identities. The default security level is 23.
|
||||
- Move IW4x resource files into their own folder to prevent clogging up the main game directories.
|
||||
- Reintroduce parties, now also available for dedicated servers ("lobby servers").
|
||||
|
||||
### Changed
|
||||
|
||||
- Move logs to `userraw` folder.
|
||||
- Replace main menu background music.
|
||||
|
20
Jenkinsfile
vendored
20
Jenkinsfile
vendored
@ -268,15 +268,17 @@ gitlabBuilds(builds: ["Checkout & Versioning", "Build", "Testing", "Archiving"])
|
||||
}
|
||||
executions["$testName on Linux"] = {
|
||||
node("docker && linux && amd64") {
|
||||
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
|
||||
def image = null
|
||||
dir("src") {
|
||||
unstash "jenkins-files"
|
||||
image = docker.build("github.com/IW4x/iw4x-client-testing-wine32", "--rm --force-rm -f jenkins/wine32.Dockerfile jenkins")
|
||||
deleteDir()
|
||||
}
|
||||
image.inside {
|
||||
doUnitTests(test.StashName)
|
||||
timeout(time: 10, unit: "MINUTES") {
|
||||
wrap([$class: 'AnsiColorBuildWrapper', 'colorMapName': 'XTerm']) {
|
||||
def image = null
|
||||
dir("src") {
|
||||
unstash "jenkins-files"
|
||||
image = docker.build("github.com/IW4x/iw4x-client-testing-wine32", "--rm --force-rm -f jenkins/wine32.Dockerfile jenkins")
|
||||
deleteDir()
|
||||
}
|
||||
image.inside {
|
||||
doUnitTests(test.StashName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||

|
||||

|
||||

|
||||
[](https://discord.gg/sKeVmR3)
|
||||
[](https://ci.appveyor.com/project/iw4x/iw4x-client/branch/develop)
|
||||
[](https://discord.gg/sKeVmR3)
|
||||
[](https://www.patreon.com/iw4x)
|
||||
|
||||
# IW4x: Client
|
||||
|
||||
@ -38,7 +40,6 @@
|
||||
| `--disable-bitmessage` | Disable use of BitMessage completely. |
|
||||
| `--disable-base128` | Disable base128 encoding for minidumps. |
|
||||
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
|
||||
| `--enable-dxsdk` | Enable DirectX SDK (required for GfxMap exporting). |
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
34
appveyor.yml
Normal file
34
appveyor.yml
Normal file
@ -0,0 +1,34 @@
|
||||
# AppVeyor CI configuration
|
||||
|
||||
version: "#{build} ({branch})"
|
||||
|
||||
environment:
|
||||
matrix:
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
PREMAKE_ACTION: vs2019
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
platform: Win32
|
||||
|
||||
install:
|
||||
- ps: |
|
||||
Write-Host "Updating version information..." -ForegroundColor Cyan
|
||||
|
||||
Update-AppveyorBuild -Version $(& tools/premake5.exe version | select -Last 1)
|
||||
- git submodule update --init --recursive
|
||||
- ps: |
|
||||
Write-Host "Generating project files with premake..." -ForegroundColor Cyan
|
||||
& "./tools/premake5.exe" $env:PREMAKE_ACTION
|
||||
Write-Host "Generated" -ForegroundColor Green
|
||||
|
||||
build:
|
||||
project: build/iw4x.sln
|
||||
parallel: true
|
||||
verbosity: minimal
|
||||
|
||||
artifacts:
|
||||
- path: build/bin/**/*.dll
|
||||
- path: build/bin/**/*.pdb
|
1
deps/dxsdk
vendored
Submodule
1
deps/dxsdk
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 996cf740444ce56178e6bc32e7fbe2c8b0f40f08
|
2
deps/json11
vendored
2
deps/json11
vendored
@ -1 +1 @@
|
||||
Subproject commit 8ccf1f0c5ecab6151a65f216e7eeccd8588e5457
|
||||
Subproject commit e2e3a11e99672b018e0e0657867e6a3439e180cf
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
||||
Subproject commit a1f6312416ef6cd183ee62db58b640dc2d7ec1f4
|
||||
Subproject commit 1937f412605e1b04ddb41ef9c2f2f0aab7e61548
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
||||
Subproject commit 13444a8af2d077eda8fd0be017cac2dad20465dc
|
||||
Subproject commit 6ac0b0c1b69b9a88e1b3b3002c2e3a9062ae99b4
|
2
deps/mongoose
vendored
2
deps/mongoose
vendored
@ -1 +1 @@
|
||||
Subproject commit ff4649fe42bfe93835a0bac77fbe8ab84c8b36e4
|
||||
Subproject commit cb602f178ccea7f0c790cf5510f7a29c017db954
|
2
deps/pdcurses
vendored
2
deps/pdcurses
vendored
@ -1 +1 @@
|
||||
Subproject commit b12c4a5212e97cf83f0074618c1473253fad4131
|
||||
Subproject commit 618e0aaa31b4728eb4df78ec4de6c2b873908eda
|
2
deps/protobuf
vendored
2
deps/protobuf
vendored
@ -1 +1 @@
|
||||
Subproject commit 66dc42d891a4fc8e9190c524fd67961688a37bbe
|
||||
Subproject commit 63cfdafacba6141717a2df97fc123dc0c14ba7c4
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
||||
Subproject commit 79baebe50e4d6b73ae1f8b603f0ef41300110aa3
|
||||
Subproject commit d71dc66fa8a153fb6e7c626847095d9697a6cf42
|
@ -1,7 +1,7 @@
|
||||
# Requires a decent modern Docker version (v1.10.x at least ideally)
|
||||
|
||||
# Use semi-official Arch Linux image with fixed versioning
|
||||
FROM base/archlinux:2018.11.01
|
||||
FROM archlinux/base
|
||||
|
||||
# Environment variables
|
||||
ENV WINEPREFIX /wine32
|
||||
@ -10,7 +10,7 @@ ENV WINEDEBUG -all
|
||||
|
||||
# Install Wine (32-bit)
|
||||
RUN \
|
||||
echo -e "#!/bin/sh\nwine \$@\nretval=\$?\ntail --pid=\$(pidof wineserver 2>/dev/null||echo 0) -f /dev/null\nexit \$retval" > /usr/local/bin/wine-wrapper &&\
|
||||
echo -e "#!/bin/sh\nwine \$@\nretval=\$?\nwineserver -w\nexit \$retval" > /usr/local/bin/wine-wrapper &&\
|
||||
chmod +x /usr/local/bin/wine-wrapper &&\
|
||||
\
|
||||
(\
|
||||
@ -19,11 +19,13 @@ RUN \
|
||||
echo 'Include = /etc/pacman.d/mirrorlist'\
|
||||
) >> /etc/pacman.conf &&\
|
||||
pacman -Sy --noconfirm \
|
||||
awk \
|
||||
lib32-gnutls \
|
||||
wine \
|
||||
wget \
|
||||
xorg-server-xvfb \
|
||||
pacman-contrib \
|
||||
pacman-contrib \
|
||||
awk \
|
||||
&&\
|
||||
\
|
||||
wine-wrapper wineboot.exe -i &&\
|
||||
@ -39,7 +41,6 @@ RUN \
|
||||
find /. -name "*~" -type f -delete &&\
|
||||
rm -rf /tmp/* /var/tmp/* /usr/share/man/* /usr/share/info/* /usr/share/doc/* &&\
|
||||
pacman -Scc --noconfirm &&\
|
||||
paccache -rk0 &&\
|
||||
rm -rf /var/lib/pacman/sync/*
|
||||
|
||||
USER 0
|
||||
|
35
premake/dxsdk.lua
Normal file
35
premake/dxsdk.lua
Normal file
@ -0,0 +1,35 @@
|
||||
dxsdk = {
|
||||
settings = nil
|
||||
}
|
||||
|
||||
function dxsdk.setup(settings)
|
||||
if not settings.source then error("Missing source.") end
|
||||
|
||||
dxsdk.settings = settings
|
||||
|
||||
if not dxsdk.settings.defines then dxsdk.settings.defines = {} end
|
||||
end
|
||||
|
||||
function dxsdk.import()
|
||||
if not dxsdk.settings then error("You need to call dxsdk.setup first") end
|
||||
|
||||
--filter "platforms:*32"
|
||||
libdirs { path.join(dxsdk.settings.source, "Lib/x86") }
|
||||
|
||||
--filter "platforms:*64"
|
||||
-- libdirs { path.join(dxsdk.settings.source, "Lib/x64") }
|
||||
|
||||
--filter {}
|
||||
|
||||
dxsdk.includes()
|
||||
end
|
||||
|
||||
function dxsdk.includes()
|
||||
if not dxsdk.settings then error("You need to call dxsdk.setup first") end
|
||||
|
||||
includedirs { path.join(dxsdk.settings.source, "Include") }
|
||||
defines(dxsdk.settings.defines)
|
||||
end
|
||||
|
||||
function dxsdk.project()
|
||||
end
|
33
premake5.lua
33
premake5.lua
@ -1,4 +1,5 @@
|
||||
gitVersioningCommand = "git describe --tags --dirty --always"
|
||||
gitCurrentBranchCommand = "git symbolic-ref -q --short HEAD"
|
||||
|
||||
-- Quote the given string input as a C string
|
||||
function cstrquote(value)
|
||||
@ -73,11 +74,6 @@ newoption {
|
||||
description = "Upload minidumps even for Debug builds."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "enable-dxsdk",
|
||||
description = "Enable DirectX SDK (required for GfxMap exporting)."
|
||||
}
|
||||
|
||||
newaction {
|
||||
trigger = "version",
|
||||
description = "Returns the version string for the current commit of the source code.",
|
||||
@ -86,8 +82,19 @@ newaction {
|
||||
local proc = assert(io.popen(gitVersioningCommand, "r"))
|
||||
local gitDescribeOutput = assert(proc:read('*a')):gsub("%s+", "")
|
||||
proc:close()
|
||||
local version = gitDescribeOutput
|
||||
|
||||
print(gitDescribeOutput)
|
||||
proc = assert(io.popen(gitCurrentBranchCommand, "r"))
|
||||
local gitCurrentBranchOutput = assert(proc:read('*a')):gsub("%s+", "")
|
||||
local gitCurrentBranchSuccess = proc:close()
|
||||
if gitCurrentBranchSuccess then
|
||||
-- We got a branch name, check if it is a feature branch
|
||||
if gitCurrentBranchOutput ~= "develop" and gitCurrentBranchOutput ~= "master" then
|
||||
version = version .. "-" .. gitCurrentBranchOutput
|
||||
end
|
||||
end
|
||||
|
||||
print(version)
|
||||
os.exit(0)
|
||||
end
|
||||
}
|
||||
@ -182,6 +189,7 @@ require "premake/protobuf"
|
||||
require "premake/zlib"
|
||||
require "premake/udis86"
|
||||
require "premake/iw4mvm"
|
||||
require "premake/dxsdk"
|
||||
|
||||
json11.setup
|
||||
{
|
||||
@ -236,8 +244,13 @@ iw4mvm.setup
|
||||
},
|
||||
source = path.join(depsBasePath, "iw4mvm"),
|
||||
}
|
||||
dxsdk.setup
|
||||
{
|
||||
source = path.join(depsBasePath, "dxsdk"),
|
||||
}
|
||||
|
||||
workspace "iw4x"
|
||||
startproject "iw4x"
|
||||
location "./build"
|
||||
objdir "%{wks.location}/obj"
|
||||
targetdir "%{wks.location}/bin/%{cfg.buildcfg}"
|
||||
@ -311,11 +324,6 @@ workspace "iw4x"
|
||||
if _OPTIONS["force-exception-handler"] then
|
||||
defines { "FORCE_EXCEPTION_HANDLER" }
|
||||
end
|
||||
if _OPTIONS["enable-dxsdk"] then
|
||||
defines { "ENABLE_DXSDK" }
|
||||
includedirs { "%DXSDK_DIR%Include" }
|
||||
libdirs { "%DXSDK_DIR%Lib/x86" }
|
||||
end
|
||||
|
||||
-- Pre-compiled header
|
||||
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
||||
@ -332,6 +340,7 @@ workspace "iw4x"
|
||||
zlib.import()
|
||||
udis86.import()
|
||||
--iw4mvm.import()
|
||||
dxsdk.import()
|
||||
|
||||
-- fix vpaths for protobuf sources
|
||||
vpaths
|
||||
@ -450,7 +459,7 @@ workspace "*"
|
||||
buildoptions {
|
||||
"/std:c++latest"
|
||||
}
|
||||
systemversion "10.0.17763.0"
|
||||
systemversion "latest"
|
||||
defines { "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS" }
|
||||
|
||||
rule "ProtobufCompiler"
|
||||
|
@ -60,6 +60,7 @@ namespace Components
|
||||
Loader::Register(new Console());
|
||||
Loader::Register(new Friends());
|
||||
Loader::Register(new IPCPipe());
|
||||
Loader::Register(new MapDump());
|
||||
Loader::Register(new ModList());
|
||||
Loader::Register(new Monitor());
|
||||
Loader::Register(new Network());
|
||||
@ -72,9 +73,7 @@ namespace Components
|
||||
Loader::Register(new Renderer());
|
||||
Loader::Register(new UIFeeder());
|
||||
Loader::Register(new UIScript());
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
Loader::Register(new AntiCheat());
|
||||
#endif
|
||||
Loader::Register(new Changelog());
|
||||
Loader::Register(new Dedicated());
|
||||
Loader::Register(new Discovery());
|
||||
@ -103,6 +102,8 @@ namespace Components
|
||||
Loader::Register(new ConnectProtocol());
|
||||
Loader::Register(new StartupMessages());
|
||||
|
||||
Loader::Register(new Client());
|
||||
|
||||
Loader::Pregame = false;
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,7 @@ namespace Components
|
||||
#include "Modules/Logger.hpp"
|
||||
#include "Modules/Friends.hpp"
|
||||
#include "Modules/IPCPipe.hpp"
|
||||
#include "Modules/MapDump.hpp"
|
||||
#include "Modules/Session.hpp"
|
||||
#include "Modules/ClanTags.hpp"
|
||||
#include "Modules/Download.hpp"
|
||||
@ -129,3 +130,5 @@ namespace Components
|
||||
#include "Modules/ConnectProtocol.hpp"
|
||||
#include "Modules/StartupMessages.hpp"
|
||||
#include "Modules/Stats.hpp"
|
||||
|
||||
#include "Modules/Client.hpp"
|
@ -824,6 +824,64 @@ namespace Components
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
void AntiCheat::SystemTimeDiff(LPSYSTEMTIME stA, LPSYSTEMTIME stB, LPSYSTEMTIME stC) {
|
||||
FILETIME ftA, ftB, ftC;
|
||||
ULARGE_INTEGER uiA, uiB, uiC;
|
||||
|
||||
SystemTimeToFileTime(stA, &ftA);
|
||||
SystemTimeToFileTime(stB, &ftB);
|
||||
uiA.HighPart = ftA.dwHighDateTime;
|
||||
uiA.LowPart = ftA.dwLowDateTime;
|
||||
uiB.HighPart = ftB.dwHighDateTime;
|
||||
uiB.LowPart = ftB.dwLowDateTime;
|
||||
|
||||
uiC.QuadPart = uiA.QuadPart - uiB.QuadPart;
|
||||
|
||||
ftC.dwHighDateTime = uiC.HighPart;
|
||||
ftC.dwLowDateTime = uiC.LowPart;
|
||||
FileTimeToSystemTime(&ftC, stC);
|
||||
}
|
||||
|
||||
void AntiCheat::CheckStartupTime()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
FILETIME creation, exit, kernel, user;
|
||||
SYSTEMTIME current, creationSt, diffSt;
|
||||
|
||||
GetSystemTime(¤t);
|
||||
GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);
|
||||
|
||||
FileTimeToSystemTime(&creation, &creationSt);
|
||||
AntiCheat::SystemTimeDiff(¤t, &creationSt, &diffSt);
|
||||
|
||||
#ifdef DEBUG
|
||||
char buf[512];
|
||||
snprintf(buf, 512, "creation: %d:%d:%d:%d\n", creationSt.wHour, creationSt.wMinute, creationSt.wSecond, creationSt.wMilliseconds);
|
||||
OutputDebugStringA(buf);
|
||||
|
||||
snprintf(buf, 512, "current: %d:%d:%d:%d\n", current.wHour, current.wMinute, current.wSecond, current.wMilliseconds);
|
||||
OutputDebugStringA(buf);
|
||||
|
||||
snprintf(buf, 512, "diff: %d:%d:%d:%d\n", diffSt.wHour, diffSt.wMinute, diffSt.wSecond, diffSt.wMilliseconds);
|
||||
OutputDebugStringA(buf);
|
||||
#endif
|
||||
|
||||
// crash client if they are using process suspension to inject dlls during startup (aka before we got to here)
|
||||
// maybe tweak this value depending on what the above logging reveals during testing,
|
||||
// but 5 seconds seems about right for now
|
||||
int time = diffSt.wMilliseconds + (diffSt.wSecond * 1000) + (diffSt.wMinute * 1000 * 60);
|
||||
if (time > 5000) {
|
||||
Components::AntiCheat::CrashClient();
|
||||
}
|
||||
|
||||
// use below for logging when using StartSuspended.exe
|
||||
// FILE* f = fopen("times.txt", "a");
|
||||
// fwrite(buf, 1, strlen(buf), f);
|
||||
// fclose(f);
|
||||
|
||||
__VMProtectEnd;
|
||||
}
|
||||
|
||||
AntiCheat::AntiCheat()
|
||||
{
|
||||
__VMProtectBeginUltra("");
|
||||
@ -831,13 +889,12 @@ namespace Components
|
||||
time(nullptr);
|
||||
AntiCheat::Flags = NO_FLAG;
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef DISABLE_ANTICHEAT
|
||||
Command::Add("penis", [](Command::Params*)
|
||||
{
|
||||
AntiCheat::CrashClient();
|
||||
});
|
||||
#else
|
||||
|
||||
Utils::Hook(0x507BD5, AntiCheat::PatchWinAPI, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x5082FD, AntiCheat::LostD3DStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x51C76C, AntiCheat::CinematicStub, HOOK_CALL).install()->quick();
|
||||
@ -866,6 +923,7 @@ namespace Components
|
||||
|
||||
// Set the integrity flag
|
||||
AntiCheat::Flags |= AntiCheat::IntergrityFlag::INITIALIZATION;
|
||||
|
||||
#endif
|
||||
|
||||
__VMProtectEnd;
|
||||
|
@ -54,6 +54,9 @@ namespace Components
|
||||
static void UninstallLibHook();
|
||||
static void InstallLibHook();
|
||||
|
||||
static void CheckStartupTime();
|
||||
static void SystemTimeDiff(LPSYSTEMTIME stA, LPSYSTEMTIME stB, LPSYSTEMTIME stC);
|
||||
|
||||
private:
|
||||
enum IntergrityFlag
|
||||
{
|
||||
@ -114,3 +117,4 @@ namespace Components
|
||||
static Utils::Hook VirtualProtectHook[2];
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -112,5 +112,9 @@ namespace Components
|
||||
Utils::Hook::Set<BYTE>(0x4A95F8, 32);
|
||||
|
||||
Utils::Hook::Set<int>(0x42F22B, offsetof(Game::newMapArena_t, mapName) - offsetof(Game::newMapArena_t, other));
|
||||
|
||||
// patch max arena count
|
||||
constexpr auto arenaCount = sizeof(ArenaLength::NewArenas) / sizeof(Game::newMapArena_t);
|
||||
Utils::Hook::Set<std::uint32_t>(0x630AA3, arenaCount);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Components
|
||||
{
|
||||
thread_local int AssetHandler::BypassState = 0;
|
||||
bool AssetHandler::ShouldSearchTempAssets = false;
|
||||
std::map<Game::XAssetType, AssetHandler::IAsset*> AssetHandler::AssetInterfaces;
|
||||
std::map<Game::XAssetType, Utils::Slot<AssetHandler::Callback>> AssetHandler::TypeCallbacks;
|
||||
Utils::Signal<AssetHandler::RestrictCallback> AssetHandler::RestrictSignal;
|
||||
@ -69,6 +70,21 @@ namespace Components
|
||||
return header;
|
||||
}
|
||||
|
||||
Game::XAssetHeader AssetHandler::FindTemporaryAsset(Game::XAssetType type, const char* filename)
|
||||
{
|
||||
Game::XAssetHeader header = { nullptr };
|
||||
if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header;
|
||||
|
||||
auto tempPool = &AssetHandler::TemporaryAssets[type];
|
||||
auto entry = tempPool->find(filename);
|
||||
if (entry != tempPool->end())
|
||||
{
|
||||
header = { entry->second };
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
int AssetHandler::HasThreadBypass()
|
||||
{
|
||||
return AssetHandler::BypassState > 0;
|
||||
@ -116,7 +132,7 @@ namespace Components
|
||||
|
||||
|
||||
test al, al
|
||||
jnz finishOriginal
|
||||
jnz checkTempAssets
|
||||
|
||||
mov ecx, [esp + 18h] // Asset type
|
||||
mov ebx, [esp + 1Ch] // Filename
|
||||
@ -139,9 +155,28 @@ namespace Components
|
||||
|
||||
test eax, eax
|
||||
jnz finishFound
|
||||
|
||||
checkTempAssets:
|
||||
mov al, AssetHandler::ShouldSearchTempAssets // check to see if enabled
|
||||
test eax, eax
|
||||
jz finishOriginal
|
||||
|
||||
finishOriginal:
|
||||
// Asset not found using custom handlers, redirect to DB_FindXAssetHeader
|
||||
mov ecx, [esp + 18h] // Asset type
|
||||
mov ebx, [esp + 1Ch] // Filename
|
||||
|
||||
push ebx
|
||||
push ecx
|
||||
|
||||
call AssetHandler::FindTemporaryAsset
|
||||
|
||||
add esp, 8h
|
||||
|
||||
test eax, eax
|
||||
jnz finishFound
|
||||
|
||||
finishOriginal:
|
||||
// Asset not found using custom handlers or in temp assets or bypasses were enabled
|
||||
// redirect to DB_FindXAssetHeader
|
||||
mov ebx, ds:6D7190h // InterlockedDecrement
|
||||
mov eax, 40793Bh
|
||||
jmp eax
|
||||
@ -181,7 +216,7 @@ namespace Components
|
||||
}
|
||||
|
||||
// Fix shader const stuff
|
||||
if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359)
|
||||
if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359 && Zones::Version() < 448)
|
||||
{
|
||||
for (int i = 0; i < 48; ++i)
|
||||
{
|
||||
@ -454,6 +489,11 @@ namespace Components
|
||||
Utils::Hook::Set<Game::XAssetEntry*>(0x5BAEA2, entryPool + 1);
|
||||
}
|
||||
|
||||
void AssetHandler::ExposeTemporaryAssets(bool expose)
|
||||
{
|
||||
AssetHandler::ShouldSearchTempAssets = expose;
|
||||
}
|
||||
|
||||
AssetHandler::AssetHandler()
|
||||
{
|
||||
this->reallocateEntryPool();
|
||||
@ -493,6 +533,210 @@ namespace Components
|
||||
|
||||
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
// #define DUMP_TECHSETS
|
||||
#ifdef DUMP_TECHSETS
|
||||
if (type == Game::XAssetType::ASSET_TYPE_VERTEXDECL && !name.empty() && name[0] != ',')
|
||||
{
|
||||
std::filesystem::create_directories("techsets/vertexdecl");
|
||||
|
||||
auto vertexdecl = asset.vertexDecl;
|
||||
|
||||
std::vector<json11::Json> routingData;
|
||||
for (int i = 0; i < vertexdecl->streamCount; i++)
|
||||
{
|
||||
routingData.push_back(json11::Json::object
|
||||
{
|
||||
{ "source", (int)vertexdecl->routing.data[i].source },
|
||||
{ "dest", (int)vertexdecl->routing.data[i].dest },
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<json11::Json> declData;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (vertexdecl->routing.decl[i])
|
||||
{
|
||||
routingData.push_back(int(vertexdecl->routing.decl[i]));
|
||||
}
|
||||
else
|
||||
{
|
||||
routingData.push_back(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
json11::Json vertexData = json11::Json::object
|
||||
{
|
||||
{ "name", vertexdecl->name },
|
||||
{ "streamCount", vertexdecl->streamCount },
|
||||
{ "hasOptionalSource", vertexdecl->hasOptionalSource },
|
||||
{ "routing", routingData },
|
||||
{ "decl", declData },
|
||||
};
|
||||
|
||||
auto stringData = vertexData.dump();
|
||||
|
||||
auto fp = fopen(Utils::String::VA("techsets/vertexdecl/%s.%s.json", vertexdecl->name, Zones::Version() > 276 ? "codo" : "iw4"), "wb");
|
||||
fwrite(&stringData[0], stringData.size(), 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (type == Game::ASSET_TYPE_TECHNIQUE_SET && !name.empty() && name[0] != ',')
|
||||
{
|
||||
std::filesystem::create_directory("techsets");
|
||||
std::filesystem::create_directories("techsets/techniques");
|
||||
|
||||
auto techset = asset.techniqueSet;
|
||||
|
||||
std::vector<json11::Json> techniques;
|
||||
for (int technique = 0; technique < 48; technique++)
|
||||
{
|
||||
auto curTech = techset->techniques[technique];
|
||||
if (curTech)
|
||||
{
|
||||
std::vector<json11::Json> passDataArray;
|
||||
for (int pass = 0; pass < curTech->passCount; pass++)
|
||||
{
|
||||
auto curPass = &curTech->passArray[pass];
|
||||
|
||||
std::vector<json11::Json> argDataArray;
|
||||
for (int arg = 0; arg < curPass->perObjArgCount + curPass->perPrimArgCount + curPass->stableArgCount; arg++)
|
||||
{
|
||||
auto curArg = &curPass->args[arg];
|
||||
|
||||
if (curArg->type == 1 || curArg->type == 7)
|
||||
{
|
||||
std::vector<float> literalConsts;
|
||||
if (curArg->u.literalConst != 0)
|
||||
{
|
||||
literalConsts.push_back(curArg->u.literalConst[0]);
|
||||
literalConsts.push_back(curArg->u.literalConst[1]);
|
||||
literalConsts.push_back(curArg->u.literalConst[2]);
|
||||
literalConsts.push_back(curArg->u.literalConst[3]);
|
||||
}
|
||||
|
||||
json11::Json argData = json11::Json::object
|
||||
{
|
||||
{ "type", curArg->type },
|
||||
{ "value", literalConsts },
|
||||
};
|
||||
argDataArray.push_back(argData);
|
||||
}
|
||||
else if (curArg->type == 3 || curArg->type == 5)
|
||||
{
|
||||
json11::Json argData = json11::Json::object
|
||||
{
|
||||
{ "type", curArg->type },
|
||||
{ "firstRow", curArg->u.codeConst.firstRow },
|
||||
{ "rowCount", curArg->u.codeConst.rowCount },
|
||||
{ "index", curArg->u.codeConst.index },
|
||||
};
|
||||
argDataArray.push_back(argData);
|
||||
}
|
||||
else
|
||||
{
|
||||
json11::Json argData = json11::Json::object
|
||||
{
|
||||
{ "type", curArg->type },
|
||||
{ "value", static_cast<int>(curArg->u.codeSampler) },
|
||||
};
|
||||
argDataArray.push_back(argData);
|
||||
}
|
||||
}
|
||||
|
||||
json11::Json passData = json11::Json::object
|
||||
{
|
||||
{ "perObjArgCount", curPass->perObjArgCount },
|
||||
{ "perPrimArgCount", curPass->perPrimArgCount },
|
||||
{ "stableArgCount", curPass->stableArgCount },
|
||||
{ "args", argDataArray },
|
||||
{ "pixelShader", curPass->pixelShader ? curPass->pixelShader->name : "" },
|
||||
{ "vertexShader", curPass->vertexShader ? curPass->vertexShader->name : "" },
|
||||
{ "vertexDecl", curPass->vertexDecl ? curPass->vertexDecl->name : "" },
|
||||
};
|
||||
passDataArray.push_back(passData);
|
||||
}
|
||||
|
||||
json11::Json techData = json11::Json::object
|
||||
{
|
||||
{ "name", curTech->name },
|
||||
{ "index", technique },
|
||||
{ "flags", curTech->flags },
|
||||
{ "numPasses", curTech->passCount },
|
||||
{ "pass", passDataArray },
|
||||
};
|
||||
|
||||
auto stringData = techData.dump();
|
||||
|
||||
auto fp = fopen(Utils::String::VA("techsets/techniques/%s.%s.json", curTech->name, Zones::Version() > 276 ? "codo" : "iw4"), "wb");
|
||||
fwrite(&stringData[0], stringData.size(), 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
json11::Json techsetTechnique = json11::Json::object
|
||||
{
|
||||
{ "name", curTech->name },
|
||||
{ "index", technique },
|
||||
};
|
||||
techniques.push_back(techsetTechnique);
|
||||
}
|
||||
else
|
||||
{
|
||||
techniques.push_back(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
json11::Json techsetData = json11::Json::object
|
||||
{
|
||||
{ "name", techset->name },
|
||||
{ "techniques", techniques },
|
||||
};
|
||||
|
||||
auto stringData = techsetData.dump();
|
||||
|
||||
auto fp = fopen(Utils::String::VA("techsets/%s.%s.json", techset->name, Zones::Version() > 276 ? "codo" : "iw4"), "wb");
|
||||
fwrite(&stringData[0], stringData.size(), 1, fp);
|
||||
fclose(fp);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 460)
|
||||
{
|
||||
auto techset = asset.techniqueSet;
|
||||
if (techset)
|
||||
{
|
||||
for (int t = 0; t < 48; t++)
|
||||
{
|
||||
if (techset->techniques[t])
|
||||
{
|
||||
for (int p = 0; p < techset->techniques[t]->passCount; p++)
|
||||
{
|
||||
for (int a = 0; a < techset->techniques[t]->passArray[p].perObjArgCount +
|
||||
techset->techniques[t]->passArray[p].perPrimArgCount +
|
||||
techset->techniques[t]->passArray[p].stableArgCount; a++)
|
||||
{
|
||||
auto arg = &techset->techniques[t]->passArray[p].args[a];
|
||||
if (arg->type == 3 || arg->type == 5)
|
||||
{
|
||||
if (arg->u.codeConst.index > 140)
|
||||
{
|
||||
OutputDebugStringA(Utils::String::VA("codeConst %i is out of range for %s::%s[%i]\n", arg->u.codeConst.index,
|
||||
techset->name, techset->techniques[t]->name, p));
|
||||
|
||||
if (!ZoneBuilder::IsEnabled())
|
||||
{
|
||||
__debugbreak();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
if (Dvar::Var("r_noVoid").get<bool>() && type == Game::XAssetType::ASSET_TYPE_XMODEL && name == "void")
|
||||
{
|
||||
asset.model->numLods = 0;
|
||||
@ -500,16 +744,16 @@ namespace Components
|
||||
});
|
||||
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP, 1);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 : 7168);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, ZoneBuilder::IsEnabled() ? 14336 * 2 : 7168);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700 * 2);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200 * 2);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, 14000);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192 * 2);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125 * 2);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PHYSPRESET, 128);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PIXELSHADER, ZoneBuilder::IsEnabled() ? 0x4000 : 10000);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, ZoneBuilder::IsEnabled() ? 0x2000 : 3072);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192 * 2);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXDECL, ZoneBuilder::IsEnabled() ? 0x400 : 196);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_WEAPON, WEAPON_LIMIT);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_STRINGTABLE, 800);
|
||||
@ -519,12 +763,14 @@ namespace Components
|
||||
if (ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MAP_ENTS, 10);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, 8192);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, 8192 * 2);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, 0x2000);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FONT, 32);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_RAWFILE, 2048);
|
||||
Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LEADERBOARD, 500);
|
||||
|
||||
AssetHandler::RegisterInterface(new Assets::IFont_s());
|
||||
AssetHandler::RegisterInterface(new Assets::IWeapon());
|
||||
AssetHandler::RegisterInterface(new Assets::IXModel());
|
||||
AssetHandler::RegisterInterface(new Assets::IFxWorld());
|
||||
AssetHandler::RegisterInterface(new Assets::IMapEnts());
|
||||
@ -535,8 +781,9 @@ namespace Components
|
||||
AssetHandler::RegisterInterface(new Assets::ISndCurve());
|
||||
AssetHandler::RegisterInterface(new Assets::IMaterial());
|
||||
AssetHandler::RegisterInterface(new Assets::IMenuList());
|
||||
AssetHandler::RegisterInterface(new Assets::IclipMap_t());
|
||||
AssetHandler::RegisterInterface(new Assets::ImenuDef_t());
|
||||
AssetHandler::RegisterInterface(new Assets::IclipMap_t());
|
||||
AssetHandler::RegisterInterface(new Assets::ITracerDef());
|
||||
AssetHandler::RegisterInterface(new Assets::IPhysPreset());
|
||||
AssetHandler::RegisterInterface(new Assets::IXAnimParts());
|
||||
AssetHandler::RegisterInterface(new Assets::IFxEffectDef());
|
||||
|
@ -39,8 +39,13 @@ namespace Components
|
||||
|
||||
static void ResetBypassState();
|
||||
|
||||
static void ExposeTemporaryAssets(bool expose);
|
||||
|
||||
static void OffsetToAlias(Utils::Stream::Offset* offset);
|
||||
|
||||
private:
|
||||
static thread_local int BypassState;
|
||||
static bool ShouldSearchTempAssets;
|
||||
|
||||
static std::map<std::string, Game::XAssetHeader> TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT];
|
||||
|
||||
@ -55,12 +60,11 @@ namespace Components
|
||||
static void RegisterInterface(IAsset* iAsset);
|
||||
|
||||
static Game::XAssetHeader FindAsset(Game::XAssetType type, const char* filename);
|
||||
static Game::XAssetHeader FindTemporaryAsset(Game::XAssetType type, const char* filename);
|
||||
static bool IsAssetEligible(Game::XAssetType type, Game::XAssetHeader* asset);
|
||||
static void FindAssetStub();
|
||||
static void AddAssetStub();
|
||||
|
||||
static void OffsetToAlias(Utils::Stream::Offset* offset);
|
||||
|
||||
static void StoreEmptyAsset(Game::XAssetType type, const char* name);
|
||||
static void StoreEmptyAssetStub();
|
||||
|
||||
@ -76,6 +80,7 @@ namespace Components
|
||||
}
|
||||
|
||||
#include "AssetInterfaces/IFont_s.hpp"
|
||||
#include "AssetInterfaces/IWeapon.hpp"
|
||||
#include "AssetInterfaces/IXModel.hpp"
|
||||
#include "AssetInterfaces/IFxWorld.hpp"
|
||||
#include "AssetInterfaces/IMapEnts.hpp"
|
||||
@ -86,8 +91,9 @@ namespace Components
|
||||
#include "AssetInterfaces/IMaterial.hpp"
|
||||
#include "AssetInterfaces/ISndCurve.hpp"
|
||||
#include "AssetInterfaces/IMenuList.hpp"
|
||||
#include "AssetInterfaces/ImenuDef_t.hpp"
|
||||
#include "AssetInterfaces/IclipMap_t.hpp"
|
||||
#include "AssetInterfaces/ImenuDef_t.hpp"
|
||||
#include "AssetInterfaces/ITracerDef.hpp"
|
||||
#include "AssetInterfaces/IPhysPreset.hpp"
|
||||
#include "AssetInterfaces/IXAnimParts.hpp"
|
||||
#include "AssetInterfaces/IFxEffectDef.hpp"
|
||||
@ -105,3 +111,4 @@ namespace Components
|
||||
#include "AssetInterfaces/IMaterialVertexShader.hpp"
|
||||
#include "AssetInterfaces/IStructuredDataDefSet.hpp"
|
||||
#include "AssetInterfaces/IMaterialVertexDeclaration.hpp"
|
||||
|
||||
|
@ -1,7 +1,56 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
|
||||
void IMaterialPixelShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
}
|
||||
|
||||
void IMaterialPixelShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
{
|
||||
header->pixelShader = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).pixelShader;
|
||||
}
|
||||
|
||||
void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File psFile(Utils::String::VA("ps/%s.iw4xPS", name.data()));
|
||||
if (!psFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), psFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xPIXL", 8))
|
||||
{
|
||||
Components::Logger::Error(0, "Reading pixel shader '%s' failed, header is invalid!", name.data());
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error("Reading pixel shader '%s' failed, expected version is %d, but it was %d!", name.data(), atoi(IW4X_TECHSET_VERSION), atoi(version.data()));
|
||||
}
|
||||
|
||||
Game::MaterialPixelShader* asset = reader.readObject<Game::MaterialPixelShader>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->prog.loadDef.program)
|
||||
{
|
||||
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
|
||||
}
|
||||
|
||||
header->pixelShader = asset;
|
||||
}
|
||||
|
||||
void IMaterialPixelShader::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::MaterialPixelShader, 16);
|
||||
|
@ -8,5 +8,9 @@ namespace Assets
|
||||
virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_PIXELSHADER; };
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
};
|
||||
}
|
||||
|
@ -1,9 +1,144 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IMaterialTechniqueSet::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
{
|
||||
header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet;
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::MaterialPass, 20);
|
||||
|
||||
Components::FileSystem::File techFile(Utils::String::VA("techniques/%s.iw4xTech", name.data()));
|
||||
if (!techFile.exists()) {
|
||||
*tech = nullptr;
|
||||
Components::Logger::Print("Warning: Missing technique '%s'\n", name.data());
|
||||
return;
|
||||
}
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), techFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xTECH", 8))
|
||||
{
|
||||
Components::Logger::Error(0, "Reading technique '%s' failed, header is invalid!", name.data());
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error("Reading technique '%s' failed, expected version is %d, but it was %d!", name.data(), atoi(IW4X_TECHSET_VERSION), atoi(version.data()));
|
||||
}
|
||||
|
||||
unsigned short flags = reader.read<unsigned short>();
|
||||
unsigned short passCount = reader.read<unsigned short>();
|
||||
|
||||
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passCount - 1)));
|
||||
|
||||
asset->name = builder->getAllocator()->duplicateString(name);
|
||||
asset->flags = flags;
|
||||
asset->passCount = passCount;
|
||||
|
||||
Game::MaterialPass* passes = reader.readArray<Game::MaterialPass>(passCount);
|
||||
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * passCount);
|
||||
|
||||
for (unsigned short i = 0; i < asset->passCount; i++)
|
||||
{
|
||||
Game::MaterialPass* pass = &asset->passArray[i];
|
||||
|
||||
if (pass->vertexDecl)
|
||||
{
|
||||
const char* declName = reader.readCString();
|
||||
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
|
||||
}
|
||||
|
||||
if (pass->vertexShader)
|
||||
{
|
||||
const char* vsName = reader.readCString();
|
||||
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
|
||||
|
||||
}
|
||||
|
||||
if (pass->pixelShader)
|
||||
{
|
||||
const char* psName = reader.readCString();
|
||||
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
|
||||
}
|
||||
|
||||
pass->args = reader.readArray<Game::MaterialShaderArgument>(pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount);
|
||||
|
||||
for (int j = 0; j < pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount; j++)
|
||||
{
|
||||
if (pass->args[j].type == 1 || pass->args[j].type == 7)
|
||||
{
|
||||
pass->args[j].u.literalConst = reader.readArray<float>(4);
|
||||
}
|
||||
|
||||
if (pass->args[j].type == 3 || pass->args[j].type == 5)
|
||||
{
|
||||
pass->args[j].u.codeConst.index = *reader.readObject<unsigned short>();
|
||||
pass->args[j].u.codeConst.firstRow = *reader.readObject<unsigned char>();
|
||||
pass->args[j].u.codeConst.rowCount = *reader.readObject<unsigned char>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*tech = asset;
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File tsFile(Utils::String::VA("techsets/%s.iw4xTS", name.data()));
|
||||
if (!tsFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), tsFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xTSET", 8))
|
||||
{
|
||||
Components::Logger::Error(0, "Reading techset '%s' failed, header is invalid!", name.data());
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error("Reading techset '%s' failed, expected version is %d, but it was %d!", name.data(), atoi(IW4X_TECHSET_VERSION), atoi(version.data()));
|
||||
}
|
||||
|
||||
Game::MaterialTechniqueSet* asset = reader.readObject<Game::MaterialTechniqueSet>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
for (int i = 0; i < 48; i++)
|
||||
{
|
||||
if (asset->techniques[i])
|
||||
{
|
||||
const char* techName = reader.readCString();
|
||||
this->loadBinaryTechnique(&asset->techniques[i], techName, builder);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
header->techniqueSet = asset;
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
{
|
||||
Game::MaterialTechniqueSet* asset = header.techniqueSet;
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i)
|
||||
|
@ -9,5 +9,11 @@ namespace Assets
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
|
||||
void loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,50 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IMaterialVertexDeclaration::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
}
|
||||
|
||||
void IMaterialVertexDeclaration::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
{
|
||||
header->vertexDecl = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).vertexDecl;
|
||||
}
|
||||
|
||||
void IMaterialVertexDeclaration::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File declFile(Utils::String::VA("decl/%s.iw4xDECL", name.data()));
|
||||
if (!declFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), declFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xDECL", 8))
|
||||
{
|
||||
Components::Logger::Error(0, "Reading vertex declaration '%s' failed, header is invalid!", name.data());
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error("Reading vertex declaration '%s' failed, expected version is %d, but it was %d!", name.data(), atoi(IW4X_TECHSET_VERSION), atoi(version.data()));
|
||||
}
|
||||
|
||||
Game::MaterialVertexDeclaration* asset = reader.readObject<Game::MaterialVertexDeclaration>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
header->vertexDecl = asset;
|
||||
}
|
||||
|
||||
void IMaterialVertexDeclaration::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::MaterialVertexDeclaration, 100);
|
||||
|
@ -8,5 +8,9 @@ namespace Assets
|
||||
virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_VERTEXDECL; };
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
};
|
||||
}
|
||||
|
@ -1,7 +1,55 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IMaterialVertexShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
}
|
||||
|
||||
void IMaterialVertexShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
{
|
||||
header->vertexShader = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).vertexShader;
|
||||
}
|
||||
|
||||
void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File vsFile(Utils::String::VA("vs/%s.iw4xVS", name.data()));
|
||||
if (!vsFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), vsFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xVERT", 8))
|
||||
{
|
||||
Components::Logger::Error(0, "Reading vertex shader '%s' failed, header is invalid!", name.data());
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error("Reading vertex shader '%s' failed, expected version is %d, but it was %d!", name.data(), atoi(IW4X_TECHSET_VERSION), atoi(version.data()));
|
||||
}
|
||||
|
||||
Game::MaterialVertexShader* asset = reader.readObject<Game::MaterialVertexShader>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->prog.loadDef.program)
|
||||
{
|
||||
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
|
||||
}
|
||||
|
||||
header->vertexShader = asset;
|
||||
}
|
||||
|
||||
void IMaterialVertexShader::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::MaterialVertexShader, 16);
|
||||
|
@ -8,5 +8,9 @@ namespace Assets
|
||||
virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_VERTEXSHADER; };
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
};
|
||||
}
|
||||
|
@ -2,6 +2,36 @@
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IMenuList::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Utils::Memory::Allocator* allocator = builder->getAllocator();
|
||||
|
||||
// actually gets the whole list
|
||||
auto menus = Components::Menus::LoadMenu(name);
|
||||
if (menus.empty()) return;
|
||||
|
||||
// Allocate new menu list
|
||||
Game::MenuList* newList = allocator->allocate<Game::MenuList>();
|
||||
if (!newList) return;
|
||||
|
||||
newList->menus = allocator->allocateArray<Game::menuDef_t*>(menus.size());
|
||||
if (!newList->menus)
|
||||
{
|
||||
allocator->free(newList);
|
||||
return;
|
||||
}
|
||||
|
||||
newList->name = allocator->duplicateString(name);
|
||||
newList->menuCount = menus.size();
|
||||
|
||||
// Copy new menus
|
||||
for (unsigned int i = 0; i < menus.size(); ++i)
|
||||
{
|
||||
newList->menus[i] = menus[i].second;
|
||||
}
|
||||
|
||||
header->menuList = newList;
|
||||
}
|
||||
void IMenuList::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::MenuList *asset = header.menuList;
|
||||
|
@ -9,6 +9,6 @@ namespace Assets
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
// virtual void load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
};
|
||||
}
|
||||
|
44
src/Components/Modules/AssetInterfaces/ITracerDef.cpp
Normal file
44
src/Components/Modules/AssetInterfaces/ITracerDef.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void ITracerDef::load(Game::XAssetHeader* /*header*/, const std::string& /*name*/, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
{
|
||||
return; // don't load from filesystem right now
|
||||
}
|
||||
|
||||
void ITracerDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::TracerDef* asset = header.tracerDef;
|
||||
|
||||
if (asset->material)
|
||||
{
|
||||
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->material);
|
||||
}
|
||||
}
|
||||
|
||||
void ITracerDef::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::TracerDef, 0x70);
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
Game::TracerDef* asset = header.tracerDef;
|
||||
Game::TracerDef* dest = buffer->dest<Game::TracerDef>();
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
buffer->saveString(builder->getAssetName(this->getType(), asset->name));
|
||||
Utils::Stream::ClearPointer(&dest->name);
|
||||
}
|
||||
|
||||
if (asset->material)
|
||||
{
|
||||
dest->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->material).material;
|
||||
}
|
||||
|
||||
buffer->popBlock();
|
||||
}
|
||||
}
|
14
src/Components/Modules/AssetInterfaces/ITracerDef.hpp
Normal file
14
src/Components/Modules/AssetInterfaces/ITracerDef.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
class ITracerDef : public Components::AssetHandler::IAsset
|
||||
{
|
||||
public:
|
||||
virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_TRACER; };
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
};
|
||||
}
|
702
src/Components/Modules/AssetInterfaces/IWeapon.cpp
Normal file
702
src/Components/Modules/AssetInterfaces/IWeapon.cpp
Normal file
@ -0,0 +1,702 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IWeapon::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
{
|
||||
// Try loading raw weapon
|
||||
if (Components::FileSystem::File(Utils::String::VA("weapons/mp/%s", name.data())).exists())
|
||||
{
|
||||
// let the function see temporary assets when calling DB_FindXAssetHeader during the loading function
|
||||
// otherwise it fails to link things properly
|
||||
Components::AssetHandler::ExposeTemporaryAssets(true);
|
||||
header->data = Game::BG_LoadWeaponDef_LoadObj(name.data());
|
||||
Components::AssetHandler::ExposeTemporaryAssets(false);
|
||||
}
|
||||
}
|
||||
|
||||
void IWeapon::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::WeaponCompleteDef* asset = header.weapon;
|
||||
|
||||
// convert all script strings
|
||||
if (asset->hideTags)
|
||||
{
|
||||
for (char i = 0; i < 32; ++i)
|
||||
{
|
||||
if (asset->hideTags[i] == NULL) break; // no more strings
|
||||
builder->addScriptString(asset->hideTags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->weapDef->notetrackSoundMapKeys)
|
||||
{
|
||||
for (char i = 0; i < 16; ++i)
|
||||
{
|
||||
if (asset->weapDef->notetrackSoundMapKeys[i] == NULL) break; // no more strings
|
||||
builder->addScriptString(asset->weapDef->notetrackSoundMapKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->weapDef->notetrackSoundMapValues)
|
||||
{
|
||||
for (char i = 0; i < 16; ++i)
|
||||
{
|
||||
if (asset->weapDef->notetrackSoundMapValues[i] == NULL) break; // no more strings
|
||||
builder->addScriptString(asset->weapDef->notetrackSoundMapValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->weapDef->notetrackRumbleMapKeys)
|
||||
{
|
||||
for (char i = 0; i < 16; ++i)
|
||||
{
|
||||
if (asset->weapDef->notetrackRumbleMapKeys[i] == NULL) break; // no more strings
|
||||
builder->addScriptString(asset->weapDef->notetrackRumbleMapKeys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->weapDef->notetrackRumbleMapValues)
|
||||
{
|
||||
for (char i = 0; i < 16; ++i)
|
||||
{
|
||||
if (asset->weapDef->notetrackRumbleMapValues[i] == NULL) break; // no more strings
|
||||
builder->addScriptString(asset->weapDef->notetrackRumbleMapValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// now load all sub-assets properly
|
||||
if (asset->killIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->killIcon);
|
||||
if (asset->dpadIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->dpadIcon);
|
||||
if (asset->weapDef->reticleCenter) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->reticleCenter);
|
||||
if (asset->weapDef->reticleSide) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->reticleSide);
|
||||
if (asset->weapDef->hudIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->hudIcon);
|
||||
if (asset->weapDef->pickupIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->pickupIcon);
|
||||
if (asset->weapDef->ammoCounterIcon) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->ammoCounterIcon);
|
||||
if (asset->weapDef->overlayMaterial) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterial);
|
||||
if (asset->weapDef->overlayMaterialLowRes) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterialLowRes);
|
||||
if (asset->weapDef->overlayMaterialEMP) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterialEMP);
|
||||
if (asset->weapDef->overlayMaterialEMPLowRes) builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->weapDef->overlayMaterialEMPLowRes);
|
||||
|
||||
if (asset->weapDef->gunXModel)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (asset->weapDef->gunXModel[i]) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->gunXModel[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->weapDef->handXModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->handXModel);
|
||||
|
||||
if (asset->weapDef->worldModel)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (asset->weapDef->worldModel[i]) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->worldModel[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->weapDef->worldClipModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->worldClipModel);
|
||||
if (asset->weapDef->rocketModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->rocketModel);
|
||||
if (asset->weapDef->knifeModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->knifeModel);
|
||||
if (asset->weapDef->worldKnifeModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->worldKnifeModel);
|
||||
if (asset->weapDef->projectileModel) builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->weapDef->projectileModel);
|
||||
|
||||
if (asset->weapDef->physCollmap) builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, asset->weapDef->physCollmap);
|
||||
|
||||
if (asset->weapDef->tracerType) builder->loadAsset(Game::XAssetType::ASSET_TYPE_TRACER, asset->weapDef->tracerType);
|
||||
|
||||
if (asset->weapDef->viewFlashEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->viewFlashEffect);
|
||||
if (asset->weapDef->worldFlashEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->worldFlashEffect);
|
||||
if (asset->weapDef->viewShellEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->viewShellEjectEffect);
|
||||
if (asset->weapDef->worldShellEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->worldShellEjectEffect);
|
||||
if (asset->weapDef->viewLastShotEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->viewLastShotEjectEffect);
|
||||
if (asset->weapDef->worldLastShotEjectEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->worldLastShotEjectEffect);
|
||||
if (asset->weapDef->projExplosionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projExplosionEffect);
|
||||
if (asset->weapDef->projDudEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projDudEffect);
|
||||
if (asset->weapDef->projTrailEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projTrailEffect);
|
||||
if (asset->weapDef->projBeaconEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projBeaconEffect);
|
||||
if (asset->weapDef->projIgnitionEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->projIgnitionEffect);
|
||||
if (asset->weapDef->turretOverheatEffect) builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, asset->weapDef->turretOverheatEffect);
|
||||
}
|
||||
|
||||
void IWeapon::writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer)
|
||||
{
|
||||
AssertSize(Game::WeaponDef, 0x684);
|
||||
|
||||
Game::WeaponDef* dest = buffer->dest<Game::WeaponDef>();
|
||||
buffer->save(def);
|
||||
|
||||
if (def->szOverlayName)
|
||||
{
|
||||
buffer->saveString(def->szOverlayName);
|
||||
Utils::Stream::ClearPointer(&dest->szOverlayName);
|
||||
}
|
||||
|
||||
if (def->gunXModel)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::XModel** pointerTable = buffer->dest<Game::XModel*>();
|
||||
buffer->saveMax(16 * sizeof(Game::XModel*));
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (!def->gunXModel[i])
|
||||
{
|
||||
pointerTable[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
pointerTable[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->gunXModel[i]).model;
|
||||
}
|
||||
Utils::Stream::ClearPointer(&dest->gunXModel);
|
||||
}
|
||||
|
||||
if (def->handXModel)
|
||||
{
|
||||
dest->handXModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->handXModel).model;
|
||||
}
|
||||
|
||||
if (def->szXAnimsRightHanded)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
int* poinerTable = buffer->dest<int>();
|
||||
buffer->saveMax(37 * sizeof(char*)); // array of 37 string pointers
|
||||
for (int i = 0; i < 37; i++)
|
||||
{
|
||||
if (!def->szXAnimsRightHanded[i]) {
|
||||
poinerTable[i] = 0; // clear poiner if there isn't a string here
|
||||
continue;
|
||||
}
|
||||
|
||||
// save string if it is present
|
||||
buffer->saveString(def->szXAnimsRightHanded[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->szXAnimsRightHanded);
|
||||
}
|
||||
|
||||
if (def->szXAnimsLeftHanded)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
int* poinerTable = buffer->dest<int>();
|
||||
buffer->saveMax(37 * sizeof(char*)); // array of 37 string pointers
|
||||
for (int i = 0; i < 37; i++)
|
||||
{
|
||||
if (!def->szXAnimsLeftHanded[i]) {
|
||||
poinerTable[i] = 0; // clear poiner if there isn't a string here
|
||||
continue;
|
||||
}
|
||||
|
||||
// save string if it is present
|
||||
buffer->saveString(def->szXAnimsLeftHanded[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->szXAnimsLeftHanded);
|
||||
}
|
||||
|
||||
if (def->szModeName)
|
||||
{
|
||||
buffer->saveString(def->szModeName);
|
||||
Utils::Stream::ClearPointer(&dest->szModeName);
|
||||
}
|
||||
|
||||
if (def->notetrackSoundMapKeys)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackSoundMapKeys, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackSoundMapKeys);
|
||||
}
|
||||
|
||||
if (def->notetrackSoundMapValues)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackSoundMapValues, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackSoundMapValues);
|
||||
}
|
||||
|
||||
if (def->notetrackRumbleMapKeys)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackRumbleMapKeys, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapKeys);
|
||||
}
|
||||
|
||||
if (def->notetrackRumbleMapValues)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackRumbleMapValues, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapValues);
|
||||
}
|
||||
|
||||
if (def->viewFlashEffect)
|
||||
{
|
||||
dest->viewFlashEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->viewFlashEffect).fx;
|
||||
}
|
||||
|
||||
if (def->worldFlashEffect)
|
||||
{
|
||||
dest->worldFlashEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->worldFlashEffect).fx;
|
||||
}
|
||||
|
||||
// This is compressed because I don't want to write the same piece of code 47 times
|
||||
// TODO: verify that this is saving the aliases correctly because the old code looks wrong and this looks right but the old code worked so go figure
|
||||
Game::snd_alias_list_t ** allSounds = &def->pickupSound;
|
||||
Game::snd_alias_list_t ** allSoundsDest = &dest->pickupSound;
|
||||
for (int i = 0; i < 47; i++) {
|
||||
if (!allSounds[i]) continue;
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||
buffer->saveString(allSounds[i]->aliasName);
|
||||
Utils::Stream::ClearPointer(&allSoundsDest[i]);
|
||||
}
|
||||
|
||||
if (def->bounceSound)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
int* ptrs = buffer->dest<int>();
|
||||
buffer->saveMax(37 * sizeof(Game::snd_alias_list_t*));
|
||||
|
||||
for (int i = 0; i < 37; i++)
|
||||
{
|
||||
if (!def->bounceSound[i])
|
||||
{
|
||||
ptrs[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
buffer->saveMax(sizeof(Game::snd_alias_list_t*));
|
||||
buffer->saveString(def->bounceSound[i]->aliasName);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->bounceSound);
|
||||
}
|
||||
|
||||
if (def->viewShellEjectEffect)
|
||||
{
|
||||
dest->viewShellEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->viewShellEjectEffect).fx;
|
||||
}
|
||||
|
||||
if (def->worldShellEjectEffect)
|
||||
{
|
||||
dest->worldShellEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->worldShellEjectEffect).fx;
|
||||
}
|
||||
|
||||
if (def->viewLastShotEjectEffect)
|
||||
{
|
||||
dest->viewLastShotEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->viewLastShotEjectEffect).fx;
|
||||
}
|
||||
|
||||
if (def->worldLastShotEjectEffect)
|
||||
{
|
||||
dest->worldLastShotEjectEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->worldLastShotEjectEffect).fx;
|
||||
}
|
||||
|
||||
if (def->reticleCenter)
|
||||
{
|
||||
dest->reticleCenter = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->reticleCenter).material;
|
||||
}
|
||||
|
||||
if (def->reticleSide)
|
||||
{
|
||||
dest->reticleSide = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->reticleSide).material;
|
||||
}
|
||||
|
||||
if (def->worldModel)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::XModel** pointerTable = buffer->dest<Game::XModel*>();
|
||||
buffer->saveMax(16 * sizeof(Game::XModel*));
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (!def->worldModel[i])
|
||||
{
|
||||
pointerTable[i] = NULL;
|
||||
continue;
|
||||
}
|
||||
pointerTable[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->worldModel[i]).model;
|
||||
}
|
||||
Utils::Stream::ClearPointer(&dest->worldModel);
|
||||
}
|
||||
|
||||
if (def->worldClipModel)
|
||||
{
|
||||
dest->worldClipModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->worldClipModel).model;
|
||||
}
|
||||
|
||||
if (def->rocketModel)
|
||||
{
|
||||
dest->rocketModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->rocketModel).model;
|
||||
}
|
||||
|
||||
if (def->knifeModel)
|
||||
{
|
||||
dest->knifeModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->knifeModel).model;
|
||||
}
|
||||
|
||||
if (def->worldKnifeModel)
|
||||
{
|
||||
dest->worldKnifeModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->worldKnifeModel).model;
|
||||
}
|
||||
|
||||
if (def->hudIcon)
|
||||
{
|
||||
dest->hudIcon = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->hudIcon).material;
|
||||
}
|
||||
|
||||
if (def->pickupIcon)
|
||||
{
|
||||
dest->pickupIcon = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->pickupIcon).material;
|
||||
}
|
||||
|
||||
if (def->ammoCounterIcon)
|
||||
{
|
||||
dest->ammoCounterIcon = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->ammoCounterIcon).material;
|
||||
}
|
||||
|
||||
if (def->szAmmoName)
|
||||
{
|
||||
buffer->saveString(def->szAmmoName);
|
||||
Utils::Stream::ClearPointer(&dest->szAmmoName);
|
||||
}
|
||||
|
||||
if (def->szClipName)
|
||||
{
|
||||
buffer->saveString(def->szClipName);
|
||||
Utils::Stream::ClearPointer(&dest->szClipName);
|
||||
}
|
||||
|
||||
if (def->szSharedAmmoCapName)
|
||||
{
|
||||
buffer->saveString(def->szSharedAmmoCapName);
|
||||
Utils::Stream::ClearPointer(&dest->szSharedAmmoCapName);
|
||||
}
|
||||
|
||||
if (def->overlayMaterial)
|
||||
{
|
||||
dest->overlayMaterial = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterial).material;
|
||||
}
|
||||
|
||||
if (def->overlayMaterialLowRes)
|
||||
{
|
||||
dest->overlayMaterialLowRes = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterialLowRes).material;
|
||||
}
|
||||
|
||||
if (def->overlayMaterialEMP)
|
||||
{
|
||||
dest->overlayMaterialEMP = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterialEMP).material;
|
||||
}
|
||||
|
||||
if (def->overlayMaterialEMPLowRes)
|
||||
{
|
||||
dest->overlayMaterialEMPLowRes = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, def->overlayMaterialEMPLowRes).material;
|
||||
}
|
||||
|
||||
if (def->physCollmap)
|
||||
{
|
||||
dest->physCollmap = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, def->overlayMaterialEMPLowRes).physCollmap;
|
||||
}
|
||||
|
||||
if (def->projectileModel)
|
||||
{
|
||||
dest->projectileModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def->projectileModel).model;
|
||||
}
|
||||
|
||||
if (def->projExplosionEffect)
|
||||
{
|
||||
dest->projExplosionEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projExplosionEffect).fx;
|
||||
}
|
||||
|
||||
if (def->projDudEffect)
|
||||
{
|
||||
dest->projDudEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projDudEffect).fx;
|
||||
}
|
||||
|
||||
if (def->projExplosionSound)
|
||||
{
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->projExplosionSound->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->projExplosionSound);
|
||||
}
|
||||
|
||||
if (def->projDudSound)
|
||||
{
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->projDudSound->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->projDudSound);
|
||||
}
|
||||
|
||||
if (def->parallelBounce)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(def->parallelBounce, 31);
|
||||
Utils::Stream::ClearPointer(&dest->parallelBounce);
|
||||
}
|
||||
|
||||
if (def->perpendicularBounce)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(def->perpendicularBounce, 31);
|
||||
Utils::Stream::ClearPointer(&dest->perpendicularBounce);
|
||||
}
|
||||
|
||||
if (def->projTrailEffect)
|
||||
{
|
||||
dest->projTrailEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projTrailEffect).fx;
|
||||
}
|
||||
|
||||
if (def->projBeaconEffect)
|
||||
{
|
||||
dest->projBeaconEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projBeaconEffect).fx;
|
||||
}
|
||||
|
||||
if (def->projIgnitionEffect)
|
||||
{
|
||||
dest->projIgnitionEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->projIgnitionEffect).fx;
|
||||
}
|
||||
|
||||
if (def->projIgnitionSound)
|
||||
{
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->projIgnitionSound->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->projIgnitionSound);
|
||||
}
|
||||
|
||||
if (def->accuracyGraphName[0])
|
||||
{
|
||||
buffer->saveString(def->accuracyGraphName[0]);
|
||||
Utils::Stream::ClearPointer(&dest->accuracyGraphName[0]);
|
||||
}
|
||||
|
||||
if (def->originalAccuracyGraphKnots[0])
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(def->originalAccuracyGraphKnots[0], def->originalAccuracyGraphKnotCount[0]);
|
||||
Utils::Stream::ClearPointer(&dest->originalAccuracyGraphKnots[0]);
|
||||
}
|
||||
|
||||
if (def->accuracyGraphName[1])
|
||||
{
|
||||
buffer->saveString(def->accuracyGraphName[1]);
|
||||
Utils::Stream::ClearPointer(&dest->accuracyGraphName[1]);
|
||||
}
|
||||
|
||||
if (def->originalAccuracyGraphKnots[1])
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(def->originalAccuracyGraphKnots[1], def->originalAccuracyGraphKnotCount[1]);
|
||||
Utils::Stream::ClearPointer(&dest->originalAccuracyGraphKnots[1]);
|
||||
}
|
||||
|
||||
if (def->szUseHintString)
|
||||
{
|
||||
buffer->saveString(def->szUseHintString);
|
||||
Utils::Stream::ClearPointer(&dest->szUseHintString);
|
||||
}
|
||||
|
||||
if (def->dropHintString)
|
||||
{
|
||||
buffer->saveString(def->dropHintString);
|
||||
Utils::Stream::ClearPointer(&dest->dropHintString);
|
||||
}
|
||||
|
||||
if (def->szScript)
|
||||
{
|
||||
buffer->saveString(def->szScript);
|
||||
Utils::Stream::ClearPointer(&dest->szScript);
|
||||
}
|
||||
|
||||
if (def->locationDamageMultipliers)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(def->locationDamageMultipliers, 20);
|
||||
Utils::Stream::ClearPointer(&dest->locationDamageMultipliers);
|
||||
}
|
||||
|
||||
if (def->fireRumble)
|
||||
{
|
||||
buffer->saveString(def->fireRumble);
|
||||
Utils::Stream::ClearPointer(&dest->fireRumble);
|
||||
}
|
||||
|
||||
if (def->meleeImpactRumble)
|
||||
{
|
||||
buffer->saveString(def->meleeImpactRumble);
|
||||
Utils::Stream::ClearPointer(&dest->meleeImpactRumble);
|
||||
}
|
||||
|
||||
if (def->tracerType)
|
||||
{
|
||||
dest->tracerType = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_TRACER, def->tracerType).tracerDef;
|
||||
}
|
||||
|
||||
if (def->turretOverheatSound)
|
||||
{
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->turretOverheatSound->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->turretOverheatSound);
|
||||
}
|
||||
|
||||
if (def->turretOverheatEffect)
|
||||
{
|
||||
dest->turretOverheatEffect = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, def->turretOverheatEffect).fx;
|
||||
}
|
||||
|
||||
if (def->turretBarrelSpinRumble)
|
||||
{
|
||||
buffer->saveString(def->turretBarrelSpinRumble);
|
||||
Utils::Stream::ClearPointer(&dest->turretBarrelSpinRumble);
|
||||
}
|
||||
|
||||
if (def->turretBarrelSpinMaxSnd)
|
||||
{
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->turretBarrelSpinMaxSnd->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->turretBarrelSpinMaxSnd);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!def->turretBarrelSpinUpSnd[i]) continue;
|
||||
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->turretBarrelSpinUpSnd[i]->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->turretBarrelSpinUpSnd[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!def->turretBarrelSpinDownSnd[i]) continue;
|
||||
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->turretBarrelSpinDownSnd[i]->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->turretBarrelSpinDownSnd[i]);
|
||||
}
|
||||
|
||||
if (def->missileConeSoundAlias)
|
||||
{
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->missileConeSoundAlias->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->missileConeSoundAlias);
|
||||
}
|
||||
|
||||
if (def->missileConeSoundAliasAtBase)
|
||||
{
|
||||
buffer->saveMax(4);
|
||||
buffer->saveString(def->missileConeSoundAliasAtBase->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->missileConeSoundAliasAtBase);
|
||||
}
|
||||
}
|
||||
|
||||
void IWeapon::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::WeaponCompleteDef, 0x74);
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
Game::WeaponCompleteDef* asset = header.weapon;
|
||||
Game::WeaponCompleteDef* dest = buffer->dest<Game::WeaponCompleteDef>();
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
|
||||
if (asset->szInternalName)
|
||||
{
|
||||
buffer->saveString(builder->getAssetName(this->getType(), asset->szInternalName));
|
||||
Utils::Stream::ClearPointer(&dest->szInternalName);
|
||||
}
|
||||
|
||||
if (asset->weapDef)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
IWeapon::writeWeaponDef(asset->weapDef, builder, buffer);
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->weapDef);
|
||||
}
|
||||
|
||||
if (asset->szDisplayName)
|
||||
{
|
||||
buffer->saveString(asset->szDisplayName);
|
||||
Utils::Stream::ClearPointer(&dest->szDisplayName);
|
||||
}
|
||||
|
||||
if (asset->hideTags)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(asset->hideTags, 32);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->hideTags);
|
||||
}
|
||||
|
||||
if (asset->szXAnims)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
int* poinerTable = buffer->dest<int>();
|
||||
buffer->saveMax(37 * sizeof(char*)); // array of 37 string pointers
|
||||
for (int i = 0; i < 37; i++)
|
||||
{
|
||||
if (!asset->szXAnims[i]) {
|
||||
poinerTable[i] = 0; // clear poiner if there isn't a string here
|
||||
continue;
|
||||
}
|
||||
|
||||
// save string if it is present
|
||||
buffer->saveString(asset->szXAnims[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->szXAnims);
|
||||
}
|
||||
|
||||
if (asset->szAltWeaponName)
|
||||
{
|
||||
buffer->saveString(asset->szAltWeaponName);
|
||||
Utils::Stream::ClearPointer(&dest->szAltWeaponName);
|
||||
}
|
||||
|
||||
if (asset->killIcon)
|
||||
{
|
||||
dest->killIcon = builder->saveSubAsset(Game::ASSET_TYPE_MATERIAL, asset->killIcon).material;
|
||||
}
|
||||
|
||||
if (asset->dpadIcon)
|
||||
{
|
||||
dest->dpadIcon = builder->saveSubAsset(Game::ASSET_TYPE_MATERIAL, asset->dpadIcon).material;
|
||||
}
|
||||
|
||||
if (asset->accuracyGraphKnots[0])
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(asset->accuracyGraphKnots[0], asset->accuracyGraphKnotCount[0]);
|
||||
Utils::Stream::ClearPointer(&dest->accuracyGraphKnots[0]);
|
||||
}
|
||||
|
||||
if (asset->accuracyGraphKnots[1])
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(asset->accuracyGraphKnots[1], asset->accuracyGraphKnotCount[1]);
|
||||
Utils::Stream::ClearPointer(&dest->accuracyGraphKnots[1]);
|
||||
}
|
||||
|
||||
buffer->popBlock();
|
||||
}
|
||||
}
|
17
src/Components/Modules/AssetInterfaces/IWeapon.hpp
Normal file
17
src/Components/Modules/AssetInterfaces/IWeapon.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
class IWeapon : public Components::AssetHandler::IAsset
|
||||
{
|
||||
public:
|
||||
virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_WEAPON; };
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
private:
|
||||
void writeWeaponDef(Game::WeaponDef* def, Components::ZoneBuilder::Zone* builder, Utils::Stream* buffer);
|
||||
};
|
||||
}
|
@ -17,7 +17,7 @@ namespace Assets
|
||||
}
|
||||
}
|
||||
|
||||
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader)
|
||||
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (surf->vertInfo.vertsBlend)
|
||||
{
|
||||
@ -48,13 +48,23 @@ namespace Assets
|
||||
}
|
||||
|
||||
// Access index block
|
||||
if (surf->triIndices)
|
||||
{
|
||||
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
|
||||
}
|
||||
if (surf->triIndices)
|
||||
{
|
||||
void* oldPtr = surf->triIndices;
|
||||
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
|
||||
|
||||
if (builder->getAllocator()->isPointerMapped(oldPtr))
|
||||
{
|
||||
surf->triIndices = builder->getAllocator()->getPointer<unsigned short>(oldPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->getAllocator()->mapPointer(oldPtr, surf->triIndices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader)
|
||||
void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (asset->name)
|
||||
{
|
||||
@ -67,7 +77,7 @@ namespace Assets
|
||||
|
||||
for (int i = 0; i < asset->numsurfs; ++i)
|
||||
{
|
||||
this->loadXSurface(&asset->surfs[i], reader);
|
||||
this->loadXSurface(&asset->surfs[i], reader, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,7 +175,7 @@ namespace Assets
|
||||
if (asset->lodInfo[i].modelSurfs)
|
||||
{
|
||||
asset->lodInfo[i].modelSurfs = reader.readObject<Game::XModelSurfs>();
|
||||
this->loadXModelSurfs(asset->lodInfo[i].modelSurfs, &reader);
|
||||
this->loadXModelSurfs(asset->lodInfo[i].modelSurfs, &reader, builder);
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, { asset->lodInfo[i].modelSurfs });
|
||||
|
||||
asset->lodInfo[i].surfs = asset->lodInfo[i].modelSurfs->surfs;
|
||||
@ -293,7 +303,7 @@ namespace Assets
|
||||
}
|
||||
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { asset->physCollmap });
|
||||
//asset->physCollmap = nullptr;
|
||||
// asset->physCollmap = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,8 +12,9 @@ namespace Assets
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
private:
|
||||
void loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader);
|
||||
void loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader);
|
||||
std::map<void*, void*> triIndicies;
|
||||
void loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder);
|
||||
void loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder);
|
||||
void loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader);
|
||||
};
|
||||
}
|
||||
|
@ -81,12 +81,16 @@ namespace Assets
|
||||
|
||||
// Access index block
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_INDEX);
|
||||
if (surf->triIndices)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_16);
|
||||
buffer->saveArray(surf->triIndices, surf->triCount * 3);
|
||||
Utils::Stream::ClearPointer(&destSurf->triIndices);
|
||||
}
|
||||
if (builder->hasPointer(surf->triIndices))
|
||||
{
|
||||
destSurf->triIndices = builder->getPointer(surf->triIndices);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_16);
|
||||
buffer->saveArray(surf->triIndices, surf->triCount * 3);
|
||||
Utils::Stream::ClearPointer(&destSurf->triIndices);
|
||||
}
|
||||
buffer->popBlock();
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,21 @@
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
|
||||
std::unordered_map<std::string, Game::menuDef_t*> ImenuDef_t::LoadedMenus;
|
||||
|
||||
void ImenuDef_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
{
|
||||
// load from disk
|
||||
auto menus = Components::Menus::LoadMenu(Utils::String::VA("ui_mp/%s.menu", name.data()));
|
||||
|
||||
if (menus.size() == 0) return;
|
||||
if (menus.size() > 1) Components::Logger::Print("Menu '%s' on disk has more than one menudef in it. Only saving the first one\n", name.data());
|
||||
|
||||
header->menu = menus[0].second;
|
||||
}
|
||||
|
||||
|
||||
void ImenuDef_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::menuDef_t *asset = header.menu;
|
||||
@ -37,6 +52,10 @@ namespace Assets
|
||||
AssertSize(Game::ExpressionSupportingData, 24);
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("ExpressionSupportingData");
|
||||
#endif
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
Game::ExpressionSupportingData *dest = buffer->dest<Game::ExpressionSupportingData>();
|
||||
@ -107,13 +126,21 @@ namespace Assets
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImenuDef_t::save_Statement_s(Game::Statement_s* asset, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::Statement_s, 24);
|
||||
AssertSize(Game::expressionEntry, 12);
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("Statement_s");
|
||||
#endif
|
||||
|
||||
// Write header data
|
||||
Game::Statement_s *dest = buffer->dest<Game::Statement_s>();
|
||||
buffer->save(asset);
|
||||
@ -121,6 +148,9 @@ namespace Assets
|
||||
// Write statement entries
|
||||
if (asset->entries)
|
||||
{
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("statement entries");
|
||||
#endif
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
// Write entries
|
||||
@ -130,6 +160,9 @@ namespace Assets
|
||||
// Loop through entries
|
||||
for (int i = 0; i < asset->numEntries; ++i)
|
||||
{
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("entry");
|
||||
#endif
|
||||
if (asset->entries[i].type)
|
||||
{
|
||||
switch (asset->entries[i].data.operand.dataType)
|
||||
@ -159,13 +192,23 @@ namespace Assets
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (asset->supportingData)
|
||||
{
|
||||
this->save_ExpressionSupportingData(asset->supportingData, builder);
|
||||
Utils::Stream::ClearPointer(&dest->supportingData);
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImenuDef_t::save_MenuEventHandlerSet(Game::MenuEventHandlerSet* asset, Components::ZoneBuilder::Zone* builder)
|
||||
@ -173,6 +216,10 @@ namespace Assets
|
||||
AssertSize(Game::MenuEventHandlerSet, 8);
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("MenuEventHandlerSet");
|
||||
#endif
|
||||
|
||||
// Write header data
|
||||
Game::MenuEventHandlerSet *destset = buffer->dest<Game::MenuEventHandlerSet>();
|
||||
buffer->save(asset);
|
||||
@ -191,6 +238,9 @@ namespace Assets
|
||||
if (asset->eventHandlers[i])
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("MenuEventHandler");
|
||||
#endif
|
||||
|
||||
// Write menu event handler
|
||||
Game::MenuEventHandler *dest = buffer->dest<Game::MenuEventHandler>();
|
||||
@ -278,11 +328,17 @@ namespace Assets
|
||||
}
|
||||
break;
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&destset->eventHandlers);
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImenuDef_t::save_ItemKeyHandler(Game::ItemKeyHandler* asset, Components::ZoneBuilder::Zone* builder)
|
||||
@ -290,6 +346,10 @@ namespace Assets
|
||||
AssertSize(Game::ItemKeyHandler, 12);
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("ItemKeyHandler");
|
||||
#endif
|
||||
|
||||
while (asset)
|
||||
{
|
||||
// Write header
|
||||
@ -313,6 +373,9 @@ namespace Assets
|
||||
// Next key handler
|
||||
asset = asset->next;
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
#define EVENTHANDLERSET(__indice) \
|
||||
@ -340,6 +403,10 @@ namespace Assets
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("itemDefData_t");
|
||||
#endif
|
||||
|
||||
// feeder
|
||||
if (type == 6)
|
||||
{
|
||||
@ -421,6 +488,10 @@ namespace Assets
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->typeData.data);
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImenuDef_t::save_itemDef_s(Game::itemDef_s *asset, Components::ZoneBuilder::Zone* builder)
|
||||
@ -430,6 +501,15 @@ namespace Assets
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
Game::itemDef_s* dest = buffer->dest<Game::itemDef_s>();
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
if (asset->window.name)
|
||||
buffer->enterStruct(Utils::String::VA("itemDef_s: name = '%s'", asset->window.name));
|
||||
else if (asset->window.background)
|
||||
buffer->enterStruct(Utils::String::VA("itemDef_s: bg = '%s'", asset->window.background->info.name));
|
||||
else
|
||||
buffer->enterStruct("itemDef_s");
|
||||
#endif
|
||||
|
||||
buffer->save(asset);
|
||||
|
||||
// window data
|
||||
@ -458,6 +538,7 @@ namespace Assets
|
||||
buffer->saveString(asset->dvar);
|
||||
Utils::Stream::ClearPointer(&dest->dvar);
|
||||
}
|
||||
|
||||
if (asset->dvarTest)
|
||||
{
|
||||
buffer->saveString(asset->dvarTest);
|
||||
@ -478,6 +559,7 @@ namespace Assets
|
||||
buffer->saveString(asset->enableDvar);
|
||||
Utils::Stream::ClearPointer(&dest->enableDvar);
|
||||
}
|
||||
|
||||
if (asset->localVar)
|
||||
{
|
||||
buffer->saveString(asset->localVar);
|
||||
@ -500,6 +582,9 @@ namespace Assets
|
||||
if (asset->floatExpressions)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("floatExpressions");
|
||||
#endif
|
||||
|
||||
Game::ItemFloatExpression* destExp = buffer->dest<Game::ItemFloatExpression>();
|
||||
buffer->saveArray(asset->floatExpressions, asset->floatExpressionCount);
|
||||
@ -508,10 +593,14 @@ namespace Assets
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
this->save_Statement_s(asset->floatExpressions[i].expression, builder);
|
||||
Utils::Stream::ClearPointer(&destExp->expression);
|
||||
Utils::Stream::ClearPointer(&destExp[i].expression);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->floatExpressions);
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Statements
|
||||
@ -519,16 +608,23 @@ namespace Assets
|
||||
STATEMENT(disabledExp);
|
||||
STATEMENT(textExp);
|
||||
STATEMENT(materialExp);
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ImenuDef_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::menuDef_t, 400);
|
||||
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->enterStruct("ImenuDef_t");
|
||||
#endif
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
Game::menuDef_t* asset = header.menu;
|
||||
Game::menuDef_t* dest = buffer->dest<Game::menuDef_t>();
|
||||
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
@ -603,6 +699,9 @@ namespace Assets
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef WRITE_LOGS
|
||||
buffer->leaveStruct();
|
||||
#endif
|
||||
|
||||
buffer->popBlock();
|
||||
}
|
||||
|
@ -9,7 +9,9 @@ namespace Assets
|
||||
|
||||
virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
// virtual void load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) override;
|
||||
virtual void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
static std::unordered_map<std::string, Game::menuDef_t*> LoadedMenus;
|
||||
|
||||
private:
|
||||
template <typename T> void save_windowDef_t(Game::windowDef_t* asset, T* dest, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -8,6 +8,12 @@ namespace Components
|
||||
Utils::Cryptography::Token Auth::ComputeToken;
|
||||
Utils::Cryptography::ECC::Key Auth::GuidKey;
|
||||
|
||||
std::vector<std::uint64_t> Auth::BannedUids = {
|
||||
0xf4d2c30b712ac6e3,
|
||||
0xf7e33c4081337fa3,
|
||||
0x6f5597f103cc50e9
|
||||
};
|
||||
|
||||
void Auth::Frame()
|
||||
{
|
||||
if (Auth::TokenContainer.generating)
|
||||
@ -53,7 +59,7 @@ namespace Components
|
||||
Auth::TokenContainer.cancel = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Auth::SendConnectDataStub(Game::netsrc_t sock, Game::netadr_t adr, const char *format, int len)
|
||||
{
|
||||
// Ensure our certificate is loaded
|
||||
@ -64,6 +70,13 @@ namespace Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::find(Auth::BannedUids.begin(), Auth::BannedUids.end(), Steam::SteamUser()->GetSteamID().bits) != Auth::BannedUids.end())
|
||||
{
|
||||
Auth::GenerateKey();
|
||||
Logger::SoftError("Your online profile is invalid. A new key has been generated.");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string connectString(format, len);
|
||||
Game::SV_Cmd_TokenizeString(connectString.data());
|
||||
|
||||
@ -86,7 +99,7 @@ namespace Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (Steam::Enabled() && !Dvar::Var("cl_anonymous").get<bool>() && Steam::Proxy::SteamUser_)
|
||||
if (Steam::Enabled() && !Friends::IsInvisible() && !Dvar::Var("cl_anonymous").get<bool>() && Steam::Proxy::SteamUser_)
|
||||
{
|
||||
infostr.set("realsteamId", Utils::String::VA("%llX", Steam::Proxy::SteamUser_->GetSteamID().bits));
|
||||
}
|
||||
@ -187,6 +200,12 @@ namespace Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::find(Auth::BannedUids.begin(), Auth::BannedUids.end(), xuid) != Auth::BannedUids.end())
|
||||
{
|
||||
Network::Send(address, "error\nYour online profile is invalid. Delete your players folder and restart ^2IW4x^7.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (xuid != Auth::GetKeyHash(connectData.publickey()))
|
||||
{
|
||||
Network::Send(address, "error\nXUID doesn't match the certificate!");
|
||||
@ -268,6 +287,14 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Auth::GenerateKey()
|
||||
{
|
||||
Auth::GuidToken.clear();
|
||||
Auth::ComputeToken.clear();
|
||||
Auth::GuidKey = Utils::Cryptography::ECC::GenerateKey(512);
|
||||
Auth::StoreKey();
|
||||
}
|
||||
|
||||
void Auth::LoadKey(bool force)
|
||||
{
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return;
|
||||
@ -287,10 +314,7 @@ namespace Components
|
||||
|
||||
if (!Auth::GuidKey.isValid())
|
||||
{
|
||||
Auth::GuidToken.clear();
|
||||
Auth::ComputeToken.clear();
|
||||
Auth::GuidKey = Utils::Cryptography::ECC::GenerateKey(512);
|
||||
Auth::StoreKey();
|
||||
Auth::GenerateKey();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,8 @@ namespace Components
|
||||
|
||||
static void StoreKey();
|
||||
static void LoadKey(bool force = false);
|
||||
static void GenerateKey();
|
||||
|
||||
static unsigned __int64 GetKeyHash();
|
||||
static unsigned __int64 GetKeyHash(const std::string& key);
|
||||
|
||||
@ -41,7 +43,8 @@ namespace Components
|
||||
static Utils::Cryptography::Token GuidToken;
|
||||
static Utils::Cryptography::Token ComputeToken;
|
||||
static Utils::Cryptography::ECC::Key GuidKey;
|
||||
|
||||
static std::vector<std::uint64_t> BannedUids;
|
||||
|
||||
static void SendConnectDataStub(Game::netsrc_t sock, Game::netadr_t adr, const char *format, int len);
|
||||
static void ParseConnectData(Game::msg_t* msg, Game::netadr_t* addr);
|
||||
static void DirectConnectStub();
|
||||
|
@ -1,12 +1,94 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
#define KEY_MASK_FIRE 1
|
||||
#define KEY_MASK_SPRINT 2
|
||||
#define KEY_MASK_MELEE 4
|
||||
#define KEY_MASK_RELOAD 16
|
||||
#define KEY_MASK_LEANLEFT 64
|
||||
#define KEY_MASK_LEANRIGHT 128
|
||||
#define KEY_MASK_PRONE 256
|
||||
#define KEY_MASK_CROUCH 512
|
||||
#define KEY_MASK_JUMP 1024
|
||||
#define KEY_MASK_ADS_MODE 2048
|
||||
#define KEY_MASK_TEMP_ACTION 4096
|
||||
#define KEY_MASK_HOLDBREATH 8192
|
||||
#define KEY_MASK_FRAG 16384
|
||||
#define KEY_MASK_SMOKE 32768
|
||||
#define KEY_MASK_NIGHTVISION 262144
|
||||
#define KEY_MASK_ADS 524288
|
||||
#define KEY_MASK_USE 0x28
|
||||
|
||||
#define MAX_G_BOTAI_ENTRIES 18
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::vector<std::string> Bots::BotNames;
|
||||
|
||||
typedef struct BotMovementInfo_t
|
||||
{
|
||||
/* Actions */
|
||||
int buttons;
|
||||
/* Movement */
|
||||
int8 forward;
|
||||
int8 right;
|
||||
/* Weapon */
|
||||
unsigned short weapon;
|
||||
} BotMovementInfo_t;
|
||||
|
||||
static BotMovementInfo_t g_botai[MAX_G_BOTAI_ENTRIES];
|
||||
|
||||
struct BotAction_t
|
||||
{
|
||||
const char* action;
|
||||
int key;
|
||||
};
|
||||
|
||||
static const BotAction_t BotActions[] =
|
||||
{
|
||||
{ "gostand", KEY_MASK_JUMP },
|
||||
{ "gocrouch", KEY_MASK_CROUCH },
|
||||
{ "goprone", KEY_MASK_PRONE },
|
||||
{ "fire", KEY_MASK_FIRE },
|
||||
{ "melee", KEY_MASK_MELEE },
|
||||
{ "frag", KEY_MASK_FRAG },
|
||||
{ "smoke", KEY_MASK_SMOKE },
|
||||
{ "reload", KEY_MASK_RELOAD },
|
||||
{ "sprint", KEY_MASK_SPRINT },
|
||||
{ "leanleft", KEY_MASK_LEANLEFT },
|
||||
{ "leanright", KEY_MASK_LEANRIGHT },
|
||||
{ "ads", KEY_MASK_ADS_MODE },
|
||||
{ "holdbreath", KEY_MASK_HOLDBREATH },
|
||||
{ "use", KEY_MASK_USE },
|
||||
{ "0", 8 },
|
||||
{ "1", 32 },
|
||||
{ "2", 65536 },
|
||||
{ "3", 131072 },
|
||||
{ "4", 1048576 },
|
||||
{ "5", 2097152 },
|
||||
{ "6", 4194304 },
|
||||
{ "7", 8388608 },
|
||||
{ "8", 16777216 },
|
||||
{ "9", 33554432 },
|
||||
};
|
||||
|
||||
unsigned int Bots::GetClientNum(Game::client_s* cl)
|
||||
{
|
||||
unsigned int num;
|
||||
|
||||
num = ((byte*)cl - (byte*)Game::svs_clients) / sizeof(Game::client_s);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
bool Bots::IsValidClientNum(unsigned int cNum)
|
||||
{
|
||||
return (cNum >= 0) && (cNum < (unsigned int)*Game::svs_numclients);
|
||||
}
|
||||
|
||||
void Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port)
|
||||
{
|
||||
static int botId = 0;
|
||||
const char* botName;
|
||||
|
||||
if (Bots::BotNames.empty())
|
||||
{
|
||||
@ -27,15 +109,19 @@ namespace Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Bots::BotNames.empty())
|
||||
{
|
||||
Bots::BotNames.push_back("bot");
|
||||
}
|
||||
}
|
||||
|
||||
botId %= Bots::BotNames.size();
|
||||
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, Bots::BotNames[botId++].data(), protocol, checksum, statVer, statStuff, port), 0x400);
|
||||
if (!Bots::BotNames.empty())
|
||||
{
|
||||
botId %= Bots::BotNames.size();
|
||||
botName = Bots::BotNames[botId++].data();
|
||||
}
|
||||
else
|
||||
{
|
||||
botName = Utils::String::VA("bot%d", ++botId);
|
||||
}
|
||||
|
||||
strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, botName, protocol, checksum, statVer, statStuff, port), 0x400);
|
||||
}
|
||||
|
||||
void Bots::Spawn(unsigned int count)
|
||||
@ -70,6 +156,241 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Bots::AddMethods()
|
||||
{
|
||||
Script::AddFunction("SetPing", [](Game::scr_entref_t id) // gsc: self SetPing(<int>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_INTEGER)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Needs one integer parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto ping = Game::Scr_GetInt(0);
|
||||
|
||||
if (ping < 0 || ping > 999)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Ping needs to between 0 and 999!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Need to call on a connected player!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1SetPing: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
client->ping = (short)ping;
|
||||
});
|
||||
|
||||
Script::AddFunction("isBot", [](Game::scr_entref_t id) // Usage: <bot> isBot();
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1isBot: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1isBot: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(client->isBot);
|
||||
});
|
||||
|
||||
Script::AddFunction("botStop", [](Game::scr_entref_t id) // Usage: <bot> botStop();
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1botStop: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botStop: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botStop: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
g_botai[clientNum] = { 0 };
|
||||
g_botai[clientNum].weapon = 1;
|
||||
});
|
||||
|
||||
Script::AddFunction("botWeapon", [](Game::scr_entref_t id) // Usage: <bot> botWeapon(<str>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto weapon = Game::Scr_GetString(0);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botWeapon: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (weapon == ""s)
|
||||
{
|
||||
g_botai[clientNum].weapon = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
int weapId = Game::G_GetWeaponIndexForName(weapon);
|
||||
|
||||
g_botai[clientNum].weapon = (unsigned short)weapId;
|
||||
});
|
||||
|
||||
Script::AddFunction("botAction", [](Game::scr_entref_t id) // Usage: <bot> botAction(<str action>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto action = Game::Scr_GetString(0);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
if (action[0] != '+' && action[0] != '-')
|
||||
{
|
||||
Game::Scr_Error("^1botAction: Sign for action must be '+' or '-'.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < sizeof(BotActions) / sizeof(BotAction_t); ++i)
|
||||
{
|
||||
if (strcmp(&action[1], BotActions[i].action))
|
||||
continue;
|
||||
|
||||
if (action[0] == '+')
|
||||
g_botai[clientNum].buttons |= BotActions[i].key;
|
||||
else
|
||||
g_botai[clientNum].buttons &= ~(BotActions[i].key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_Error("^1botAction: Unknown action.\n");
|
||||
});
|
||||
|
||||
Script::AddFunction("botMovement", [](Game::scr_entref_t id) // Usage: <bot> botMovement(<int>, <int>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 2 || Game::Scr_GetType(0) != Game::VAR_INTEGER || Game::Scr_GetType(1) != Game::VAR_INTEGER)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Needs two integer parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto forwardInt = Game::Scr_GetInt(0);
|
||||
auto rightInt = Game::Scr_GetInt(1);
|
||||
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
unsigned int clientNum = GetClientNum(client);
|
||||
|
||||
if (!Bots::IsValidClientNum(clientNum))
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Need to call on a player entity!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->state < 3)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Needs to be connected.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!client->isBot)
|
||||
{
|
||||
Game::Scr_Error("^1botMovement: Can only call on a bot!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (forwardInt > 127)
|
||||
forwardInt = 127;
|
||||
if (forwardInt < -127)
|
||||
forwardInt = -127;
|
||||
if (rightInt > 127)
|
||||
rightInt = 127;
|
||||
if (rightInt < -127)
|
||||
rightInt = -127;
|
||||
|
||||
g_botai[clientNum].forward = (int8)forwardInt;
|
||||
g_botai[clientNum].right = (int8)rightInt;
|
||||
});
|
||||
}
|
||||
|
||||
Bots::Bots()
|
||||
{
|
||||
// Replace connect string
|
||||
@ -78,6 +399,50 @@ namespace Components
|
||||
// Intercept sprintf for the connect string
|
||||
Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick();
|
||||
|
||||
// Stop default behavour of bots spinning and shooting
|
||||
Utils::Hook(0x627021, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x627241, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
|
||||
// zero the bot command array
|
||||
for (int i = 0; i < MAX_G_BOTAI_ENTRIES; i++)
|
||||
{
|
||||
g_botai[i] = { 0 };
|
||||
g_botai[i].weapon = 1; // prevent the bots from defaulting to the 'none' weapon
|
||||
}
|
||||
|
||||
// have the bots perform the command every server frame
|
||||
Scheduler::OnFrame([]()
|
||||
{
|
||||
if (!Game::SV_Loaded())
|
||||
return;
|
||||
|
||||
int time = *Game::svs_time;
|
||||
int numClients = *Game::svs_numclients;
|
||||
for (int i = 0; i < numClients; ++i)
|
||||
{
|
||||
Game::client_t* client = &Game::svs_clients[i];
|
||||
|
||||
if (client->state < 3)
|
||||
continue;
|
||||
|
||||
if (!client->isBot)
|
||||
continue;
|
||||
|
||||
Game::usercmd_s ucmd = { 0 };
|
||||
|
||||
ucmd.serverTime = time;
|
||||
|
||||
ucmd.buttons = g_botai[i].buttons;
|
||||
ucmd.forwardmove = g_botai[i].forward;
|
||||
ucmd.rightmove = g_botai[i].right;
|
||||
ucmd.weapon = g_botai[i].weapon;
|
||||
|
||||
client->deltaMessage = client->outgoingSequence - 1;
|
||||
|
||||
Game::SV_ClientThink(client, &ucmd);
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("spawnBot", [](Command::Params* params)
|
||||
{
|
||||
unsigned int count = 1;
|
||||
@ -103,6 +468,8 @@ namespace Components
|
||||
|
||||
Bots::Spawn(count);
|
||||
});
|
||||
|
||||
Bots::AddMethods();
|
||||
}
|
||||
|
||||
Bots::~Bots()
|
||||
|
@ -7,6 +7,8 @@ namespace Components
|
||||
public:
|
||||
Bots();
|
||||
~Bots();
|
||||
static unsigned int GetClientNum(Game::client_s*);
|
||||
static bool IsValidClientNum(unsigned int);
|
||||
|
||||
private:
|
||||
static std::vector<std::string> BotNames;
|
||||
@ -14,5 +16,7 @@ namespace Components
|
||||
static void BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
|
||||
|
||||
static void Spawn(unsigned int count);
|
||||
|
||||
static void AddMethods();
|
||||
};
|
||||
}
|
||||
|
@ -220,6 +220,12 @@ namespace Components
|
||||
// Table lookup stuff
|
||||
Utils::Hook(0x62DCC1, CardTitles::TableLookupByRowHookStub).install()->quick();
|
||||
Utils::Hook::Nop(0x62DCC6, 1);
|
||||
|
||||
// This is placed here in case the anticheat has been disabled!
|
||||
// This checks specifically for launching the process suspended to inject a dll
|
||||
#if !defined(DISABLE_ANTICHEAT)
|
||||
AntiCheat::CheckStartupTime();
|
||||
#endif
|
||||
}
|
||||
|
||||
CardTitles::~CardTitles()
|
||||
|
@ -12,7 +12,7 @@ namespace Components
|
||||
|
||||
std::lock_guard<std::mutex> _(Changelog::Mutex);
|
||||
Changelog::Lines.clear();
|
||||
std::string data = Utils::Cache::GetFile("/iw4/changelog.txt");
|
||||
std::string data = Utils::Cache::GetFile("/develop/CHANGELOG.md");
|
||||
|
||||
if (data.empty())
|
||||
{
|
||||
|
@ -13,7 +13,7 @@ namespace Components
|
||||
~ClanTags();
|
||||
|
||||
private:
|
||||
static std::string ClanTags::Tags[18];
|
||||
static std::string Tags[18];
|
||||
|
||||
static void DrawPlayerNameOnScoreboard();
|
||||
|
||||
|
176
src/Components/Modules/Client.cpp
Normal file
176
src/Components/Modules/Client.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
void Client::AddFunctions()
|
||||
{
|
||||
//File functions
|
||||
|
||||
Script::AddFunction("fileWrite", [](Game::scr_entref_t) // gsc: fileWrite(<filepath>, <string>, <mode>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
auto text = Game::Scr_GetString(1);
|
||||
auto mode = Game::Scr_GetString(2);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileWrite: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileWrite: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode != "append"s && mode != "write"s)
|
||||
{
|
||||
Game::Com_Printf(0, "^3fileWrite: mode not defined or was wrong, defaulting to 'write'\n");
|
||||
mode = const_cast<char*>("write");
|
||||
}
|
||||
|
||||
if (mode == "write"s)
|
||||
{
|
||||
FileSystem::FileWriter(path).write(text);
|
||||
}
|
||||
else if (mode == "append"s)
|
||||
{
|
||||
FileSystem::FileWriter(path, true).write(text);
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("fileRead", [](Game::scr_entref_t) // gsc: fileRead(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FileSystem::FileReader(path).exists())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRead: file not found!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::Scr_AddString(FileSystem::FileReader(path).getBuffer().data());
|
||||
});
|
||||
|
||||
Script::AddFunction("fileExists", [](Game::scr_entref_t) // gsc: fileExists(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileExists: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileExists: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Game::Scr_AddInt(FileSystem::FileReader(path).exists());
|
||||
});
|
||||
|
||||
Script::AddFunction("fileRemove", [](Game::scr_entref_t) // gsc: fileRemove(<filepath>)
|
||||
{
|
||||
std::string path = Game::Scr_GetString(0);
|
||||
|
||||
if (path.empty())
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRemove: filepath not defined!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<const char*> queryStrings = { R"(..)", R"(../)", R"(..\)" };
|
||||
for (auto i = 0u; i < queryStrings.size(); i++)
|
||||
{
|
||||
if (path.find(queryStrings[i]) != std::string::npos)
|
||||
{
|
||||
Game::Com_Printf(0, "^1fileRemove: directory traversal is not allowed!\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto p = std::filesystem::path(path);
|
||||
std::string folder = p.parent_path().string();
|
||||
std::string file = p.filename().string();
|
||||
Game::Scr_AddInt(FileSystem::DeleteFile(folder, file));
|
||||
});
|
||||
}
|
||||
|
||||
void Client::AddMethods()
|
||||
{
|
||||
// Client methods
|
||||
|
||||
Script::AddFunction("getIp", [](Game::scr_entref_t id) // gsc: self getIp()
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
|
||||
if (client->state >= 3)
|
||||
{
|
||||
std::string ip = Game::NET_AdrToString(client->addr);
|
||||
if (ip.find_first_of(":") != std::string::npos)
|
||||
ip.erase(ip.begin() + ip.find_first_of(":"), ip.end()); // erase port
|
||||
Game::Scr_AddString(ip.data());
|
||||
}
|
||||
});
|
||||
|
||||
Script::AddFunction("getPing", [](Game::scr_entref_t id) // gsc: self getPing()
|
||||
{
|
||||
Game::gentity_t* gentity = Script::getEntFromEntRef(id);
|
||||
Game::client_t* client = Script::getClientFromEnt(gentity);
|
||||
|
||||
if (client->state >= 3)
|
||||
{
|
||||
int ping = (int)client->ping;
|
||||
Game::Scr_AddInt(ping);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Client::AddCommands()
|
||||
{
|
||||
Command::Add("NULL", [](Command::Params*)
|
||||
{
|
||||
return NULL;
|
||||
});
|
||||
}
|
||||
|
||||
Client::Client()
|
||||
{
|
||||
Client::AddFunctions();
|
||||
Client::AddMethods();
|
||||
Client::AddCommands();
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
17
src/Components/Modules/Client.hpp
Normal file
17
src/Components/Modules/Client.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class Client : public Component
|
||||
{
|
||||
public:
|
||||
Client();
|
||||
~Client();
|
||||
|
||||
private:
|
||||
|
||||
static void AddFunctions();
|
||||
static void AddMethods();
|
||||
static void AddCommands();
|
||||
};
|
||||
}
|
@ -25,7 +25,7 @@ namespace Components
|
||||
|
||||
char* Command::ClientParams::get(size_t index)
|
||||
{
|
||||
if (index >= this->length()) return "";
|
||||
if (index >= this->length()) return const_cast<char*>("");
|
||||
return Game::cmd_argv[this->commandId][index];
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ namespace Components
|
||||
|
||||
char* Command::ServerParams::get(size_t index)
|
||||
{
|
||||
if (index >= this->length()) return "";
|
||||
if (index >= this->length()) return const_cast<char*>("");
|
||||
return Game::cmd_argv_sv[this->commandId][index];
|
||||
}
|
||||
|
||||
@ -160,6 +160,10 @@ namespace Components
|
||||
{
|
||||
AssertSize(Game::cmd_function_t, 24);
|
||||
|
||||
static int toastDurationShort = 1000;
|
||||
static int toastDurationMedium = 2500;
|
||||
static int toastDurationLong = 5000;
|
||||
|
||||
// Disable native noclip command
|
||||
Utils::Hook::Nop(0x474846, 5);
|
||||
|
||||
@ -169,21 +173,21 @@ namespace Components
|
||||
if (!Game::CL_IsCgameInitialized() || clientNum >= 18 || clientNum < 0 || !Game::g_entities[clientNum].client)
|
||||
{
|
||||
Logger::Print("You are not hosting a match!\n");
|
||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", 3000);
|
||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", toastDurationMedium);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||
{
|
||||
Logger::Print("Cheats disabled!\n");
|
||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", 3000);
|
||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", toastDurationMedium);
|
||||
return;
|
||||
}
|
||||
|
||||
Game::g_entities[clientNum].client->flags ^= Game::PLAYER_FLAG_NOCLIP;
|
||||
|
||||
Logger::Print("Noclip toggled\n");
|
||||
Toast::Show("cardicon_abduction", "Success", "Noclip toggled", 3000);
|
||||
Toast::Show("cardicon_abduction", "Success", "Noclip toggled", toastDurationShort);
|
||||
});
|
||||
|
||||
Command::Add("ufo", [](Command::Params*)
|
||||
@ -192,21 +196,21 @@ namespace Components
|
||||
if (!Game::CL_IsCgameInitialized() || clientNum >= 18 || clientNum < 0 || !Game::g_entities[clientNum].client)
|
||||
{
|
||||
Logger::Print("You are not hosting a match!\n");
|
||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", 3000);
|
||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", toastDurationMedium);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||
{
|
||||
Logger::Print("Cheats disabled!\n");
|
||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", 3000);
|
||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", toastDurationMedium);
|
||||
return;
|
||||
}
|
||||
|
||||
Game::g_entities[clientNum].client->flags ^= Game::PLAYER_FLAG_UFO;
|
||||
|
||||
Logger::Print("UFO toggled\n");
|
||||
Toast::Show("cardicon_abduction", "Success", "UFO toggled", 3000);
|
||||
Toast::Show("cardicon_abduction", "Success", "UFO toggled", toastDurationShort);
|
||||
});
|
||||
|
||||
Command::Add("setviewpos", [](Command::Params* params)
|
||||
@ -215,21 +219,21 @@ namespace Components
|
||||
if (!Game::CL_IsCgameInitialized() || clientNum >= 18 || clientNum < 0 || !Game::g_entities[clientNum].client)
|
||||
{
|
||||
Logger::Print("You are not hosting a match!\n");
|
||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", 3000);
|
||||
Toast::Show("cardicon_stop", "Error", "You are not hosting a match!", toastDurationMedium);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||
{
|
||||
Logger::Print("Cheats disabled!\n");
|
||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", 3000);
|
||||
Toast::Show("cardicon_stop", "Error", "Cheats disabled!", toastDurationMedium);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params->length() != 4 && params->length() != 6)
|
||||
{
|
||||
Logger::Print("Invalid coordinate specified!\n");
|
||||
Toast::Show("cardicon_stop", "Error", "Invalid coordinate specified!", 3000);
|
||||
Toast::Show("cardicon_stop", "Error", "Invalid coordinate specified!", toastDurationMedium);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -250,7 +254,7 @@ namespace Components
|
||||
|
||||
// Logging that will spam the console and screen if people use cinematics
|
||||
//Logger::Print("Successfully teleported player!\n");
|
||||
//Toast::Show("cardicon_abduction", "Success", "You have been teleported!", 3000);
|
||||
//Toast::Show("cardicon_abduction", "Success", "You have been teleported!", toastDurationShort);
|
||||
});
|
||||
|
||||
Command::Add("openLink", [](Command::Params* params)
|
||||
|
@ -537,7 +537,7 @@ namespace Components
|
||||
Console::Console()
|
||||
{
|
||||
// Console '%s: %s> ' string
|
||||
Utils::Hook::Set<char*>(0x5A44B4, "IW4x: " VERSION "> ");
|
||||
Utils::Hook::Set<const char*>(0x5A44B4, "IW4x: " VERSION "> ");
|
||||
|
||||
// Patch console color
|
||||
static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f };
|
||||
|
@ -748,7 +748,7 @@ namespace Components
|
||||
{
|
||||
if (Dedicated::IsEnabled()) return;
|
||||
|
||||
Dvar::Register<bool>("r_useD3D9Ex", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use extended d3d9 interface!");
|
||||
Dvar::Register<bool>("r_useD3D9Ex", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Use extended d3d9 interface!");
|
||||
|
||||
// Hook Interface creation
|
||||
Utils::Hook::Set(0x6D74D0, D3D9Ex::Direct3DCreate9Stub);
|
||||
|
@ -120,65 +120,7 @@ namespace Components
|
||||
if (ev == MG_EV_RECV)
|
||||
{
|
||||
size_t bytes = static_cast<size_t>(*reinterpret_cast<int*>(ev_data));
|
||||
fDownload->receivedBytes += bytes;
|
||||
fDownload->download->downBytes += bytes;
|
||||
fDownload->download->timeStampBytes += bytes;
|
||||
|
||||
double progress = 0;
|
||||
if (fDownload->download->totalBytes)
|
||||
{
|
||||
progress = (100.0 / fDownload->download->totalBytes) * fDownload->download->downBytes;
|
||||
}
|
||||
|
||||
static unsigned int dlIndex, dlSize, dlProgress;
|
||||
dlIndex = fDownload->index + 1;
|
||||
dlSize = fDownload->download->files.size();
|
||||
dlProgress = static_cast<unsigned int>(progress);
|
||||
|
||||
static bool framePushed = false;
|
||||
|
||||
if (!framePushed)
|
||||
{
|
||||
framePushed = true;
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
framePushed = false;
|
||||
Dvar::Var("ui_dl_progress").set(Utils::String::VA("(%d/%d) %d%%", dlIndex, dlSize, dlProgress));
|
||||
});
|
||||
}
|
||||
|
||||
int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp;
|
||||
if (delta > 300)
|
||||
{
|
||||
bool doFormat = fDownload->download->lastTimeStamp != 0;
|
||||
fDownload->download->lastTimeStamp = Game::Sys_Milliseconds();
|
||||
|
||||
size_t dataLeft = fDownload->download->totalBytes - fDownload->download->downBytes;
|
||||
|
||||
int timeLeft = 0;
|
||||
if (fDownload->download->timeStampBytes)
|
||||
{
|
||||
double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes) * delta;
|
||||
timeLeft = static_cast<int>(timeLeftD);
|
||||
}
|
||||
|
||||
if (doFormat)
|
||||
{
|
||||
static size_t dlTsBytes;
|
||||
static int dlDelta, dlTimeLeft;
|
||||
dlTimeLeft = timeLeft;
|
||||
dlDelta = delta;
|
||||
dlTsBytes = fDownload->download->timeStampBytes;
|
||||
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
Dvar::Var("ui_dl_timeLeft").set(Utils::String::FormatTimeSpan(dlTimeLeft));
|
||||
Dvar::Var("ui_dl_transRate").set(Utils::String::FormatBandwidth(dlTsBytes, dlDelta));
|
||||
});
|
||||
}
|
||||
|
||||
fDownload->download->timeStampBytes = 0;
|
||||
}
|
||||
Download::DownloadProgress(fDownload, bytes);
|
||||
}
|
||||
|
||||
if (ev == MG_EV_HTTP_REPLY)
|
||||
@ -259,7 +201,7 @@ namespace Components
|
||||
+ (download->isPrivate ? ("?password=" + download->hashedPassword) : "");
|
||||
}
|
||||
|
||||
Logger::Print("Downloading from url %s", url.data());
|
||||
Logger::Print("Downloading from url %s\n", url.data());
|
||||
|
||||
Download::FileDownload fDownload;
|
||||
fDownload.file = file;
|
||||
@ -270,17 +212,42 @@ namespace Components
|
||||
|
||||
Utils::String::Replace(url, " ", "%20");
|
||||
|
||||
// Just a speedtest ;)
|
||||
//download->totalBytes = 1048576000;
|
||||
//url = "http://speed.hetzner.de/1GB.bin";
|
||||
|
||||
download->valid = true;
|
||||
ZeroMemory(&download->mgr, sizeof download->mgr);
|
||||
/*ZeroMemory(&download->mgr, sizeof download->mgr);
|
||||
mg_mgr_init(&download->mgr, &fDownload);
|
||||
mg_connect_http(&download->mgr, Download::DownloadHandler, url.data(), nullptr, nullptr);
|
||||
|
||||
while (fDownload.downloading && !fDownload.download->terminateThread)
|
||||
{
|
||||
mg_mgr_poll(&download->mgr, 0);
|
||||
mg_mgr_poll(&download->mgr, 100);
|
||||
}
|
||||
|
||||
mg_mgr_free(&download->mgr);
|
||||
mg_mgr_free(&download->mgr);*/
|
||||
|
||||
fDownload.downloading = true;
|
||||
|
||||
Utils::WebIO webIO;
|
||||
webIO.setProgressCallback([&fDownload, &webIO](size_t bytes, size_t)
|
||||
{
|
||||
if(!fDownload.downloading || fDownload.download->terminateThread)
|
||||
{
|
||||
webIO.cancelDownload();
|
||||
return;
|
||||
}
|
||||
|
||||
Download::DownloadProgress(&fDownload, bytes - fDownload.receivedBytes);
|
||||
});
|
||||
|
||||
bool result = false;
|
||||
fDownload.buffer = webIO.get(url, &result);
|
||||
if (!result) fDownload.buffer.clear();
|
||||
|
||||
fDownload.downloading = false;
|
||||
|
||||
download->valid = false;
|
||||
|
||||
if (fDownload.buffer.size() != file.size || Utils::Cryptography::SHA256::Compute(fDownload.buffer, true) != file.hash)
|
||||
@ -430,6 +397,69 @@ namespace Components
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Download::DownloadProgress(FileDownload* fDownload, size_t bytes)
|
||||
{
|
||||
fDownload->receivedBytes += bytes;
|
||||
fDownload->download->downBytes += bytes;
|
||||
fDownload->download->timeStampBytes += bytes;
|
||||
|
||||
static volatile bool framePushed = false;
|
||||
|
||||
if (!framePushed)
|
||||
{
|
||||
double progress = 0;
|
||||
if (fDownload->download->totalBytes)
|
||||
{
|
||||
progress = (100.0 / fDownload->download->totalBytes) * fDownload->download->downBytes;
|
||||
}
|
||||
|
||||
static unsigned int dlIndex, dlSize, dlProgress;
|
||||
dlIndex = fDownload->index + 1;
|
||||
dlSize = fDownload->download->files.size();
|
||||
dlProgress = static_cast<unsigned int>(progress);
|
||||
|
||||
framePushed = true;
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
framePushed = false;
|
||||
Dvar::Var("ui_dl_progress").set(Utils::String::VA("(%d/%d) %d%%", dlIndex, dlSize, dlProgress));
|
||||
});
|
||||
}
|
||||
|
||||
int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp;
|
||||
if (delta > 300)
|
||||
{
|
||||
bool doFormat = fDownload->download->lastTimeStamp != 0;
|
||||
fDownload->download->lastTimeStamp = Game::Sys_Milliseconds();
|
||||
|
||||
auto dataLeft = fDownload->download->totalBytes - fDownload->download->downBytes;
|
||||
|
||||
int timeLeft = 0;
|
||||
if (fDownload->download->timeStampBytes)
|
||||
{
|
||||
double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes) * delta;
|
||||
timeLeft = static_cast<int>(timeLeftD);
|
||||
}
|
||||
|
||||
if (doFormat)
|
||||
{
|
||||
static size_t dlTsBytes;
|
||||
static int dlDelta, dlTimeLeft;
|
||||
dlTimeLeft = timeLeft;
|
||||
dlDelta = delta;
|
||||
dlTsBytes = fDownload->download->timeStampBytes;
|
||||
|
||||
Scheduler::Once([]()
|
||||
{
|
||||
Dvar::Var("ui_dl_timeLeft").set(Utils::String::FormatTimeSpan(dlTimeLeft));
|
||||
Dvar::Var("ui_dl_transRate").set(Utils::String::FormatBandwidth(dlTsBytes, dlDelta));
|
||||
});
|
||||
}
|
||||
|
||||
fDownload->download->timeStampBytes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Download::VerifyPassword(mg_connection *nc, http_message* message)
|
||||
{
|
||||
std::string g_password = Dvar::Var("g_password").get<std::string>();
|
||||
@ -701,9 +731,11 @@ namespace Components
|
||||
//if (!Download::VerifyPassword(nc, reinterpret_cast<http_message*>(ev_data))) return;
|
||||
|
||||
Utils::InfoString status = ServerInfo::GetInfo();
|
||||
Utils::InfoString host = ServerInfo::GetHostInfo();
|
||||
|
||||
std::map<std::string, json11::Json> info;
|
||||
info["status"] = status.to_json();
|
||||
info["host"] = host.to_json();
|
||||
|
||||
std::vector<json11::Json> players;
|
||||
|
||||
@ -932,36 +964,35 @@ namespace Components
|
||||
Download::ScriptDownloads.clear();
|
||||
});
|
||||
|
||||
if (Dedicated::IsEnabled() || Flags::HasFlag("scriptablehttp"))
|
||||
Script::AddFunction("httpGet", [](Game::scr_entref_t)
|
||||
{
|
||||
Script::AddFunction("httpGet", [](Game::scr_entref_t)
|
||||
if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return;
|
||||
if (Game::Scr_GetNumParam() < 1) return;
|
||||
|
||||
std::string url = Game::Scr_GetString(0);
|
||||
unsigned int object = Game::AllocObject();
|
||||
|
||||
Game::Scr_AddObject(object);
|
||||
|
||||
Download::ScriptDownloads.push_back(std::make_shared<ScriptDownload>(url, object));
|
||||
Game::RemoveRefToObject(object);
|
||||
});
|
||||
|
||||
Script::AddFunction("httpCancel", [](Game::scr_entref_t)
|
||||
{
|
||||
if (!Dedicated::IsEnabled() && !Flags::HasFlag("scriptablehttp")) return;
|
||||
if (Game::Scr_GetNumParam() < 1) return;
|
||||
|
||||
unsigned int object = Game::Scr_GetObject(0);
|
||||
for (auto& download : Download::ScriptDownloads)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() < 1) return;
|
||||
|
||||
std::string url = Game::Scr_GetString(0);
|
||||
unsigned int object = Game::AllocObject();
|
||||
|
||||
Game::Scr_AddObject(object);
|
||||
|
||||
Download::ScriptDownloads.push_back(std::make_shared<ScriptDownload>(url, object));
|
||||
Game::RemoveRefToObject(object);
|
||||
});
|
||||
|
||||
Script::AddFunction("httpCancel", [](Game::scr_entref_t)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() < 1) return;
|
||||
|
||||
unsigned int object = Game::Scr_GetObject(0);
|
||||
for (auto& download : Download::ScriptDownloads)
|
||||
if (object == download->getObject())
|
||||
{
|
||||
if (object == download->getObject())
|
||||
{
|
||||
download->cancel();
|
||||
break;
|
||||
}
|
||||
download->cancel();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Download::~Download()
|
||||
|
@ -26,7 +26,7 @@ namespace Components
|
||||
bool terminateThread;
|
||||
bool isMap;
|
||||
bool isPrivate;
|
||||
mg_mgr mgr;
|
||||
//mg_mgr mgr;
|
||||
Network::Address target;
|
||||
std::string hashedPassword;
|
||||
std::string mod;
|
||||
@ -64,7 +64,7 @@ namespace Components
|
||||
if (this->valid)
|
||||
{
|
||||
this->valid = false;
|
||||
mg_mgr_free(&(this->mgr));
|
||||
//mg_mgr_free(&(this->mgr));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -212,6 +212,8 @@ namespace Components
|
||||
static bool Terminate;
|
||||
static bool ServerRunning;
|
||||
|
||||
static void DownloadProgress(FileDownload* fDownload, size_t bytes);
|
||||
|
||||
static bool VerifyPassword(mg_connection *nc, http_message* message);
|
||||
|
||||
static void EventHandler(mg_connection *nc, int ev, void *ev_data);
|
||||
|
@ -27,7 +27,7 @@ namespace Components
|
||||
return const_cast<char*>(this->dvar->current.string);
|
||||
}
|
||||
|
||||
return "";
|
||||
return const_cast<char*>("");
|
||||
}
|
||||
template <> const char* Dvar::Var::get()
|
||||
{
|
||||
@ -138,6 +138,10 @@ namespace Components
|
||||
{
|
||||
return Game::Dvar_RegisterInt(name, value, min, max, flag.val, description);
|
||||
}
|
||||
template<> static Dvar::Var Dvar::Register(const char* name, float value, float min, float max, Dvar::Flag flag, const char* description)
|
||||
{
|
||||
return Game::Dvar_RegisterFloat(name, value, min, max, flag.val, description);
|
||||
}
|
||||
|
||||
void Dvar::OnInit(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
@ -223,6 +227,9 @@ namespace Components
|
||||
|
||||
// un-cheat cg_fov and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// un-cheat cg_fovscale and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8E68, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// un-cheat cg_debugInfoCornerOffset and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8FC2, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
@ -251,6 +258,12 @@ namespace Components
|
||||
// Hook dvar 'name' registration
|
||||
Utils::Hook(0x40531C, Dvar::RegisterName, HOOK_CALL).install()->quick();
|
||||
|
||||
// un-cheat safeArea_* and add archive flags
|
||||
Utils::Hook::Xor<INT>(0x42E3F5, Game::dvar_flag::DVAR_FLAG_READONLY | Game::dvar_flag::DVAR_FLAG_SAVED); //safeArea_adjusted_horizontal
|
||||
Utils::Hook::Xor<INT>(0x42E423, Game::dvar_flag::DVAR_FLAG_READONLY | Game::dvar_flag::DVAR_FLAG_SAVED); //safeArea_adjusted_vertical
|
||||
Utils::Hook::Xor<BYTE>(0x42E398, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); //safeArea_horizontal
|
||||
Utils::Hook::Xor<BYTE>(0x42E3C4, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); //safeArea_vertical
|
||||
|
||||
// Don't allow setting cheat protected dvars via menus
|
||||
Utils::Hook(0x63C897, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x63CA96, Dvar::SetFromStringByNameExternal, HOOK_CALL).install()->quick();
|
||||
|
@ -76,7 +76,7 @@ namespace Components
|
||||
errorStr = Utils::String::VA("Fatal error (0x%08X) at 0x%08X.", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
||||
}
|
||||
|
||||
Exception::SuspendProcess();
|
||||
//Exception::SuspendProcess();
|
||||
|
||||
bool doFullDump = Flags::HasFlag("bigdumps") || Flags::HasFlag("reallybigdumps");
|
||||
/*if (!doFullDump)
|
||||
@ -131,15 +131,15 @@ namespace Components
|
||||
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
AntiCheat::InstallLibHook();
|
||||
#endif
|
||||
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE)
|
||||
//if (ExceptionInfo->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE)
|
||||
{
|
||||
TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
AntiCheat::InstallLibHook();
|
||||
#endif
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
@ -231,26 +231,6 @@ namespace Components
|
||||
auto oldHandler = Exception::Hook();
|
||||
Logger::Print("Old exception handler was 0x%010X.\n", oldHandler);
|
||||
});
|
||||
|
||||
// Check if folder exists && crash-helper exists
|
||||
|
||||
if (Utils::IO::DirectoryExists("minidumps\\") && Utils::IO::FileExists("crash-helper.exe"))
|
||||
{
|
||||
if (!Utils::IO::DirectoryIsEmpty("minidumps\\"))
|
||||
{
|
||||
STARTUPINFOA sInfo;
|
||||
PROCESS_INFORMATION pInfo;
|
||||
|
||||
ZeroMemory(&sInfo, sizeof(sInfo));
|
||||
ZeroMemory(&pInfo, sizeof(pInfo));
|
||||
sInfo.cb = sizeof(sInfo);
|
||||
|
||||
CreateProcessA("crash-helper.exe", const_cast<char*>(Utils::String::VA("crash-helper.exe %s", VERSION)), nullptr, nullptr, false, NULL, nullptr, nullptr, &sInfo, &pInfo);
|
||||
|
||||
if (pInfo.hThread && pInfo.hThread != INVALID_HANDLE_VALUE) CloseHandle(pInfo.hThread);
|
||||
if (pInfo.hProcess && pInfo.hProcess != INVALID_HANDLE_VALUE) CloseHandle(pInfo.hProcess);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Exception::~Exception()
|
||||
|
@ -18,6 +18,7 @@ namespace Components
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
|
||||
static __declspec(noreturn) void ErrorLongJmp(jmp_buf _Buf, int _Value);
|
||||
static __declspec(noreturn) void LongJmp(jmp_buf _Buf, int _Value);
|
||||
static void DebugMinidumpCommand(Command::Params*);
|
||||
|
||||
static int MiniDumpType;
|
||||
static Utils::Hook SetFilterHook;
|
||||
|
@ -320,7 +320,7 @@ namespace Components
|
||||
Zones::SetVersion(*version);
|
||||
|
||||
// Allow loading of codo versions
|
||||
if (*version >= VERSION_ALPHA2 && *version <= 360)
|
||||
if ((*version >= VERSION_ALPHA2 && *version <= 360) || (*version >= 423 && *version <= 461))
|
||||
{
|
||||
*version = XFILE_VERSION;
|
||||
}
|
||||
@ -369,7 +369,7 @@ namespace Components
|
||||
|
||||
unsigned long outLen = sizeof(FastFiles::CurrentKey);
|
||||
rsa_import(FastFiles::ZoneKey, sizeof(FastFiles::ZoneKey), &key);
|
||||
rsa_decrypt_key_ex(encKey, 256, FastFiles::CurrentKey.data, &outLen, nullptr, NULL, hash, (Zones::Version() >= 359 ? 1 : 2), &stat, &key);
|
||||
rsa_decrypt_key_ex(encKey, 256, FastFiles::CurrentKey.data, &outLen, nullptr, NULL, hash, Zones::Version() >= 359 ? 1 : 2, &stat, &key);
|
||||
rsa_free(&key);
|
||||
|
||||
ctr_start(aes, FastFiles::CurrentKey.iv, FastFiles::CurrentKey.key, sizeof(FastFiles::CurrentKey.key), 0, 0, &FastFiles::CurrentCTR);
|
||||
@ -484,7 +484,7 @@ namespace Components
|
||||
}
|
||||
#endif
|
||||
|
||||
void FastFiles::Load_XSurfaceArray(int atStreamStart, int /*count*/)
|
||||
void FastFiles::Load_XSurfaceArray(int atStreamStart, [[maybe_unused]] int count)
|
||||
{
|
||||
// read the actual count from the varXModelSurfs ptr
|
||||
auto surface = *reinterpret_cast<Game::XModelSurfs**>(0x0112A95C);
|
||||
|
@ -172,11 +172,11 @@ namespace Components
|
||||
return fileList;
|
||||
}
|
||||
|
||||
void FileSystem::DeleteFile(const std::string& folder, const std::string& file)
|
||||
bool FileSystem::DeleteFile(const std::string& folder, const std::string& file)
|
||||
{
|
||||
char path[MAX_PATH] = { 0 };
|
||||
Game::FS_BuildPathToFile(Dvar::Var("fs_basepath").get<const char*>(), reinterpret_cast<char*>(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast<char**>(&path));
|
||||
Game::FS_Remove(path);
|
||||
return Game::FS_Remove(path);
|
||||
}
|
||||
|
||||
int FileSystem::ReadFile(const char* path, char** buffer)
|
||||
@ -263,10 +263,18 @@ namespace Components
|
||||
void FileSystem::FsRestartSync(int a1, int a2)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
Maps::GetUserMap()->freeIwd();
|
||||
Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
||||
Maps::GetUserMap()->reloadIwd();
|
||||
}
|
||||
|
||||
void FileSystem::FsShutdownSync(int a1)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
Maps::GetUserMap()->freeIwd();
|
||||
Utils::Hook::Call<void(int)>(0x4A46C0)(a1); // FS_Shutdown
|
||||
}
|
||||
|
||||
void FileSystem::DelayLoadImagesSync()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
@ -322,6 +330,10 @@ namespace Components
|
||||
Utils::Hook(0x4C8609, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // FS_ConditionalRestart
|
||||
Utils::Hook(0x5AC68E, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // CL_ParseServerMessage
|
||||
|
||||
// Synchronize filesystem stops
|
||||
Utils::Hook(0x461A55, FileSystem::FsShutdownSync, HOOK_CALL).install()->quick(); // FS_Restart
|
||||
Utils::Hook(0x4D40DB, FileSystem::FsShutdownSync, HOOK_CALL).install()->quick(); // Com_Quitf
|
||||
|
||||
// Synchronize db image loading
|
||||
Utils::Hook(0x415AB8, FileSystem::DelayLoadImagesSync, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4D32BC, FileSystem::LoadTextureSync, HOOK_CALL).install()->quick();
|
||||
|
@ -90,7 +90,7 @@ namespace Components
|
||||
|
||||
static std::vector<std::string> GetFileList(const std::string& path, const std::string& extension);
|
||||
static std::vector<std::string> GetSysFileList(const std::string& path, const std::string& extension, bool folders = false);
|
||||
static void DeleteFile(const std::string& folder, const std::string& file);
|
||||
static bool DeleteFile(const std::string& folder, const std::string& file);
|
||||
|
||||
private:
|
||||
static std::mutex Mutex;
|
||||
@ -109,6 +109,7 @@ namespace Components
|
||||
|
||||
static void FsStartupSync(const char* a1);
|
||||
static void FsRestartSync(int a1, int a2);
|
||||
static void FsShutdownSync(int a1);
|
||||
static void DelayLoadImagesSync();
|
||||
static int LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
|
||||
|
||||
|
@ -124,7 +124,7 @@ namespace Components
|
||||
|
||||
void Friends::UpdateState(bool force)
|
||||
{
|
||||
if (Dvar::Var("cl_anonymous").get<bool>() || !Steam::Enabled()) return;
|
||||
if (Dvar::Var("cl_anonymous").get<bool>() || Friends::IsInvisible() || !Steam::Enabled()) return;
|
||||
|
||||
if (force)
|
||||
{
|
||||
@ -186,7 +186,7 @@ namespace Components
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(Friends::Mutex);
|
||||
|
||||
const unsigned int modId = *reinterpret_cast<unsigned int*>("IW4x") | 0x80000000;
|
||||
const unsigned int modId = *reinterpret_cast<unsigned int*>(const_cast<char*>("IW4x")) | 0x80000000;
|
||||
|
||||
// Split up the list
|
||||
for (auto entry : Friends::FriendsList)
|
||||
@ -228,7 +228,7 @@ namespace Components
|
||||
|
||||
void Friends::SetPresence(const std::string& key, const std::string& value)
|
||||
{
|
||||
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamUtils && !Dvar::Var("cl_anonymous").get<bool>() && Steam::Enabled())
|
||||
if (Steam::Proxy::ClientFriends && Steam::Proxy::SteamUtils && !Dvar::Var("cl_anonymous").get<bool>() && !Friends::IsInvisible() && Steam::Enabled())
|
||||
{
|
||||
Friends::SetRawPresence(key.data(), value.data());
|
||||
}
|
||||
@ -494,6 +494,11 @@ namespace Components
|
||||
return appId;
|
||||
}
|
||||
|
||||
bool Friends::IsInvisible()
|
||||
{
|
||||
return Friends::InitialState == 7;
|
||||
}
|
||||
|
||||
void Friends::UpdateTimeStamp()
|
||||
{
|
||||
Friends::SetPresence("iw4x_playing", Utils::String::VA("%d", Steam::SteamUtils()->GetServerRealTime()));
|
||||
@ -696,10 +701,10 @@ namespace Components
|
||||
{
|
||||
if (Steam::Proxy::SteamFriends)
|
||||
{
|
||||
Friends::InitialState = Steam::Proxy::SteamFriends->GetPersonaState();
|
||||
Friends::InitialState = Steam::Proxy::SteamFriends->GetFriendPersonaState(Steam::Proxy::SteamUser_->GetSteamID());
|
||||
}
|
||||
|
||||
if (Dvar::Var("cl_anonymous").get<bool>() || !Steam::Enabled())
|
||||
if (Dvar::Var("cl_anonymous").get<bool>() || Friends::IsInvisible() || !Steam::Enabled())
|
||||
{
|
||||
if (Steam::Proxy::ClientFriends)
|
||||
{
|
||||
|
@ -23,6 +23,8 @@ namespace Components
|
||||
|
||||
static int GetGame(SteamID user);
|
||||
|
||||
static bool IsInvisible();
|
||||
|
||||
private:
|
||||
#pragma pack(push, 4)
|
||||
struct FriendRichPresenceUpdate
|
||||
|
@ -11,8 +11,8 @@ namespace Components
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled() || Monitor::IsEnabled() || Loader::IsPerformingUnitTests()) return;
|
||||
|
||||
DWORD oldProtect;
|
||||
std::uint8_t* module = reinterpret_cast<std::uint8_t*>(GetModuleHandle(nullptr));
|
||||
VirtualProtect(module + 0x1000, 0x2D6000, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
std::uint8_t* _module = reinterpret_cast<std::uint8_t*>(GetModuleHandle(nullptr));
|
||||
VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READWRITE, &oldProtect);
|
||||
|
||||
#ifdef COMPILE_IW4MVM
|
||||
client_main::Init();
|
||||
@ -26,7 +26,7 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
VirtualProtect(module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect);
|
||||
VirtualProtect(_module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect);
|
||||
}
|
||||
|
||||
IW4MVM::~IW4MVM()
|
||||
|
@ -185,15 +185,23 @@ namespace Components
|
||||
"Dasfonia",
|
||||
"Deity",
|
||||
"Dizzy",
|
||||
"Dss0",
|
||||
"H3X1C",
|
||||
"HardNougat",
|
||||
"Homura",
|
||||
"INeedGames",
|
||||
"Killera",
|
||||
"Lithium",
|
||||
"OneFourOne",
|
||||
"quaK",
|
||||
"RaidMax",
|
||||
"Revo",
|
||||
"RezTech",
|
||||
"Shadow the Hedgehog",
|
||||
"Slykuiper",
|
||||
"st0rm",
|
||||
"VVLNT",
|
||||
"X3RX35"
|
||||
};
|
||||
|
||||
static const char* specials[] =
|
||||
@ -202,7 +210,7 @@ namespace Components
|
||||
"aerosoul94",
|
||||
"ReactIW4",
|
||||
"IW4Play",
|
||||
"Rocket V2",
|
||||
"V2",
|
||||
"luckyy"
|
||||
};
|
||||
|
||||
|
451
src/Components/Modules/MapDump.cpp
Normal file
451
src/Components/Modules/MapDump.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class MapDumper
|
||||
{
|
||||
public:
|
||||
MapDumper(Game::GfxWorld* world) : world_(world)
|
||||
{
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
if (!this->world_) return;
|
||||
|
||||
Logger::Print("Exporting '%s'...\n", this->world_->baseName);
|
||||
|
||||
this->parseVertices();
|
||||
this->parseFaces();
|
||||
this->parseStaticModels();
|
||||
|
||||
this->write();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Vertex
|
||||
{
|
||||
Game::vec3_t coordinate;
|
||||
Game::vec2_t texture;
|
||||
Game::vec3_t normal;
|
||||
};
|
||||
|
||||
struct Face
|
||||
{
|
||||
int a{};
|
||||
int b{};
|
||||
int c{};
|
||||
};
|
||||
|
||||
struct FaceList
|
||||
{
|
||||
std::vector<Face> indices{};
|
||||
};
|
||||
|
||||
class File
|
||||
{
|
||||
public:
|
||||
File() {}
|
||||
|
||||
File(const std::string& file)
|
||||
{
|
||||
Utils::IO::WriteFile(file, {});
|
||||
this->stream_ = std::ofstream(file, std::ofstream::out);
|
||||
}
|
||||
|
||||
void append(const std::string& str)
|
||||
{
|
||||
this->stream_.write(str.data(), str.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::ofstream stream_{};
|
||||
};
|
||||
|
||||
Game::GfxWorld* world_{};
|
||||
std::vector<Vertex> vertices_{};
|
||||
std::unordered_map<Game::Material*, FaceList> faces_{};
|
||||
std::vector<Game::Material*> facesOrder_{};
|
||||
|
||||
File object_{};
|
||||
File material_{};
|
||||
|
||||
void transformAxes(Game::vec3_t& vec) const
|
||||
{
|
||||
std::swap(vec[0], vec[1]);
|
||||
std::swap(vec[1], vec[2]);
|
||||
}
|
||||
|
||||
void parseVertices()
|
||||
{
|
||||
Logger::Print("Parsing vertices...\n");
|
||||
|
||||
for (unsigned int i = 0; i < this->world_->draw.vertexCount; ++i)
|
||||
{
|
||||
const auto* vertex = &this->world_->draw.vd.vertices[i];
|
||||
|
||||
Vertex v{};
|
||||
|
||||
v.coordinate[0] = vertex->xyz[0];
|
||||
v.coordinate[1] = vertex->xyz[1];
|
||||
v.coordinate[2] = vertex->xyz[2];
|
||||
this->transformAxes(v.coordinate);
|
||||
|
||||
v.texture[0] = vertex->texCoord[0];
|
||||
v.texture[1] = -vertex->texCoord[1];
|
||||
|
||||
Game::Vec3UnpackUnitVec(vertex->normal, &v.normal);
|
||||
this->transformAxes(v.normal);
|
||||
|
||||
this->vertices_.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void parseFaces()
|
||||
{
|
||||
Logger::Print("Parsing faces...\n");
|
||||
|
||||
for (unsigned int i = 0; i < this->world_->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
const auto* surface = &this->world_->dpvs.surfaces[i];
|
||||
|
||||
const unsigned int vertOffset = surface->tris.firstVertex + 1;
|
||||
const unsigned int indexOffset = surface->tris.baseIndex;
|
||||
|
||||
auto& f = this->getFaceList(surface->material);
|
||||
|
||||
for (unsigned short j = 0; j < surface->tris.triCount; ++j)
|
||||
{
|
||||
Face face{};
|
||||
face.a = this->world_->draw.indices[indexOffset + j * 3 + 0] + vertOffset;
|
||||
face.b = this->world_->draw.indices[indexOffset + j * 3 + 1] + vertOffset;
|
||||
face.c = this->world_->draw.indices[indexOffset + j * 3 + 2] + vertOffset;
|
||||
|
||||
f.indices.push_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FaceList& getFaceList(Game::Material* material)
|
||||
{
|
||||
auto& faceList = this->faces_[material];
|
||||
|
||||
if (this->facesOrder_.size() < this->faces_.size())
|
||||
{
|
||||
this->facesOrder_.push_back(material);
|
||||
}
|
||||
|
||||
return faceList;
|
||||
}
|
||||
|
||||
void performWorldTransformation(const Game::GfxPackedPlacement& placement, Vertex& v) const
|
||||
{
|
||||
Game::MatrixVecMultiply(placement.axis, v.normal, v.normal);
|
||||
Game::Vec3Normalize(v.normal);
|
||||
|
||||
Game::MatrixVecMultiply(placement.axis, v.coordinate, v.coordinate);
|
||||
v.coordinate[0] = v.coordinate[0] * placement.scale + placement.origin[0];
|
||||
v.coordinate[1] = v.coordinate[1] * placement.scale + placement.origin[1];
|
||||
v.coordinate[2] = v.coordinate[2] * placement.scale + placement.origin[2];
|
||||
}
|
||||
|
||||
std::vector<Vertex> parseSurfaceVertices(const Game::XSurface* surface, const Game::GfxPackedPlacement& placement)
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
|
||||
for (unsigned short j = 0; j < surface->vertCount; j++)
|
||||
{
|
||||
const auto *vertex = &surface->verts0[j];
|
||||
|
||||
Vertex v{};
|
||||
|
||||
v.coordinate[0] = vertex->xyz[0];
|
||||
v.coordinate[1] = vertex->xyz[1];
|
||||
v.coordinate[2] = vertex->xyz[2];
|
||||
|
||||
// Why...
|
||||
Game::Vec2UnpackTexCoords(vertex->texCoord, &v.texture);
|
||||
std::swap(v.texture[0], v.texture[1]);
|
||||
v.texture[1] *= -1;
|
||||
|
||||
Game::Vec3UnpackUnitVec(vertex->normal, &v.normal);
|
||||
|
||||
this->performWorldTransformation(placement, v);
|
||||
this->transformAxes(v.coordinate);
|
||||
this->transformAxes(v.normal);
|
||||
|
||||
vertices.push_back(v);
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
std::vector<Face> parseSurfaceFaces(const Game::XSurface* surface) const
|
||||
{
|
||||
std::vector<Face> faces;
|
||||
|
||||
for (unsigned short j = 0; j < surface->triCount; ++j)
|
||||
{
|
||||
Face face{};
|
||||
face.a = surface->triIndices[j * 3 + 0];
|
||||
face.b = surface->triIndices[j * 3 + 1];
|
||||
face.c = surface->triIndices[j * 3 + 2];
|
||||
|
||||
faces.push_back(face);
|
||||
}
|
||||
|
||||
return faces;
|
||||
}
|
||||
|
||||
void removeVertex(const int index, std::vector<Face>& faces, std::vector<Vertex>& vertices) const
|
||||
{
|
||||
vertices.erase(vertices.begin() + index);
|
||||
|
||||
for (auto &face : faces)
|
||||
{
|
||||
if (face.a > index) --face.a;
|
||||
if (face.b > index) --face.b;
|
||||
if (face.c > index) --face.c;
|
||||
}
|
||||
}
|
||||
|
||||
void filterSurfaceVertices(std::vector<Face>& faces, std::vector<Vertex>& vertices) const
|
||||
{
|
||||
for (auto i = 0; i < int(vertices.size()); ++i)
|
||||
{
|
||||
auto referenced = false;
|
||||
|
||||
for (const auto &face : faces)
|
||||
{
|
||||
if (face.a == i || face.b == i || face.c == i)
|
||||
{
|
||||
referenced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!referenced)
|
||||
{
|
||||
this->removeVertex(i--, faces, vertices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseStaticModel(Game::GfxStaticModelDrawInst* model)
|
||||
{
|
||||
for (unsigned char i = 0; i < model->model->numsurfs; ++i)
|
||||
{
|
||||
this->getFaceList(model->model->materialHandles[i]);
|
||||
}
|
||||
|
||||
const auto* lod = &model->model->lodInfo[model->model->numLods - 1];
|
||||
|
||||
const auto baseIndex = this->vertices_.size() + 1;
|
||||
const auto surfIndex = lod->surfIndex;
|
||||
|
||||
assert(lod->modelSurfs->numsurfs <= model->model->numsurfs);
|
||||
|
||||
for (unsigned short i = 0; i < lod->modelSurfs->numsurfs; ++i)
|
||||
{
|
||||
// TODO: Something is still wrong about the models. Probably baseTriIndex and baseVertIndex might help
|
||||
|
||||
const auto* surface = &lod->modelSurfs->surfs[i];
|
||||
auto faces = this->parseSurfaceFaces(surface);
|
||||
auto vertices = this->parseSurfaceVertices(surface, model->placement);
|
||||
this->filterSurfaceVertices(faces, vertices);
|
||||
|
||||
auto& f = this->getFaceList(model->model->materialHandles[i + surfIndex]);
|
||||
|
||||
for (const auto& vertex : vertices)
|
||||
{
|
||||
this->vertices_.push_back(vertex);
|
||||
}
|
||||
|
||||
for (auto face : faces)
|
||||
{
|
||||
face.a += baseIndex;
|
||||
face.b += baseIndex;
|
||||
face.c += baseIndex;
|
||||
f.indices.push_back(std::move(face));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseStaticModels()
|
||||
{
|
||||
Logger::Print("Parsing static models...\n");
|
||||
|
||||
for (unsigned i = 0u; i < this->world_->dpvs.smodelCount; ++i)
|
||||
{
|
||||
this->parseStaticModel(this->world_->dpvs.smodelDrawInsts + i);
|
||||
}
|
||||
}
|
||||
|
||||
void write()
|
||||
{
|
||||
this->object_ = File(Utils::String::VA("raw/mapdump/%s/%s.obj", this->world_->baseName, this->world_->baseName));
|
||||
this->material_ = File(Utils::String::VA("raw/mapdump/%s/%s.mtl", this->world_->baseName, this->world_->baseName));
|
||||
|
||||
this->object_.append("# Generated by IW4x\n");
|
||||
this->object_.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
this->object_.append(Utils::String::VA("o %s\n", this->world_->baseName));
|
||||
this->object_.append(Utils::String::VA("mtllib %s.mtl\n\n", this->world_->baseName));
|
||||
|
||||
this->material_.append("# IW4x MTL File\n");
|
||||
this->material_.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
|
||||
this->writeVertices();
|
||||
this->writeFaces();
|
||||
|
||||
Logger::Print("Writing files...\n");
|
||||
|
||||
this->object_ = {};
|
||||
this->material_ = {};
|
||||
}
|
||||
|
||||
void writeVertices()
|
||||
{
|
||||
Logger::Print("Writing vertices...\n");
|
||||
this->object_.append("# Vertices\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("v %.6f %.6f %.6f\n", vertex.coordinate[0], vertex.coordinate[1], vertex.coordinate[2]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing texture coordinates...\n");
|
||||
this->object_.append("\n# Texture coordinates\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("vt %.6f %.6f\n", vertex.texture[0], vertex.texture[1]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing normals...\n");
|
||||
this->object_.append("\n# Normals\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("vn %.6f %.6f %.6f\n", vertex.normal[0], vertex.normal[1], vertex.normal[2]));
|
||||
}
|
||||
|
||||
this->object_.append("\n");
|
||||
}
|
||||
|
||||
void writeMaterial(Game::Material* material)
|
||||
{
|
||||
std::string name = material->info.name;
|
||||
|
||||
const auto pos = name.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
this->object_.append(Utils::String::VA("usemtl %s\n", name.data()));
|
||||
this->object_.append("s off\n");
|
||||
|
||||
Game::GfxImage *image = nullptr;
|
||||
|
||||
for (char l = 0; l < material->textureCount; ++l)
|
||||
{
|
||||
if (material->textureTable[l].nameStart == 'c' && material->textureTable[l].nameEnd == 'p')
|
||||
{
|
||||
image = material->textureTable[l].u.image; // Hopefully our colorMap
|
||||
}
|
||||
}
|
||||
|
||||
if (!image)
|
||||
{
|
||||
Logger::Print("Failed to get color map for material: %s\n", material->info.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This is still wrong.
|
||||
if (image->mapType == 5 && false)
|
||||
{
|
||||
for (auto i = 0; i < 6; ++i)
|
||||
{
|
||||
IDirect3DSurface9* surface = nullptr;
|
||||
image->texture.cubemap->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &surface);
|
||||
|
||||
if (surface)
|
||||
{
|
||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s_%i.png", this->world_->baseName, image->name, i);
|
||||
D3DXSaveSurfaceToFileA(_name.data(), D3DXIFF_PNG, surface, nullptr, nullptr);
|
||||
surface->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", this->world_->baseName, image->name);
|
||||
D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr);
|
||||
}
|
||||
|
||||
this->material_.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
|
||||
this->material_.append("Ka 1.0000 1.0000 1.0000\n");
|
||||
this->material_.append("Kd 1.0000 1.0000 1.0000\n");
|
||||
this->material_.append("illum 1\n");
|
||||
this->material_.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name));
|
||||
this->material_.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name));
|
||||
}
|
||||
|
||||
void writeFaces()
|
||||
{
|
||||
Logger::Print("Writing faces...\n");
|
||||
Utils::IO::CreateDir(Utils::String::VA("raw/mapdump/%s/textures", this->world_->baseName));
|
||||
|
||||
this->material_.append(Utils::String::VA("# Material count: %d\n", this->faces_.size()));
|
||||
|
||||
this->object_.append("# Faces\n");
|
||||
|
||||
for (const auto& material : this->facesOrder_)
|
||||
{
|
||||
this->writeMaterial(material);
|
||||
|
||||
const auto& faces = this->getFaceList(material);
|
||||
for (const auto& index : faces.indices)
|
||||
{
|
||||
const int a = index.a;
|
||||
const int b = index.b;
|
||||
const int c = index.c;
|
||||
|
||||
this->object_.append(Utils::String::VA("f %d/%d/%d %d/%d/%d %d/%d/%d\n", a, a, a, b, b, b, c, c, c));
|
||||
}
|
||||
|
||||
this->object_.append("\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MapDump::MapDump()
|
||||
{
|
||||
Command::Add("dumpmap", [](Command::Params*)
|
||||
{
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::GfxWorld* world = nullptr;
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [](Game::XAssetHeader header, void* world)
|
||||
{
|
||||
*reinterpret_cast<Game::GfxWorld**>(world) = header.gfxWorld;
|
||||
}, &world, false);
|
||||
|
||||
if (world)
|
||||
{
|
||||
MapDumper dumper(world);
|
||||
dumper.dump();
|
||||
|
||||
Logger::Print("Map '%s' exported!\n", world->baseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print("No map loaded, unable to dump anything!\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
10
src/Components/Modules/MapDump.hpp
Normal file
10
src/Components/Modules/MapDump.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class MapDump : public Component
|
||||
{
|
||||
public:
|
||||
MapDump();
|
||||
};
|
||||
}
|
@ -128,7 +128,7 @@ namespace Components
|
||||
|
||||
Maps::SPMap = false;
|
||||
Maps::CurrentMainZone = zoneInfo->name;
|
||||
|
||||
|
||||
Maps::CurrentDependencies.clear();
|
||||
for (auto i = Maps::DependencyList.begin(); i != Maps::DependencyList.end(); ++i)
|
||||
{
|
||||
@ -149,7 +149,7 @@ namespace Components
|
||||
|
||||
std::vector<Game::XZoneInfo> data;
|
||||
Utils::Merge(&data, zoneInfo, zoneCount);
|
||||
|
||||
|
||||
Game::XZoneInfo team;
|
||||
team.allocFlags = zoneInfo->allocFlags;
|
||||
team.freeFlags = zoneInfo->freeFlags;
|
||||
@ -252,12 +252,8 @@ namespace Components
|
||||
asset.mapEnts->entityString = const_cast<char*>(mapEntities.data());
|
||||
asset.mapEnts->numEntityChars = mapEntities.size() + 1;
|
||||
}
|
||||
|
||||
// Apply new mapEnts
|
||||
// This doesn't work, entities are spawned before the patch file is loaded
|
||||
//Maps::OverrideMapEnts(asset.mapEnts);
|
||||
}
|
||||
|
||||
|
||||
// This is broken
|
||||
if ((type == Game::XAssetType::ASSET_TYPE_MENU || type == Game::XAssetType::ASSET_TYPE_MENULIST) && Zones::Version() >= 359)
|
||||
{
|
||||
@ -271,14 +267,13 @@ namespace Components
|
||||
Logger::Print("Waiting for database...\n");
|
||||
while (!Game::Sys_IsDatabaseReady()) std::this_thread::sleep_for(10ms);
|
||||
|
||||
if (!Utils::String::StartsWith(Maps::CurrentMainZone, "mp_") || Maps::SPMap)
|
||||
if (!Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp || !Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp->name ||
|
||||
!Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp->g_glassData || Maps::SPMap)
|
||||
{
|
||||
return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP].gameWorldSp[0].g_glassData;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp[0].g_glassData;
|
||||
return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP].gameWorldSp->g_glassData;
|
||||
}
|
||||
|
||||
return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp->g_glassData;
|
||||
}
|
||||
|
||||
__declspec(naked) void Maps::GetWorldDataStub()
|
||||
@ -529,140 +524,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
// Credit to SE2Dev, as we shouldn't share the code, keep that in debug mode!
|
||||
void Maps::ExportMap(Game::GfxWorld* world)
|
||||
{
|
||||
Utils::Memory::Allocator allocator;
|
||||
if (!world) return;
|
||||
|
||||
Logger::Print("Exporting '%s'...\n", world->baseName);
|
||||
|
||||
std::string mtl;
|
||||
mtl.append("# IW4x MTL File\n");
|
||||
mtl.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
|
||||
std::string map;
|
||||
map.append("# Generated by IW4x\n");
|
||||
map.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
map.append(Utils::String::VA("o %s\n", world->baseName));
|
||||
map.append(Utils::String::VA("mtllib %s.mtl\n\n", world->baseName));
|
||||
|
||||
Logger::Print("Writing vertices...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
float x = world->draw.vd.vertices[i].xyz[1];
|
||||
float y = world->draw.vd.vertices[i].xyz[2];
|
||||
float z = world->draw.vd.vertices[i].xyz[0];
|
||||
|
||||
map.append(Utils::String::VA("v %.6f %.6f %.6f\n", x, y, z));
|
||||
}
|
||||
|
||||
map.append("\n");
|
||||
|
||||
Logger::Print("Writing texture coordinates...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
map.append(Utils::String::VA("vt %.6f %.6f\n", world->draw.vd.vertices[i].texCoord[0], -world->draw.vd.vertices[i].texCoord[1]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing normals...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
Game::vec3_t normal;
|
||||
Game::Vec3UnpackUnitVec(world->draw.vd.vertices[i].normal, &normal);
|
||||
map.append(Utils::String::VA("vn %.6f %.6f %.6f\n", normal[0], normal[1], normal[2]));
|
||||
}
|
||||
|
||||
map.append("\n");
|
||||
|
||||
Logger::Print("Searching materials...\n");
|
||||
int materialCount = 0;
|
||||
Game::Material** materials = allocator.allocateArray<Game::Material*>(world->dpvs.staticSurfaceCount);
|
||||
|
||||
for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
bool isNewMat = true;
|
||||
|
||||
for (int j = 0; j < materialCount; ++j)
|
||||
{
|
||||
if (world->dpvs.surfaces[i].material == materials[j])
|
||||
{
|
||||
isNewMat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewMat)
|
||||
{
|
||||
materials[materialCount++] = world->dpvs.surfaces[i].material;
|
||||
}
|
||||
}
|
||||
|
||||
Utils::IO::CreateDir(Utils::String::VA("raw/mapdump/%s/textures", world->baseName));
|
||||
mtl.append(Utils::String::VA("# Material Count: %d\n", materialCount));
|
||||
|
||||
Logger::Print("Exporting materials and faces...\n");
|
||||
for (int m = 0; m < materialCount; ++m)
|
||||
{
|
||||
std::string name = materials[m]->info.name;
|
||||
|
||||
auto pos = name.find_last_of("/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
map.append(Utils::String::VA("\nusemtl %s\n", name.data()));
|
||||
map.append("s off\n");
|
||||
|
||||
Game::GfxImage* image = materials[m]->textureTable[0].u.image;
|
||||
|
||||
for (char l = 0; l < materials[m]->textureCount; ++l)
|
||||
{
|
||||
if (materials[m]->textureTable[l].nameStart == 'c')
|
||||
{
|
||||
if (materials[m]->textureTable[l].nameEnd == 'p')
|
||||
{
|
||||
image = materials[m]->textureTable[l].u.image; // Hopefully our colorMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", world->baseName, image->name);
|
||||
D3DXSaveTextureToFile(std::wstring(_name.begin(), _name.end()).data(), D3DXIFF_PNG, image->texture.map, NULL);
|
||||
|
||||
mtl.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
|
||||
mtl.append("Ka 1.0000 1.0000 1.0000\n");
|
||||
mtl.append("Kd 1.0000 1.0000 1.0000\n");
|
||||
mtl.append("illum 1\n");
|
||||
mtl.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name));
|
||||
mtl.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name));
|
||||
|
||||
for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
if (world->dpvs.surfaces[i].material != materials[m])
|
||||
continue;
|
||||
|
||||
int vertOffset = world->dpvs.surfaces[i].tris.firstVertex + 1;//+1 cus obj starts at 1
|
||||
int indexOffset = world->dpvs.surfaces[i].tris.baseIndex;
|
||||
for (unsigned short j = 0; j < world->dpvs.surfaces[i].tris.triCount; ++j)
|
||||
{
|
||||
int a = world->draw.indices[indexOffset + j * 3 + 0] + vertOffset;
|
||||
int b = world->draw.indices[indexOffset + j * 3 + 1] + vertOffset;
|
||||
int c = world->draw.indices[indexOffset + j * 3 + 2] + vertOffset;
|
||||
|
||||
map.append(Utils::String::VA("f %d/%d/%d %d/%d/%d %d/%d/%d\n", a, a, a, b, b, b, c, c, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::Print("Writing final files...\n");
|
||||
Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.mtl", world->baseName, world->baseName), mtl);
|
||||
Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.obj", world->baseName, world->baseName), map);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Maps::AddDlc(Maps::DLC dlc)
|
||||
{
|
||||
for (auto& pack : Maps::DlcPacks)
|
||||
@ -821,12 +682,16 @@ namespace Components
|
||||
Game::dvar_t* Maps::GetSpecularDvar()
|
||||
{
|
||||
Game::dvar_t*& r_specular = *reinterpret_cast<Game::dvar_t**>(0x69F0D94);
|
||||
static Game::dvar_t* r_specularCustomMaps = Game::Dvar_RegisterBool("r_specularCustomMaps", false, Game::DVAR_FLAG_SAVED, "Allows shaders to use phong specular lighting on custom maps");
|
||||
|
||||
if (Maps::IsCustomMap())
|
||||
{
|
||||
static Game::dvar_t noSpecular;
|
||||
ZeroMemory(&noSpecular, sizeof noSpecular);
|
||||
return &noSpecular;
|
||||
if (!r_specularCustomMaps->current.enabled)
|
||||
{
|
||||
static Game::dvar_t noSpecular;
|
||||
ZeroMemory(&noSpecular, sizeof noSpecular);
|
||||
return &noSpecular;
|
||||
}
|
||||
}
|
||||
|
||||
return r_specular;
|
||||
@ -864,6 +729,17 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Maps::G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2)
|
||||
{
|
||||
if (Maps::CurrentMainZone == "mp_backlot_sh"s || Maps::CurrentMainZone == "mp_con_spring"s ||
|
||||
Maps::CurrentMainZone == "mp_mogadishu_sh"s || Maps::CurrentMainZone == "mp_nightshift_sh"s)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void(Game::gentity_s*, int, int)>(0x408910)(ent, unk, unk2);
|
||||
}
|
||||
|
||||
Maps::Maps()
|
||||
{
|
||||
Dvar::OnInit([]()
|
||||
@ -874,8 +750,8 @@ namespace Components
|
||||
Maps::AddDlc({ 1, "Stimulus Pack", {"mp_complex", "mp_compact", "mp_storm", "mp_overgrown", "mp_crash"} });
|
||||
Maps::AddDlc({ 2, "Resurgence Pack", {"mp_abandon", "mp_vacant", "mp_trailerpark", "mp_strike", "mp_fuel2"} });
|
||||
Maps::AddDlc({ 3, "Nuketown", {"mp_nuked"} });
|
||||
Maps::AddDlc({ 4, "Classics Pack", {"mp_cross_fire", "mp_cargoship", "mp_bloc"} });
|
||||
Maps::AddDlc({ 5, "Classics Pack", {"mp_killhouse", "mp_bog_sh"} });
|
||||
Maps::AddDlc({ 4, "Classics Pack #1", {"mp_cross_fire", "mp_cargoship", "mp_bloc"} });
|
||||
Maps::AddDlc({ 5, "Classics Pack #2", {"mp_killhouse", "mp_bog_sh"} });
|
||||
Maps::AddDlc({ 6, "Freighter", {"mp_cargoship_sh"} });
|
||||
Maps::AddDlc({ 7, "Resurrection Pack", {"mp_shipment_long", "mp_rust_long", "mp_firingrange"} });
|
||||
Maps::AddDlc({ 8, "Recycled Pack", {"mp_bloc_sh", "mp_crash_tropical", "mp_estate_tropical", "mp_fav_tropical", "mp_storm_spring"} });
|
||||
@ -890,8 +766,7 @@ namespace Components
|
||||
{
|
||||
if (pack.index == dlc)
|
||||
{
|
||||
News::LaunchUpdater(Utils::String::VA("-dlc %i -c", pack.index));
|
||||
//ShellExecuteA(nullptr, "open", pack.url.data(), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
ShellExecute(0, 0, L"https://xlabs.dev/support_iw4x_client.html", 0, 0, SW_SHOW);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -900,6 +775,10 @@ namespace Components
|
||||
});
|
||||
});
|
||||
|
||||
// disable turrets on CoD:OL 448+ maps for now
|
||||
Utils::Hook(0x5EE577, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x44A4D5, Maps::G_SpawnTurretHook, HOOK_CALL).install()->quick();
|
||||
|
||||
//#define SORT_SMODELS
|
||||
#if !defined(DEBUG) || !defined(SORT_SMODELS)
|
||||
// Don't sort static models
|
||||
@ -991,33 +870,6 @@ namespace Components
|
||||
//Maps::AddDependency("co_hunted", "mp_storm");
|
||||
//Maps::AddDependency("mp_shipment", "mp_shipment_long");
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
Command::Add("dumpmap", [](Command::Params*)
|
||||
{
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::GfxWorld* world = nullptr;
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [](Game::XAssetHeader header, void* world)
|
||||
{
|
||||
*reinterpret_cast<Game::GfxWorld**>(world) = header.gfxWorld;
|
||||
}, &world, false);
|
||||
|
||||
if (world)
|
||||
{
|
||||
Maps::ExportMap(world);
|
||||
Logger::Print("Map '%s' exported!\n", world->baseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print("No map loaded, unable to dump anything!\n");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
// Allow hiding specific smodels
|
||||
Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick();
|
||||
|
||||
|
@ -100,10 +100,6 @@ namespace Components
|
||||
static void AddDlc(DLC dlc);
|
||||
static void UpdateDlcStatus();
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
static void ExportMap(Game::GfxWorld* world);
|
||||
#endif
|
||||
|
||||
static void PrepareUsermap(const char* mapname);
|
||||
static void SpawnServerStub();
|
||||
static void LoadMapLoadscreenStub();
|
||||
@ -123,5 +119,6 @@ namespace Components
|
||||
static Game::dvar_t* GetSpecularDvar();
|
||||
static void SetSpecularStub1();
|
||||
static void SetSpecularStub2();
|
||||
static void G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2);
|
||||
};
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ namespace Components
|
||||
int handle = Menus::ReserveSourceHandle();
|
||||
if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot!
|
||||
|
||||
Game::script_t *script = Menus::LoadMenuScript(name, buffer);
|
||||
Game::script_t* script = Menus::LoadMenuScript(name, buffer);
|
||||
|
||||
if (!script)
|
||||
{
|
||||
@ -70,7 +70,7 @@ namespace Components
|
||||
|
||||
script->next = nullptr;
|
||||
|
||||
Game::source_t *source = allocator->allocate<Game::source_t>();
|
||||
Game::source_t* source = allocator->allocate<Game::source_t>();
|
||||
if (!source)
|
||||
{
|
||||
Game::FreeMemory(script);
|
||||
@ -83,7 +83,7 @@ namespace Components
|
||||
source->defines = nullptr;
|
||||
source->indentstack = nullptr;
|
||||
source->skip = 0;
|
||||
source->definehash = static_cast<Game::define_t**>(allocator->allocate(4096));
|
||||
source->definehash = static_cast<Game::define_t * *>(allocator->allocate(4096));
|
||||
|
||||
Game::sourceFiles[handle] = source;
|
||||
|
||||
@ -173,6 +173,69 @@ namespace Components
|
||||
return menu;
|
||||
}
|
||||
|
||||
Game::MenuList* Menus::LoadCustomMenuList(const std::string& menu, Utils::Memory::Allocator* allocator)
|
||||
{
|
||||
std::vector<std::pair<bool, Game::menuDef_t*>> menus;
|
||||
FileSystem::File menuFile(menu);
|
||||
|
||||
if (!menuFile.exists()) return nullptr;
|
||||
|
||||
Game::pc_token_t token;
|
||||
int handle = Menus::LoadMenuSource(menu, menuFile.getBuffer());
|
||||
|
||||
if (Menus::IsValidSourceHandle(handle))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ZeroMemory(&token, sizeof(token));
|
||||
|
||||
if (!Game::PC_ReadTokenHandle(handle, &token) || token.string[0] == '}')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!_stricmp(token.string, "loadmenu"))
|
||||
{
|
||||
Game::PC_ReadTokenHandle(handle, &token);
|
||||
|
||||
Utils::Merge(&menus, Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", token.string)));
|
||||
}
|
||||
|
||||
if (!_stricmp(token.string, "menudef"))
|
||||
{
|
||||
Game::menuDef_t* menudef = Menus::ParseMenu(handle);
|
||||
if (menudef) menus.push_back({ true, menudef }); // Custom menu
|
||||
}
|
||||
}
|
||||
|
||||
Menus::FreeMenuSource(handle);
|
||||
}
|
||||
|
||||
if (menus.empty()) return nullptr;
|
||||
|
||||
// Allocate new menu list
|
||||
Game::MenuList* list = allocator->allocate<Game::MenuList>();
|
||||
if (!list) return nullptr;
|
||||
|
||||
list->menus = allocator->allocateArray<Game::menuDef_t*>(menus.size());
|
||||
if (!list->menus)
|
||||
{
|
||||
allocator->free(list);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
list->name = allocator->duplicateString(menu);
|
||||
list->menuCount = menus.size();
|
||||
|
||||
// Copy new menus
|
||||
for (unsigned int i = 0; i < menus.size(); ++i)
|
||||
{
|
||||
list->menus[i] = menus[i].second;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
std::vector<std::pair<bool, Game::menuDef_t*>> Menus::LoadMenu(const std::string& menu)
|
||||
{
|
||||
std::vector<std::pair<bool, Game::menuDef_t*>> menus;
|
||||
@ -221,15 +284,15 @@ namespace Components
|
||||
|
||||
if (menus.empty())
|
||||
{
|
||||
// // Try loading the original menu, if we can't load our custom one
|
||||
// Game::menuDef_t* originalMenu = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, menudef->window.name).menu;
|
||||
//
|
||||
// if (originalMenu)
|
||||
// {
|
||||
// menus.push_back({ false, originalMenu });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // Try loading the original menu, if we can't load our custom one
|
||||
// Game::menuDef_t* originalMenu = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, menudef->window.name).menu;
|
||||
//
|
||||
// if (originalMenu)
|
||||
// {
|
||||
// menus.push_back({ false, originalMenu });
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
menus.push_back({ false, menudef }); // Native menu
|
||||
// }
|
||||
}
|
||||
@ -313,7 +376,7 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
if(increment) ++i;
|
||||
if (increment) ++i;
|
||||
}
|
||||
|
||||
Utils::Merge(menus, newMenus);
|
||||
@ -337,7 +400,7 @@ namespace Components
|
||||
for (auto menu : Menus::CustomMenus)
|
||||
{
|
||||
bool hasMenu = false;
|
||||
for (auto &loadedMenu : menus)
|
||||
for (auto& loadedMenu : menus)
|
||||
{
|
||||
if (loadedMenu.second->window.name == menu)
|
||||
{
|
||||
@ -383,7 +446,7 @@ namespace Components
|
||||
|
||||
if (!Menus::IsValidSourceHandle(handle)) return;
|
||||
|
||||
Game::source_t *source = Game::sourceFiles[handle];
|
||||
Game::source_t* source = Game::sourceFiles[handle];
|
||||
|
||||
while (source->scriptstack)
|
||||
{
|
||||
@ -517,7 +580,7 @@ namespace Components
|
||||
// EDIT: We might also remove the old instances inside RemoveMenu
|
||||
// EDIT2: Removing old instances without having a menu to replace them with might leave a nullptr
|
||||
// EDIT3: Wouldn't it be better to check if the new menu we're trying to load has already been loaded and not was not deallocated and return that one instead of loading a new one?
|
||||
void Menus::OverrideMenu(Game::menuDef_t *menu)
|
||||
void Menus::OverrideMenu(Game::menuDef_t* menu)
|
||||
{
|
||||
if (!menu || !menu->window.name) return;
|
||||
std::string name = menu->window.name;
|
||||
@ -580,12 +643,12 @@ namespace Components
|
||||
Menus::MenuList.clear();
|
||||
}
|
||||
|
||||
Game::XAssetHeader Menus::MenuLoad(Game::XAssetType /*type*/, const std::string& filename)
|
||||
Game::XAssetHeader Menus::MenuFindHook(Game::XAssetType /*type*/, const std::string& filename)
|
||||
{
|
||||
return { Game::Menus_FindByName(Game::uiContext, filename.data()) };
|
||||
}
|
||||
|
||||
Game::XAssetHeader Menus::MenuFileLoad(Game::XAssetType type, const std::string& filename)
|
||||
Game::XAssetHeader Menus::MenuListFindHook(Game::XAssetType type, const std::string& filename)
|
||||
{
|
||||
Game::XAssetHeader header = { nullptr };
|
||||
|
||||
@ -643,7 +706,7 @@ namespace Components
|
||||
return header;
|
||||
}
|
||||
|
||||
bool Menus::IsMenuVisible(Game::UiContext *dc, Game::menuDef_t *menu)
|
||||
bool Menus::IsMenuVisible(Game::UiContext* dc, Game::menuDef_t* menu)
|
||||
{
|
||||
if (menu && menu->window.name)
|
||||
{
|
||||
@ -664,7 +727,7 @@ namespace Components
|
||||
return Game::Menu_IsVisible(dc, menu);
|
||||
}
|
||||
|
||||
void Menus::RemoveMenuFromContext(Game::UiContext *dc, Game::menuDef_t *menu)
|
||||
void Menus::RemoveMenuFromContext(Game::UiContext* dc, Game::menuDef_t* menu)
|
||||
{
|
||||
// Search menu in context
|
||||
int i = 0;
|
||||
@ -694,6 +757,54 @@ namespace Components
|
||||
Menus::CustomMenus.push_back(menu);
|
||||
}
|
||||
|
||||
void Menus::RegisterCustomMenusHook()
|
||||
{
|
||||
Game::UiContext* uiInfoArray = (Game::UiContext*)0x62E2858;
|
||||
// Game::MenuList list;
|
||||
|
||||
Utils::Hook::Call<void()>(0x401700)(); // call original load functions
|
||||
//Game::XAssetHeader header = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MENULIST, "ui_mp/iw4x.txt");
|
||||
//if (header.data && !(header.menuList->menuCount == 1 && !_stricmp("default_menu", header.menuList->menus[0]->window.name)))
|
||||
//{
|
||||
// // Utils::Hook::Call<void(void*, Game::MenuList*, int)>(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus
|
||||
// std::memcpy(&list, header.data, sizeof(Game::MenuList));
|
||||
|
||||
// for (int i = 0; i < uiInfoArray->menuCount; i++)
|
||||
// {
|
||||
// for (int j = 0; j < list.menuCount; j++)
|
||||
// {
|
||||
// if (!list.menus[j]) continue; // skip already used entries
|
||||
// if (!stricmp(list.menus[j]->window.name, uiInfoArray->Menus[i]->window.name))
|
||||
// {
|
||||
// uiInfoArray->Menus[i] = list.menus[j]; // overwrite UiContext pointer
|
||||
// list.menus[j] = nullptr; // clear entries that already exist so we don't add them later
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// for (int i = 0; i < list.menuCount; i++)
|
||||
// {
|
||||
// if (list.menus[i])
|
||||
// {
|
||||
// uiInfoArray->Menus[uiInfoArray->menuCount++] = list.menus[i];
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
for (int i = 0; i < uiInfoArray->menuCount; i++)
|
||||
{
|
||||
OutputDebugStringA(Utils::String::VA("%s\n", uiInfoArray->Menus[i]->window.name));
|
||||
}
|
||||
|
||||
/*
|
||||
header = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MENULIST, "ui_mp/mod.txt");
|
||||
if (header.data && !(header.menuList->menuCount == 1 && !_stricmp("default_menu", header.menuList->menus[0]->window.name)))
|
||||
{
|
||||
Utils::Hook::Call<void(void*, Game::MenuList*, int)>(0x401700)(uiInfoArray, header.menuList, 1); // add loaded menus
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
Menus::Menus()
|
||||
{
|
||||
if (Dedicated::IsEnabled()) return;
|
||||
@ -702,97 +813,101 @@ namespace Components
|
||||
Menus::FreeEverything();
|
||||
|
||||
// Intercept asset finding
|
||||
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENU, Menus::MenuLoad);
|
||||
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENULIST, Menus::MenuFileLoad);
|
||||
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENU, Menus::MenuFindHook);
|
||||
AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENULIST, Menus::MenuListFindHook);
|
||||
|
||||
// Don't open connect menu
|
||||
//Utils::Hook::Nop(0x428E48, 5);
|
||||
|
||||
// register custom menufiles if they exist
|
||||
Utils::Hook(0x4A58C3, Menus::RegisterCustomMenusHook, HOOK_CALL).install()->quick();
|
||||
|
||||
// Use the connect menu open call to update server motds
|
||||
Utils::Hook(0x428E48, []()
|
||||
{
|
||||
if (!Party::GetMotd().empty() && Party::Target() == *Game::connectedHost)
|
||||
{
|
||||
Dvar::Var("didyouknow").set(Party::GetMotd());
|
||||
}
|
||||
}, HOOK_CALL).install()->quick();
|
||||
if (!Party::GetMotd().empty() && Party::Target() == *Game::connectedHost)
|
||||
{
|
||||
Dvar::Var("didyouknow").set(Party::GetMotd());
|
||||
}
|
||||
}, HOOK_CALL).install()->quick();
|
||||
|
||||
// Intercept menu painting
|
||||
Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick();
|
||||
// Intercept menu painting
|
||||
Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick();
|
||||
|
||||
// disable the 2 new tokens in ItemParse_rect
|
||||
Utils::Hook::Set<BYTE>(0x640693, 0xEB);
|
||||
// disable the 2 new tokens in ItemParse_rect
|
||||
Utils::Hook::Set<BYTE>(0x640693, 0xEB);
|
||||
|
||||
// don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail)
|
||||
Utils::Hook::Nop(0x453406, 5);
|
||||
// don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail)
|
||||
Utils::Hook::Nop(0x453406, 5);
|
||||
|
||||
//make Com_Error and similar go back to main_text instead of menu_xboxlive.
|
||||
Utils::Hook::SetString(0x6FC790, "main_text");
|
||||
//make Com_Error and similar go back to main_text instead of menu_xboxlive.
|
||||
Utils::Hook::SetString(0x6FC790, "main_text");
|
||||
|
||||
Command::Add("openmenu", [](Command::Params* params)
|
||||
{
|
||||
if (params->length() != 2)
|
||||
{
|
||||
Logger::Print("USAGE: openmenu <menu name>\n");
|
||||
return;
|
||||
}
|
||||
Command::Add("openmenu", [](Command::Params* params)
|
||||
{
|
||||
if (params->length() != 2)
|
||||
{
|
||||
Logger::Print("USAGE: openmenu <menu name>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus.
|
||||
if (Dvar::Var("cl_ingame").get<bool>())
|
||||
{
|
||||
Game::Key_SetCatcher(0, 16);
|
||||
}
|
||||
// Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus.
|
||||
if (Dvar::Var("cl_ingame").get<bool>())
|
||||
{
|
||||
Game::Key_SetCatcher(0, 16);
|
||||
}
|
||||
|
||||
Game::Menus_OpenByName(Game::uiContext, params->get(1));
|
||||
});
|
||||
Game::Menus_OpenByName(Game::uiContext, params->get(1));
|
||||
});
|
||||
|
||||
Command::Add("reloadmenus", [](Command::Params*)
|
||||
{
|
||||
// Close all menus
|
||||
Game::Menus_CloseAll(Game::uiContext);
|
||||
Command::Add("reloadmenus", [](Command::Params*)
|
||||
{
|
||||
// Close all menus
|
||||
Game::Menus_CloseAll(Game::uiContext);
|
||||
|
||||
// Free custom menus
|
||||
Menus::FreeEverything();
|
||||
// Free custom menus
|
||||
Menus::FreeEverything();
|
||||
|
||||
// Only disconnect if in-game, context is updated automatically!
|
||||
if (Game::CL_IsCgameInitialized())
|
||||
{
|
||||
Game::Cbuf_AddText(0, "disconnect\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reinitialize ui context
|
||||
Utils::Hook::Call<void()>(0x401700)();
|
||||
// Only disconnect if in-game, context is updated automatically!
|
||||
if (Game::CL_IsCgameInitialized())
|
||||
{
|
||||
Game::Cbuf_AddText(0, "disconnect\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reinitialize ui context
|
||||
Utils::Hook::Call<void()>(0x401700)();
|
||||
|
||||
// Reopen main menu
|
||||
Game::Menus_OpenByName(Game::uiContext, "main_text");
|
||||
}
|
||||
});
|
||||
// Reopen main menu
|
||||
Game::Menus_OpenByName(Game::uiContext, "main_text");
|
||||
}
|
||||
});
|
||||
|
||||
#ifndef DISABLE_ANTICHEAT
|
||||
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner2);
|
||||
Scheduler::OnFrameAsync(AntiCheat::QuickCodeScanner2);
|
||||
#endif
|
||||
|
||||
Command::Add("mp_QuickMessage", [](Command::Params*)
|
||||
{
|
||||
Command::Execute("openmenu quickmessage");
|
||||
});
|
||||
Command::Add("mp_QuickMessage", [](Command::Params*)
|
||||
{
|
||||
Command::Execute("openmenu quickmessage");
|
||||
});
|
||||
|
||||
// Define custom menus here
|
||||
Menus::Add("ui_mp/changelog.menu");
|
||||
Menus::Add("ui_mp/theater_menu.menu");
|
||||
Menus::Add("ui_mp/pc_options_multi.menu");
|
||||
Menus::Add("ui_mp/pc_options_game.menu");
|
||||
Menus::Add("ui_mp/stats_reset.menu");
|
||||
Menus::Add("ui_mp/stats_unlock.menu");
|
||||
Menus::Add("ui_mp/security_increase_popmenu.menu");
|
||||
Menus::Add("ui_mp/mod_download_popmenu.menu");
|
||||
Menus::Add("ui_mp/popup_friends.menu");
|
||||
Menus::Add("ui_mp/menu_first_launch.menu");
|
||||
Menus::Add("ui_mp/startup_messages.menu");
|
||||
Menus::Add("ui_mp/pc_store.menu");
|
||||
Menus::Add("ui_mp/iw4x_credits.menu");
|
||||
Menus::Add("ui_mp/resetclass.menu");
|
||||
// Define custom menus here
|
||||
Menus::Add("ui_mp/changelog.menu");
|
||||
Menus::Add("ui_mp/theater_menu.menu");
|
||||
Menus::Add("ui_mp/pc_options_multi.menu");
|
||||
Menus::Add("ui_mp/pc_options_game.menu");
|
||||
Menus::Add("ui_mp/stats_reset.menu");
|
||||
Menus::Add("ui_mp/stats_unlock.menu");
|
||||
Menus::Add("ui_mp/security_increase_popmenu.menu");
|
||||
Menus::Add("ui_mp/mod_download_popmenu.menu");
|
||||
Menus::Add("ui_mp/popup_friends.menu");
|
||||
Menus::Add("ui_mp/menu_first_launch.menu");
|
||||
Menus::Add("ui_mp/startup_messages.menu");
|
||||
Menus::Add("ui_mp/pc_store.menu");
|
||||
Menus::Add("ui_mp/iw4x_credits.menu");
|
||||
Menus::Add("ui_mp/resetclass.menu");
|
||||
Menus::Add("ui_mp/popup_customtitle.menu");
|
||||
}
|
||||
|
||||
Menus::~Menus()
|
||||
|
@ -15,18 +15,21 @@ namespace Components
|
||||
|
||||
static void Add(const std::string& menu);
|
||||
|
||||
static Game::MenuList* LoadCustomMenuList(const std::string& menu, Utils::Memory::Allocator* allocator);
|
||||
static std::vector<std::pair<bool, Game::menuDef_t*>> LoadMenu(Game::menuDef_t* menudef);
|
||||
static std::vector<std::pair<bool, Game::menuDef_t*>> LoadMenu(const std::string& file);
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string, Game::menuDef_t*> MenuList;
|
||||
static std::unordered_map<std::string, Game::MenuList*> MenuListList;
|
||||
static std::vector<std::string> CustomMenus;
|
||||
|
||||
static Game::XAssetHeader MenuLoad(Game::XAssetType type, const std::string& filename);
|
||||
static Game::XAssetHeader MenuFileLoad(Game::XAssetType type, const std::string& filename);
|
||||
static Game::XAssetHeader MenuFindHook(Game::XAssetType type, const std::string& filename);
|
||||
static Game::XAssetHeader MenuListFindHook(Game::XAssetType type, const std::string& filename);
|
||||
|
||||
static Game::MenuList* LoadMenuList(Game::MenuList* menuList);
|
||||
static Game::MenuList* LoadScriptMenu(const char* menu);
|
||||
static std::vector<std::pair<bool, Game::menuDef_t*>> LoadMenu(Game::menuDef_t* menudef);
|
||||
static std::vector<std::pair<bool, Game::menuDef_t*>> LoadMenu(const std::string& file);
|
||||
|
||||
static void SafeMergeMenus(std::vector<std::pair<bool, Game::menuDef_t*>>* menus, std::vector<std::pair<bool, Game::menuDef_t*>> newMenus);
|
||||
|
||||
static Game::script_t* LoadMenuScript(const std::string& name, const std::string& buffer);
|
||||
@ -47,11 +50,13 @@ namespace Components
|
||||
static void RemoveMenuList(const std::string& menuList);
|
||||
static void RemoveMenuList(Game::MenuList* menuList);
|
||||
|
||||
static void OverrideMenu(Game::menuDef_t *menu);
|
||||
static void OverrideMenu(Game::menuDef_t* menu);
|
||||
|
||||
static bool IsMenuVisible(Game::UiContext *dc, Game::menuDef_t *menu);
|
||||
static bool IsMenuVisible(Game::UiContext* dc, Game::menuDef_t* menu);
|
||||
|
||||
static void RemoveMenuFromContext(Game::UiContext *dc, Game::menuDef_t *menu);
|
||||
static void RemoveMenuFromContext(Game::UiContext* dc, Game::menuDef_t* menu);
|
||||
|
||||
static void RegisterCustomMenusHook();
|
||||
|
||||
// Ugly!
|
||||
static int KeywordHash(char* key);
|
||||
|
@ -336,6 +336,12 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Network::NET_DeferPacketToClientStub(Game::netadr_t* from, Game::msg_t* msg)
|
||||
{
|
||||
if (msg->cursize > 0 && msg->cursize <= 1404)
|
||||
Game::NET_DeferPacketToClient(from, msg);
|
||||
}
|
||||
|
||||
Network::Network()
|
||||
{
|
||||
AssertSize(Game::netadr_t, 20);
|
||||
@ -358,7 +364,7 @@ namespace Components
|
||||
Utils::Hook::Set<BYTE>(0x4050A5, 125);
|
||||
|
||||
// Parse port as short in Net_AddrToString
|
||||
Utils::Hook::Set<char*>(0x4698E3, "%u.%u.%u.%u:%hu");
|
||||
Utils::Hook::Set<const char*>(0x4698E3, "%u.%u.%u.%u:%hu");
|
||||
|
||||
// Install startup handler
|
||||
Utils::Hook(0x4FD4D4, Network::NetworkStartStub, HOOK_JUMP).install()->quick();
|
||||
@ -372,6 +378,9 @@ namespace Components
|
||||
// Install packet deploy hook
|
||||
Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub);
|
||||
|
||||
// Fix packets causing buffer overflow
|
||||
Utils::Hook(0x6267E3, Network::NET_DeferPacketToClientStub, HOOK_CALL).install()->quick();
|
||||
|
||||
Network::Handle("resolveAddress", [](Address address, const std::string& /*data*/)
|
||||
{
|
||||
Network::SendRaw(address, address.getString());
|
||||
|
@ -58,7 +58,7 @@ namespace Components
|
||||
|
||||
static void Handle(const std::string& packet, Utils::Slot<Callback> callback);
|
||||
static void OnStart(Utils::Slot<CallbackRaw> callback);
|
||||
|
||||
|
||||
// Send quake-styled binary data
|
||||
static void Send(Address target, const std::string& data);
|
||||
static void Send(Game::netsrc_t type, Address target, const std::string& data);
|
||||
@ -88,6 +88,7 @@ namespace Components
|
||||
static void NetworkStartStub();
|
||||
|
||||
static void PacketErrorCheck();
|
||||
static void NET_DeferPacketToClientStub(Game::netadr_t* from, Game::msg_t* msg);
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -6,9 +6,6 @@ namespace Components
|
||||
{
|
||||
bool News::Terminate;
|
||||
std::thread News::Thread;
|
||||
std::string News::UpdaterArgs;
|
||||
std::string News::UpdaterHash;
|
||||
std::mutex News::UpdaterMutex;
|
||||
|
||||
bool News::unitTest()
|
||||
{
|
||||
@ -33,165 +30,18 @@ namespace Components
|
||||
return result;
|
||||
}
|
||||
|
||||
void News::ExitProcessStub(unsigned int exitCode)
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
|
||||
STARTUPINFOA sInfo;
|
||||
PROCESS_INFORMATION pInfo;
|
||||
|
||||
ZeroMemory(&sInfo, sizeof(sInfo));
|
||||
ZeroMemory(&pInfo, sizeof(pInfo));
|
||||
sInfo.cb = sizeof(sInfo);
|
||||
|
||||
CreateProcessA("updater.exe", const_cast<char*>(Utils::String::VA("updater.exe %s", News::UpdaterArgs.data())), nullptr, nullptr, false, NULL, nullptr, nullptr, &sInfo, &pInfo);
|
||||
|
||||
if (pInfo.hThread && pInfo.hThread != INVALID_HANDLE_VALUE) CloseHandle(pInfo.hThread);
|
||||
if (pInfo.hProcess && pInfo.hProcess != INVALID_HANDLE_VALUE) CloseHandle(pInfo.hProcess);
|
||||
|
||||
TerminateProcess(GetCurrentProcess(), exitCode);
|
||||
}
|
||||
|
||||
bool News::GetLatestUpdater()
|
||||
{
|
||||
std::lock_guard<std::mutex> _(News::UpdaterMutex);
|
||||
|
||||
if (Utils::IO::FileExists("updater.exe"))
|
||||
{
|
||||
// Generate hash of local updater.exe
|
||||
std::string localUpdater = Utils::IO::ReadFile("updater.exe");
|
||||
localUpdater = Utils::Cryptography::SHA1::Compute(localUpdater, true);
|
||||
|
||||
static Utils::Time::Interval updateInterval;
|
||||
if (News::UpdaterHash.empty() || updateInterval.elapsed(15min)) // Check for updater Update every 15 mins max
|
||||
{
|
||||
updateInterval.update();
|
||||
|
||||
std::string data = Utils::Cache::GetFile("/json/updater"); // {"updater.exe":{"SHA1":"*HASH*"}}
|
||||
|
||||
std::string error;
|
||||
json11::Json listData = json11::Json::parse(data, error);
|
||||
|
||||
if (error.empty() || listData.is_object())
|
||||
{
|
||||
News::UpdaterHash = listData["updater.exe"]["SHA1"].string_value();
|
||||
}
|
||||
}
|
||||
|
||||
if (!News::UpdaterHash.empty() && localUpdater != News::UpdaterHash)
|
||||
{
|
||||
remove("updater.exe");
|
||||
}
|
||||
}
|
||||
|
||||
if (!Utils::IO::FileExists("updater.exe"))
|
||||
{
|
||||
return News::DownloadUpdater();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool News::DownloadUpdater()
|
||||
{
|
||||
std::string data = Utils::Cache::GetFile("/iw4/updater.exe");
|
||||
|
||||
if (!data.empty())
|
||||
{
|
||||
Utils::IO::WriteFile("updater.exe", data);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* News::GetNewsText()
|
||||
{
|
||||
return Localization::Get("MPUI_MOTD_TEXT");
|
||||
}
|
||||
|
||||
void News::CheckForUpdate()
|
||||
{
|
||||
std::string _client = Utils::Cache::GetFile("/json/client");
|
||||
|
||||
if (!_client.empty())
|
||||
{
|
||||
std::string error;
|
||||
json11::Json client = json11::Json::parse(_client.data(), error);
|
||||
|
||||
int revisionNumber;
|
||||
|
||||
if (client["revision"].is_number())
|
||||
{
|
||||
revisionNumber = client["revision"].int_value();
|
||||
}
|
||||
else if (client["revision"].is_string())
|
||||
{
|
||||
revisionNumber = atoi(client["revision"].string_value().data());
|
||||
}
|
||||
else return;
|
||||
|
||||
Dvar::Var("cl_updateversion").get<Game::dvar_t*>()->current.integer = revisionNumber;
|
||||
Dvar::Var("cl_updateavailable").get<Game::dvar_t*>()->current.enabled = (revisionNumber > REVISION);
|
||||
|
||||
// if there is an update then show the toast, but only once
|
||||
static bool showToast = true;
|
||||
if (revisionNumber > REVISION && showToast)
|
||||
{
|
||||
showToast = false;
|
||||
Scheduler::OnReady([]()
|
||||
{
|
||||
Toast::Show("cardicon_gears", "^4Update Available", "There is an update available for your client!", 5000);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void News::LaunchUpdater(const std::string& params)
|
||||
{
|
||||
if (News::Updating()) return;
|
||||
|
||||
News::UpdaterArgs = params;
|
||||
|
||||
Localization::SetTemp("MENU_RECONNECTING_TO_PARTY", "Downloading updater");
|
||||
Command::Execute("openmenu popup_reconnectingtoparty", true);
|
||||
|
||||
// Run the updater on shutdown
|
||||
Utils::Hook::Set(0x6D72A0, News::ExitProcessStub);
|
||||
|
||||
std::thread([]()
|
||||
{
|
||||
if (News::GetLatestUpdater())
|
||||
{
|
||||
Console::SetSkipShutdown();
|
||||
Command::Execute("wait 300; quit;", false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Localization::ClearTemp();
|
||||
News::UpdaterArgs.clear();
|
||||
Command::Execute("closemenu popup_reconnectingtoparty", false);
|
||||
Game::ShowMessageBox("Failed to download the updater!", "Error");
|
||||
}
|
||||
}).detach();
|
||||
}
|
||||
|
||||
bool News::Updating()
|
||||
{
|
||||
return !News::UpdaterArgs.empty();
|
||||
}
|
||||
|
||||
News::News()
|
||||
{
|
||||
News::UpdaterArgs.clear();
|
||||
News::UpdaterHash.clear();
|
||||
if (ZoneBuilder::IsEnabled() || Dedicated::IsEnabled()) return; // Maybe also dedi?
|
||||
|
||||
Dvar::Register<bool>("g_firstLaunch", true, Game::DVAR_FLAG_SAVED, "");
|
||||
|
||||
Dvar::Register<int>("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_FLAG_WRITEPROTECTED, "Current version number.");
|
||||
Dvar::Register<int>("cl_updateversion", 0, 0, -1, Game::DVAR_FLAG_WRITEPROTECTED, "New version number.");
|
||||
Dvar::Register<bool>("cl_updateavailable", false, Game::DVAR_FLAG_WRITEPROTECTED, "New update is available.");
|
||||
|
||||
UIScript::Add("checkFirstLaunch", [](UIScript::Token)
|
||||
{
|
||||
@ -209,14 +59,18 @@ namespace Components
|
||||
|
||||
UIScript::Add("visitWiki", [](UIScript::Token)
|
||||
{
|
||||
Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/"));
|
||||
//Utils::OpenUrl(Utils::Cache::GetStaticUrl("/wiki/"));
|
||||
Utils::OpenUrl("https://github.com/Jawesome99/IW4x/wiki");
|
||||
});
|
||||
|
||||
UIScript::Add("visitDiscord", [](UIScript::Token)
|
||||
{
|
||||
Utils::OpenUrl("https://discord.gg/sKeVmR3");
|
||||
});
|
||||
|
||||
Localization::Set("MPUI_CHANGELOG_TEXT", "Loading...");
|
||||
Localization::Set("MPUI_MOTD_TEXT", NEWS_MOTD_DEFAULT);
|
||||
|
||||
//News::GetLatestUpdater();
|
||||
|
||||
// make newsfeed (ticker) menu items not cut off based on safe area
|
||||
Utils::Hook::Nop(0x63892D, 5);
|
||||
|
||||
@ -224,17 +78,6 @@ namespace Components
|
||||
Utils::Hook::Nop(0x6388BB, 2); // skip the "if (item->text[0] == '@')" localize check
|
||||
Utils::Hook(0x6388C1, News::GetNewsText, HOOK_CALL).install()->quick();
|
||||
|
||||
Command::Add("checkforupdate", [](Command::Params*)
|
||||
{
|
||||
News::CheckForUpdate();
|
||||
});
|
||||
|
||||
Command::Add("getautoupdate", [](Command::Params*)
|
||||
{
|
||||
if (!Dvar::Var("cl_updateavailable").get<Game::dvar_t*>()->current.enabled) return;
|
||||
News::LaunchUpdater("-update -c");
|
||||
});
|
||||
|
||||
if (!Utils::IsWineEnvironment() && !Loader::IsPerformingUnitTests())
|
||||
{
|
||||
News::Terminate = false;
|
||||
@ -251,12 +94,8 @@ namespace Components
|
||||
|
||||
if (!Loader::IsPerformingUnitTests() && !News::Terminate)
|
||||
{
|
||||
News::GetLatestUpdater();
|
||||
|
||||
while (!News::Terminate)
|
||||
{
|
||||
News::CheckForUpdate();
|
||||
|
||||
// Sleep for 3 minutes
|
||||
for (int i = 0; i < 180 && !News::Terminate; ++i)
|
||||
{
|
||||
@ -268,12 +107,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
News::~News()
|
||||
{
|
||||
News::UpdaterArgs.clear();
|
||||
News::UpdaterHash.clear();
|
||||
}
|
||||
|
||||
void News::preDestroy()
|
||||
{
|
||||
News::Terminate = true;
|
||||
|
@ -6,27 +6,16 @@ namespace Components
|
||||
{
|
||||
public:
|
||||
News();
|
||||
~News();
|
||||
|
||||
void preDestroy() override;
|
||||
bool unitTest() override;
|
||||
|
||||
static void LaunchUpdater(const std::string& params);
|
||||
static bool Updating();
|
||||
|
||||
private:
|
||||
static std::string UpdaterArgs;
|
||||
static std::string UpdaterHash;
|
||||
static std::thread Thread;
|
||||
static std::mutex UpdaterMutex;
|
||||
|
||||
static bool Terminate;
|
||||
static bool GetLatestUpdater();
|
||||
static bool DownloadUpdater();
|
||||
|
||||
static void CheckForUpdate();
|
||||
static void ExitProcessStub(unsigned int exitCode);
|
||||
|
||||
static const char* GetNewsText();
|
||||
};
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ namespace Components
|
||||
{
|
||||
if (Dedicated::IsEnabled() && Dvar::Var("sv_lanOnly").get<bool>()) return;
|
||||
|
||||
if (!Dedicated::IsEnabled() && Game::CL_IsCgameInitialized())
|
||||
if (!Dedicated::IsEnabled() && *Game::clcState > 0)
|
||||
{
|
||||
wasIngame = true;
|
||||
return; // don't run while ingame because it can still cause lag spikes on lower end PCs
|
||||
@ -279,25 +279,48 @@ namespace Components
|
||||
|
||||
void Node::SendList(Network::Address address)
|
||||
{
|
||||
Proto::Node::List list;
|
||||
list.set_isnode(Dedicated::IsEnabled());
|
||||
list.set_protocol(PROTOCOL);
|
||||
list.set_port(Node::GetPort());
|
||||
|
||||
std::lock_guard<std::recursive_mutex> _(Node::Mutex);
|
||||
|
||||
for (auto& node : Node::Nodes)
|
||||
{
|
||||
if (node.isValid())
|
||||
{
|
||||
std::string* str = list.add_nodes();
|
||||
// need to keep the message size below 1404 bytes else recipient will just drop it
|
||||
std::vector<std::string> nodeListReponseMessages;
|
||||
|
||||
sockaddr addr = node.address.getSockAddr();
|
||||
str->append(reinterpret_cast<char*>(&addr), sizeof(addr));
|
||||
for (size_t curNode = 0; curNode < Node::Nodes.size();)
|
||||
{
|
||||
Proto::Node::List list;
|
||||
list.set_isnode(Dedicated::IsEnabled());
|
||||
list.set_protocol(PROTOCOL);
|
||||
list.set_port(Node::GetPort());
|
||||
|
||||
for (size_t i = 0; i < NODE_MAX_NODES_TO_SEND;)
|
||||
{
|
||||
if (curNode >= Node::Nodes.size())
|
||||
break;
|
||||
|
||||
auto node = Node::Nodes.at(curNode++);
|
||||
|
||||
if (node.isValid())
|
||||
{
|
||||
std::string* str = list.add_nodes();
|
||||
|
||||
sockaddr addr = node.address.getSockAddr();
|
||||
str->append(reinterpret_cast<char*>(&addr), sizeof(addr));
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
nodeListReponseMessages.push_back(list.SerializeAsString());
|
||||
}
|
||||
|
||||
Session::Send(address, "nodeListResponse", list.SerializeAsString());
|
||||
size_t i = 0;
|
||||
for (auto& nodeListData : nodeListReponseMessages)
|
||||
{
|
||||
Scheduler::OnDelay([nodeListData, i, address]()
|
||||
{
|
||||
NODE_LOG("Sending %d nodeListResponse length to %s\n", nodeListData.length(), address.getCString());
|
||||
Session::Send(address, "nodeListResponse", nodeListData);
|
||||
}, NODE_SEND_RATE * i++);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned short Node::GetPort()
|
||||
|
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#define NODE_HALFLIFE (3 * 60 * 1000) //3min
|
||||
#define NODE_MAX_NODES_TO_SEND 64
|
||||
#define NODE_SEND_RATE 500ms
|
||||
|
||||
#ifdef NODE_LOG_MESSAGES
|
||||
#define NODE_LOG(x, ...) Logger::Print(x, __VA_ARGS__)
|
||||
|
@ -210,7 +210,7 @@ namespace Components
|
||||
// Force xblive_privatematch 0 and rename it
|
||||
//Utils::Hook::Set<BYTE>(0x420A6A, 4);
|
||||
Utils::Hook::Set<BYTE>(0x420A6C, 0);
|
||||
Utils::Hook::Set<char*>(0x420A6E, "xblive_privateserver");
|
||||
Utils::Hook::Set<const char*>(0x420A6E, "xblive_privateserver");
|
||||
|
||||
// Remove migration shutdown, it causes crashes and will be destroyed when erroring anyways
|
||||
Utils::Hook::Nop(0x5A8E1C, 12);
|
||||
@ -244,9 +244,9 @@ namespace Components
|
||||
//Utils::Hook(0x4D5D51, Party::RegisterMinPlayers, HOOK_CALL).install()->quick();
|
||||
|
||||
// Set ui_maxclients to sv_maxclients
|
||||
Utils::Hook::Set<char*>(0x42618F, "sv_maxclients");
|
||||
Utils::Hook::Set<char*>(0x4D3756, "sv_maxclients");
|
||||
Utils::Hook::Set<char*>(0x5E3772, "sv_maxclients");
|
||||
Utils::Hook::Set<const char*>(0x42618F, "sv_maxclients");
|
||||
Utils::Hook::Set<const char*>(0x4D3756, "sv_maxclients");
|
||||
Utils::Hook::Set<const char*>(0x5E3772, "sv_maxclients");
|
||||
|
||||
// Unlatch maxclient dvars
|
||||
Utils::Hook::Xor<BYTE>(0x426187, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
|
@ -150,7 +150,7 @@ namespace Components
|
||||
Playlist::Playlist()
|
||||
{
|
||||
// Default playlists
|
||||
Utils::Hook::Set<char*>(0x60B06E, "playlists_default.info");
|
||||
Utils::Hook::Set<const char*>(0x60B06E, "playlists_default.info");
|
||||
|
||||
// disable playlist download function
|
||||
Utils::Hook::Set<BYTE>(0x4D4790, 0xC3);
|
||||
|
@ -119,6 +119,26 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) int QuickPatch::G_GetClientScore()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
mov eax, [esp + 4] // index
|
||||
mov ecx, ds : 1A831A8h // level: &g_clients
|
||||
|
||||
test ecx, ecx;
|
||||
jz invalid_ptr;
|
||||
|
||||
imul eax, 366Ch
|
||||
mov eax, [eax + ecx + 3134h]
|
||||
ret
|
||||
|
||||
invalid_ptr:
|
||||
xor eax, eax
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
bool QuickPatch::InvalidNameCheck(char *dest, char *source, int size)
|
||||
{
|
||||
strncpy(dest, source, size - 1);
|
||||
@ -128,7 +148,7 @@ namespace Components
|
||||
{
|
||||
if (!dest[i]) break;
|
||||
|
||||
if (dest[i] > 125 || dest[i] < 32)
|
||||
if (dest[i] > 125 || dest[i] < 32 || dest[i] == '%')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -139,7 +159,7 @@ namespace Components
|
||||
|
||||
__declspec(naked) void QuickPatch::InvalidNameStub()
|
||||
{
|
||||
static char* kick_reason = "Invalid name detected.";
|
||||
static const char* kick_reason = "Invalid name detected.";
|
||||
|
||||
__asm
|
||||
{
|
||||
@ -163,6 +183,219 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::g_antilag;
|
||||
|
||||
__declspec(naked) void QuickPatch::ClientEventsFireWeaponStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// check g_antilag dvar value
|
||||
mov eax, g_antilag;
|
||||
cmp byte ptr[eax + 16], 1;
|
||||
|
||||
// do antilag if 1
|
||||
je fireWeapon
|
||||
|
||||
// do not do antilag if 0
|
||||
mov eax, 0x1A83554 // level.time
|
||||
mov ecx, [eax]
|
||||
|
||||
fireWeapon:
|
||||
push edx
|
||||
push ecx
|
||||
push edi
|
||||
mov eax, 0x4A4D50 // FireWeapon
|
||||
call eax
|
||||
add esp, 0Ch
|
||||
pop edi
|
||||
pop ecx
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void QuickPatch::ClientEventsFireWeaponMeleeStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// check g_antilag dvar value
|
||||
mov eax, g_antilag;
|
||||
cmp byte ptr[eax + 16], 1;
|
||||
|
||||
// do antilag if 1
|
||||
je fireWeaponMelee
|
||||
|
||||
// do not do antilag if 0
|
||||
mov eax, 0x1A83554 // level.time
|
||||
mov edx, [eax]
|
||||
|
||||
fireWeaponMelee:
|
||||
push edx
|
||||
push edi
|
||||
mov eax, 0x4F2470 // FireWeaponMelee
|
||||
call eax
|
||||
add esp, 8
|
||||
pop edi
|
||||
pop ecx
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::sv_enableBounces;
|
||||
__declspec(naked) void QuickPatch::BounceStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// check the value of sv_enableBounces
|
||||
push eax;
|
||||
mov eax, sv_enableBounces;
|
||||
cmp byte ptr[eax + 16], 1;
|
||||
pop eax;
|
||||
|
||||
// always bounce if sv_enableBounces is set to 1
|
||||
je bounce;
|
||||
|
||||
// original code
|
||||
cmp dword ptr[esp + 24h], 0;
|
||||
jnz dontBounce;
|
||||
|
||||
bounce:
|
||||
push 0x004B1B34;
|
||||
retn;
|
||||
|
||||
dontBounce:
|
||||
push 0x004B1B48;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::r_customAspectRatio;
|
||||
Game::dvar_t* QuickPatch::Dvar_RegisterAspectRatioDvar(const char* name, char**, int defaultVal, int flags, const char* description)
|
||||
{
|
||||
static std::vector < char * > values =
|
||||
{
|
||||
const_cast<char*>("auto"),
|
||||
const_cast<char*>("standard"),
|
||||
const_cast<char*>("wide 16:10"),
|
||||
const_cast<char*>("wide 16:9"),
|
||||
const_cast<char*>("custom"),
|
||||
nullptr,
|
||||
};
|
||||
|
||||
// register custom aspect ratio dvar
|
||||
r_customAspectRatio = Game::Dvar_RegisterFloat("r_customAspectRatio", 16.0f / 9.0f, 4.0f / 3.0f, 63.0f / 9.0f, flags, "Screen aspect ratio. Divide the width by the height in order to get the aspect ratio value. For example: 16 / 9 = 1,77");
|
||||
|
||||
// register enumeration dvar
|
||||
return Game::Dvar_RegisterEnum(name, values.data(), defaultVal, flags, description);
|
||||
}
|
||||
|
||||
void QuickPatch::SetAspectRatio()
|
||||
{
|
||||
// set the aspect ratio
|
||||
Utils::Hook::Set<float>(0x66E1C78, r_customAspectRatio->current.value);
|
||||
}
|
||||
|
||||
__declspec(naked) void QuickPatch::SetAspectRatioStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
cmp eax, 4;
|
||||
ja goToDefaultCase;
|
||||
je useCustomRatio;
|
||||
|
||||
// execute switch statement code
|
||||
push 0x005063FC;
|
||||
retn;
|
||||
|
||||
goToDefaultCase:
|
||||
push 0x005064FC;
|
||||
retn;
|
||||
|
||||
useCustomRatio:
|
||||
// set custom resolution
|
||||
pushad;
|
||||
call SetAspectRatio;
|
||||
popad;
|
||||
|
||||
// set widescreen to 1
|
||||
mov eax, 1;
|
||||
|
||||
// continue execution
|
||||
push 0x00506495;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::g_playerCollision;
|
||||
__declspec(naked) void QuickPatch::PlayerCollisionStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// check the value of g_playerCollision
|
||||
push eax;
|
||||
mov eax, g_playerCollision;
|
||||
cmp byte ptr[eax + 16], 0;
|
||||
pop eax;
|
||||
|
||||
// dont collide if g_playerCollision is set to 0
|
||||
je dontcollide;
|
||||
|
||||
// original code
|
||||
mov eax, dword ptr[esp + 0xa0];
|
||||
jmp collide;
|
||||
|
||||
collide:
|
||||
push 0x00478376;
|
||||
retn;
|
||||
|
||||
dontcollide:
|
||||
mov eax, dword ptr[esp + 0xa0];
|
||||
mov ecx, dword ptr[esp + 9ch];
|
||||
push eax;
|
||||
push ecx;
|
||||
lea edx, [esp + 48h];
|
||||
push edx;
|
||||
mov eax, esi;
|
||||
push 0x0047838b;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* QuickPatch::g_playerEjection;
|
||||
__declspec(naked) void QuickPatch::PlayerEjectionStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// check the value of g_playerEjection
|
||||
push eax;
|
||||
mov eax, g_playerEjection;
|
||||
cmp byte ptr[eax + 16], 0;
|
||||
pop eax;
|
||||
|
||||
// dont eject if g_playerEjection is set to 0
|
||||
je donteject;
|
||||
|
||||
// original code
|
||||
cmp dword ptr[ebx + 19ch], edi;
|
||||
jle eject;
|
||||
|
||||
eject:
|
||||
push 0x005d8152;
|
||||
retn;
|
||||
|
||||
donteject:
|
||||
push 0x005d815b;
|
||||
retn;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> std::function < T > ImportFunction(const std::string& dll, const std::string& function)
|
||||
{
|
||||
auto dllHandle = GetModuleHandleA(&dll[0]);
|
||||
auto procAddr = GetProcAddress(dllHandle, &function[0]);
|
||||
|
||||
return std::function < T >(reinterpret_cast<T*>(procAddr));
|
||||
}
|
||||
|
||||
QuickPatch::QuickPatch()
|
||||
{
|
||||
QuickPatch::FrameTime = 0;
|
||||
@ -171,12 +404,70 @@ namespace Components
|
||||
QuickPatch::FrameTime = Game::Sys_Milliseconds();
|
||||
});
|
||||
|
||||
// quit_hard
|
||||
Command::Add("quit_hard", [](Command::Params*)
|
||||
{
|
||||
typedef enum _HARDERROR_RESPONSE_OPTION {
|
||||
OptionAbortRetryIgnore,
|
||||
OptionOk,
|
||||
OptionOkCancel,
|
||||
OptionRetryCancel,
|
||||
OptionYesNo,
|
||||
OptionYesNoCancel,
|
||||
OptionShutdownSystem
|
||||
} HARDERROR_RESPONSE_OPTION, *PHARDERROR_RESPONSE_OPTION;
|
||||
|
||||
typedef enum _HARDERROR_RESPONSE {
|
||||
ResponseReturnToCaller,
|
||||
ResponseNotHandled,
|
||||
ResponseAbort,
|
||||
ResponseCancel,
|
||||
ResponseIgnore,
|
||||
ResponseNo,
|
||||
ResponseOk,
|
||||
ResponseRetry,
|
||||
ResponseYes
|
||||
} HARDERROR_RESPONSE, *PHARDERROR_RESPONSE;
|
||||
|
||||
BOOLEAN hasPerms;
|
||||
HARDERROR_RESPONSE response;
|
||||
|
||||
auto result = ImportFunction<NTSTATUS __stdcall(ULONG, BOOLEAN, BOOLEAN, PBOOLEAN)>("ntdll.dll", "RtlAdjustPrivilege")
|
||||
(19, true, false, &hasPerms);
|
||||
|
||||
result = ImportFunction<NTSTATUS __stdcall(NTSTATUS, ULONG, LPCSTR, PVOID, HARDERROR_RESPONSE_OPTION, PHARDERROR_RESPONSE)>("ntdll.dll", "NtRaiseHardError")
|
||||
(0xC000007B /*0x0000000A*/, 0, nullptr, nullptr, OptionShutdownSystem, &response);
|
||||
});
|
||||
|
||||
// bounce dvar
|
||||
sv_enableBounces = Game::Dvar_RegisterBool("sv_enableBounces", false, Game::DVAR_FLAG_REPLICATED, "Enables bouncing on the server");
|
||||
Utils::Hook(0x4B1B2D, QuickPatch::BounceStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Intermission time dvar
|
||||
Game::Dvar_RegisterFloat("scr_intermissionTime", 10, 0, 120, Game::DVAR_FLAG_REPLICATED | Game::DVAR_FLAG_DEDISAVED, "Time in seconds before match server loads the next map");
|
||||
|
||||
// Player Collision dvar
|
||||
g_playerCollision = Game::Dvar_RegisterBool("g_playerCollision", true, Game::DVAR_FLAG_REPLICATED, "Flag whether player collision is on or off");
|
||||
Utils::Hook(0x47836F, QuickPatch::PlayerCollisionStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Player Ejection dvar
|
||||
g_playerEjection = Game::Dvar_RegisterBool("g_playerEjection", true, Game::DVAR_FLAG_REPLICATED, "Flag whether player ejection is on or off");
|
||||
Utils::Hook(0x5D814A, QuickPatch::PlayerEjectionStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
g_antilag = Game::Dvar_RegisterBool("g_antilag", true, Game::DVAR_FLAG_REPLICATED, "Perform antilag");
|
||||
Utils::Hook(0x5D6D56, QuickPatch::ClientEventsFireWeaponStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x5D6D6A, QuickPatch::ClientEventsFireWeaponMeleeStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Disallow invalid player names
|
||||
Utils::Hook(0x401983, QuickPatch::InvalidNameStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Javelin fix
|
||||
Utils::Hook(0x578F52, QuickPatch::JavelinResetHookStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Add ultrawide support
|
||||
Utils::Hook(0x0051B13B, QuickPatch::Dvar_RegisterAspectRatioDvar, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x005063F3, QuickPatch::SetAspectRatioStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Make sure preDestroy is called when the game shuts down
|
||||
Scheduler::OnShutdown(Loader::PreDestroy);
|
||||
|
||||
@ -213,16 +504,16 @@ namespace Components
|
||||
Utils::Hook::Set<DWORD>(0x45ACE0, 0xC301B0);
|
||||
|
||||
// fs_basegame
|
||||
Utils::Hook::Set<char*>(0x6431D1, BASEGAME);
|
||||
Utils::Hook::Set<const char*>(0x6431D1, BASEGAME);
|
||||
|
||||
// UI version string
|
||||
Utils::Hook::Set<char*>(0x43F73B, "IW4x: " VERSION);
|
||||
Utils::Hook::Set<const char*>(0x43F73B, "IW4x: " VERSION);
|
||||
|
||||
// console version string
|
||||
Utils::Hook::Set<char*>(0x4B12BB, "IW4x " VERSION " (built " __DATE__ " " __TIME__ ")");
|
||||
Utils::Hook::Set<const char*>(0x4B12BB, "IW4x " VERSION " (built " __DATE__ " " __TIME__ ")");
|
||||
|
||||
// version string
|
||||
Utils::Hook::Set<char*>(0x60BD56, "IW4x (" VERSION ")");
|
||||
Utils::Hook::Set<const char*>(0x60BD56, "IW4x (" VERSION ")");
|
||||
|
||||
// version string color
|
||||
static float buildLocColor[] = { 1.0f, 1.0f, 1.0f, 0.8f };
|
||||
@ -239,33 +530,33 @@ namespace Components
|
||||
// console title
|
||||
if (ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Utils::Hook::Set<char*>(0x4289E8, "IW4x (" VERSION "): ZoneBuilder");
|
||||
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" VERSION "): ZoneBuilder");
|
||||
}
|
||||
else if (Dedicated::IsEnabled())
|
||||
{
|
||||
Utils::Hook::Set<char*>(0x4289E8, "IW4x (" VERSION "): Dedicated");
|
||||
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" VERSION "): Dedicated");
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils::Hook::Set<char*>(0x4289E8, "IW4x (" VERSION "): Console");
|
||||
Utils::Hook::Set<const char*>(0x4289E8, "IW4x (" VERSION "): Console");
|
||||
}
|
||||
|
||||
// window title
|
||||
Utils::Hook::Set<char*>(0x5076A0, "IW4x: Multiplayer");
|
||||
Utils::Hook::Set<const char*>(0x5076A0, "IW4x: Multiplayer");
|
||||
|
||||
// sv_hostname
|
||||
Utils::Hook::Set<char*>(0x4D378B, "IW4Host");
|
||||
Utils::Hook::Set<const char*>(0x4D378B, "IW4Host");
|
||||
|
||||
// shortversion
|
||||
Utils::Hook::Set<char*>(0x60BD91, SHORTVERSION);
|
||||
Utils::Hook::Set<const char*>(0x60BD91, SHORTVERSION);
|
||||
|
||||
// console logo
|
||||
Utils::Hook::Set<char*>(0x428A66, BASEGAME "/images/logo.bmp");
|
||||
Utils::Hook::Set<const char*>(0x428A66, BASEGAME "/images/logo.bmp");
|
||||
|
||||
// splash logo
|
||||
Utils::Hook::Set<char*>(0x475F9E, BASEGAME "/images/splash.bmp");
|
||||
Utils::Hook::Set<const char*>(0x475F9E, BASEGAME "/images/splash.bmp");
|
||||
|
||||
Utils::Hook::Set<char*>(0x4876C6, "Successfully read stats data\n");
|
||||
Utils::Hook::Set<const char*>(0x4876C6, "Successfully read stats data\n");
|
||||
|
||||
// Numerical ping (cg_scoreboardPingText 1)
|
||||
Utils::Hook::Set<BYTE>(0x45888E, 1);
|
||||
@ -366,21 +657,21 @@ namespace Components
|
||||
// intro stuff
|
||||
Utils::Hook::Nop(0x60BEE9, 5); // Don't show legals
|
||||
Utils::Hook::Nop(0x60BEF6, 5); // Don't reset the intro dvar
|
||||
Utils::Hook::Set<char*>(0x60BED2, "unskippablecinematic IW_logo\n");
|
||||
Utils::Hook::Set<char*>(0x51C2A4, "%s\\" BASEGAME "\\video\\%s.bik");
|
||||
Utils::Hook::Set<const char*>(0x60BED2, "unskippablecinematic IW_logo\n");
|
||||
Utils::Hook::Set<const char*>(0x51C2A4, "%s\\" BASEGAME "\\video\\%s.bik");
|
||||
Utils::Hook::Set<DWORD>(0x51C2C2, 0x78A0AC);
|
||||
|
||||
// Redirect logs
|
||||
Utils::Hook::Set<char*>(0x5E44D8, "logs/games_mp.log");
|
||||
Utils::Hook::Set<char*>(0x60A90C, "logs/console_mp.log");
|
||||
Utils::Hook::Set<char*>(0x60A918, "logs/console_mp.log");
|
||||
Utils::Hook::Set<const char*>(0x5E44D8, "logs/games_mp.log");
|
||||
Utils::Hook::Set<const char*>(0x60A90C, "logs/console_mp.log");
|
||||
Utils::Hook::Set<const char*>(0x60A918, "logs/console_mp.log");
|
||||
|
||||
// Rename config
|
||||
Utils::Hook::Set<char*>(0x461B4B, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<char*>(0x47DCBB, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<char*>(0x6098F8, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<char*>(0x60B279, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<char*>(0x60BBD4, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<const char*>(0x461B4B, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<const char*>(0x47DCBB, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<const char*>(0x6098F8, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<const char*>(0x60B279, CLIENT_CONFIG);
|
||||
Utils::Hook::Set<const char*>(0x60BBD4, CLIENT_CONFIG);
|
||||
|
||||
// Disable profile system
|
||||
// Utils::Hook::Nop(0x60BEB1, 5); // GamerProfile_InitAllProfiles - Causes an error, when calling a harrier killstreak.
|
||||
@ -417,6 +708,13 @@ namespace Components
|
||||
// Patch SV_IsClientUsingOnlineStatsOffline
|
||||
Utils::Hook::Set<DWORD>(0x46B710, 0x90C3C033);
|
||||
|
||||
// Fix mouse lag
|
||||
Utils::Hook::Nop(0x4731F5, 8);
|
||||
Scheduler::OnFrame([]()
|
||||
{
|
||||
SetThreadExecutionState(ES_DISPLAY_REQUIRED);
|
||||
});
|
||||
|
||||
// Fix mouse pitch adjustments
|
||||
Dvar::Register<bool>("ui_mousePitch", false, Game::DVAR_FLAG_SAVED, "");
|
||||
UIScript::Add("updateui_mousePitch", [](UIScript::Token)
|
||||
@ -443,6 +741,12 @@ namespace Components
|
||||
// Patch selectStringTableEntryInDvar
|
||||
Utils::Hook::Set(0x405959, QuickPatch::SelectStringTableEntryInDvarStub);
|
||||
|
||||
// Patch G_GetClientScore for uninitialised game
|
||||
Utils::Hook(0x469AC0, QuickPatch::G_GetClientScore, HOOK_JUMP).install()->quick();
|
||||
|
||||
// Ignore call to print 'Offhand class mismatch when giving weapon...'
|
||||
Utils::Hook(0x5D9047, 0x4BB9B0, HOOK_CALL).install()->quick();
|
||||
|
||||
Command::Add("unlockstats", [](Command::Params*)
|
||||
{
|
||||
QuickPatch::UnlockStats();
|
||||
@ -652,18 +956,21 @@ namespace Components
|
||||
if (!Game::CL_IsCgameInitialized() || !Dvar::Var("r_drawAabbTrees").get<bool>()) return;
|
||||
|
||||
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
|
||||
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
|
||||
//Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0);
|
||||
Game::GfxWorld* gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||
if (!gameWorld) return;
|
||||
Game::clipMap_t* clipMap = *reinterpret_cast<Game::clipMap_t**>(0x7998E0);
|
||||
//Game::GfxWorld* gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||
if (!clipMap) return;
|
||||
|
||||
for (int i = 0; i < gameWorld->dpvsPlanes.cellCount; ++i)
|
||||
{
|
||||
for (int j = 0; j < gameWorld->aabbTreeCounts[i].aabbTreeCount; ++j)
|
||||
{
|
||||
Game::R_AddDebugBounds(cyan, &gameWorld->aabbTrees[i].aabbTree[j].bounds);
|
||||
}
|
||||
}
|
||||
for (unsigned short i = 0; i < clipMap->smodelNodeCount; ++i)
|
||||
{
|
||||
Game::R_AddDebugBounds(cyan, &clipMap->smodelNodes[i].bounds);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < clipMap->numStaticModels; i += 2)
|
||||
{
|
||||
Game::R_AddDebugBounds(red, &clipMap->staticModelList[i].absBounds);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace Components
|
||||
static void SelectStringTableEntryInDvarStub();
|
||||
|
||||
static int SVCanReplaceServerCommand(Game::client_t *client, const char *cmd);
|
||||
static int G_GetClientScore();
|
||||
|
||||
static int MsgReadBitsCompressCheckSV(const char *from, char *to, int size);
|
||||
static int MsgReadBitsCompressCheckCL(const char *from, char *to, int size);
|
||||
@ -27,7 +28,24 @@ namespace Components
|
||||
|
||||
static void JavelinResetHookStub();
|
||||
|
||||
static bool QuickPatch::InvalidNameCheck(char *dest, char *source, int size);
|
||||
static void QuickPatch::InvalidNameStub();
|
||||
static bool InvalidNameCheck(char *dest, char *source, int size);
|
||||
static void InvalidNameStub();
|
||||
|
||||
static Game::dvar_t* sv_enableBounces;
|
||||
static void BounceStub();
|
||||
|
||||
static Game::dvar_t* r_customAspectRatio;
|
||||
static Game::dvar_t* Dvar_RegisterAspectRatioDvar(const char* name, char** enumValues, int defaultVal, int flags, const char* description);
|
||||
static void SetAspectRatioStub();
|
||||
static void SetAspectRatio();
|
||||
|
||||
static Game::dvar_t* g_antilag;
|
||||
static void ClientEventsFireWeaponStub();
|
||||
static void ClientEventsFireWeaponMeleeStub();
|
||||
|
||||
static Game::dvar_t* g_playerCollision;
|
||||
static void PlayerCollisionStub();
|
||||
static Game::dvar_t* g_playerEjection;
|
||||
static void PlayerEjectionStub();
|
||||
};
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ namespace Components
|
||||
public:
|
||||
RawFiles();
|
||||
|
||||
static void* RawFiles::LoadModdableRawfileFunc(const char* filename);
|
||||
static void* LoadModdableRawfileFunc(const char* filename);
|
||||
};
|
||||
}
|
||||
|
@ -7,6 +7,9 @@ namespace Components
|
||||
std::vector<Script::Function> Script::ScriptFunctions;
|
||||
std::vector<std::string> Script::ScriptNameStack;
|
||||
unsigned short Script::FunctionName;
|
||||
std::unordered_map<std::string, std::string> Script::ScriptStorage;
|
||||
std::unordered_map<int, std::string> Script::ScriptBaseProgramNum;
|
||||
int Script::LastFrameTime = -1;
|
||||
|
||||
Utils::Signal<Scheduler::Callback> Script::VMShutdownSignal;
|
||||
|
||||
@ -247,11 +250,6 @@ namespace Components
|
||||
Script::ScriptFunctions.push_back({ name, function, isDev });
|
||||
}
|
||||
|
||||
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
Script::VMShutdownSignal.connect(callback);
|
||||
}
|
||||
|
||||
Game::scr_function_t Script::GetFunction(void* caller, const char** name, int* isDev)
|
||||
{
|
||||
for (auto& function : Script::ScriptFunctions)
|
||||
@ -293,6 +291,91 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Script::StoreScriptBaseProgramNum()
|
||||
{
|
||||
Script::ScriptBaseProgramNum.insert_or_assign(Utils::Hook::Get<int>(0x1CFEEF8), Script::ScriptName);
|
||||
}
|
||||
|
||||
void Script::Scr_PrintPrevCodePos(int scriptPos)
|
||||
{
|
||||
int bestCodePos = -1;
|
||||
int nextCodePos = -1;
|
||||
int offset = -1;
|
||||
std::string file;
|
||||
|
||||
for (auto kv : Script::ScriptBaseProgramNum)
|
||||
{
|
||||
int codePos = kv.first;
|
||||
|
||||
if (codePos > scriptPos)
|
||||
{
|
||||
if (nextCodePos == -1 || codePos < nextCodePos)
|
||||
nextCodePos = codePos;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (codePos < bestCodePos)
|
||||
continue;
|
||||
|
||||
bestCodePos = codePos;
|
||||
|
||||
file = kv.second;
|
||||
offset = scriptPos - bestCodePos;
|
||||
}
|
||||
|
||||
if (bestCodePos == -1)
|
||||
return;
|
||||
|
||||
float onehundred = 100.0;
|
||||
|
||||
Logger::Print(23, "\n@ %d (%d - %d)\n", scriptPos, bestCodePos, nextCodePos);
|
||||
Logger::Print(23, "in %s (%.1f%% through the source)\n\n", file.c_str(), ((offset * onehundred) / (nextCodePos - bestCodePos)));
|
||||
}
|
||||
|
||||
__declspec(naked) void Script::Scr_PrintPrevCodePosStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push esi
|
||||
call Script::Scr_PrintPrevCodePos
|
||||
add esp, 4h
|
||||
|
||||
pop esi
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Script::StoreScriptBaseProgramNumStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
// execute our hook
|
||||
pushad
|
||||
pusha
|
||||
|
||||
call Script::StoreScriptBaseProgramNum
|
||||
|
||||
popa
|
||||
popad
|
||||
|
||||
// execute overwritten code caused by the jump hook
|
||||
sub eax, ds:201A460h // gScrVarPub_programBuffer
|
||||
add esp, 0Ch
|
||||
mov ds : 1CFEEF8h, eax // gScrCompilePub_programLen
|
||||
|
||||
// jump back to the original code
|
||||
push 426C3Bh
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void Script::OnVMShutdown(Utils::Slot<Scheduler::Callback> callback)
|
||||
{
|
||||
Script::ScriptBaseProgramNum.clear();
|
||||
Script::VMShutdownSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Script::ScrShutdownSystemStub(int num)
|
||||
{
|
||||
Script::VMShutdownSignal();
|
||||
@ -316,11 +399,158 @@ namespace Components
|
||||
return Game::Scr_GetNumParam();
|
||||
}
|
||||
|
||||
Game::gentity_t* Script::getEntFromEntRef(Game::scr_entref_t entref)
|
||||
{
|
||||
Game::gentity_t* gentity = &Game::g_entities[entref];
|
||||
return gentity;
|
||||
}
|
||||
|
||||
Game::client_t* Script::getClientFromEnt(Game::gentity_t* gentity)
|
||||
{
|
||||
if (!gentity->client)
|
||||
{
|
||||
Logger::Error(5, "Entity: %i is not a client", gentity);
|
||||
}
|
||||
return &Game::svs_clients[gentity->number];
|
||||
}
|
||||
|
||||
void Script::AddFunctions()
|
||||
{
|
||||
// System time
|
||||
Script::AddFunction("GetSystemTime", [](Game::scr_entref_t) // gsc: GetSystemTime()
|
||||
{
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
|
||||
Game::Scr_AddInt(time.wSecond);
|
||||
});
|
||||
|
||||
Script::AddFunction("GetSystemMilliseconds", [](Game::scr_entref_t) // gsc: GetSystemMilliseconds()
|
||||
{
|
||||
SYSTEMTIME time;
|
||||
GetSystemTime(&time);
|
||||
|
||||
Game::Scr_AddInt(time.wMilliseconds);
|
||||
});
|
||||
|
||||
// Print to console, even without being in 'developer 1'.
|
||||
Script::AddFunction("PrintConsole", [](Game::scr_entref_t) // gsc: PrintConsole(<string>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1PrintConsole: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto str = Game::Scr_GetString(0);
|
||||
|
||||
Game::Com_Printf(0, str);
|
||||
});
|
||||
|
||||
// Executes command to the console
|
||||
Script::AddFunction("Exec", [](Game::scr_entref_t) // gsc: Exec(<string>)
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1Exec: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto str = Game::Scr_GetString(0);
|
||||
|
||||
Command::Execute(str, false);
|
||||
});
|
||||
|
||||
|
||||
// Script Storage Funcs
|
||||
Script::AddFunction("StorageSet", [](Game::scr_entref_t) // gsc: StorageSet(<str key>, <str data>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 2 || Game::Scr_GetType(0) != Game::VAR_STRING || Game::Scr_GetType(1) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1StorageSet: Needs two string parameters!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
std::string data = Game::Scr_GetString(1);
|
||||
|
||||
Script::ScriptStorage.insert_or_assign(key, data);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageRemove", [](Game::scr_entref_t) // gsc: StorageRemove(<str key>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1StorageRemove: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
if (!Script::ScriptStorage.count(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageRemove: Store does not have key '%s'!\n", key.c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
Script::ScriptStorage.erase(key);
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageGet", [](Game::scr_entref_t) // gsc: StorageGet(<str key>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1StorageGet: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
if (!Script::ScriptStorage.count(key))
|
||||
{
|
||||
Game::Scr_Error(Utils::String::VA("^1StorageGet: Store does not have key '%s'!\n", key.c_str()));
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = Script::ScriptStorage.at(key);
|
||||
Game::Scr_AddString(data.c_str());
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageHas", [](Game::scr_entref_t) // gsc: StorageHas(<str key>);
|
||||
{
|
||||
if (Game::Scr_GetNumParam() != 1 || Game::Scr_GetType(0) != Game::VAR_STRING)
|
||||
{
|
||||
Game::Scr_Error("^1StorageHas: Needs one string parameter!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key = Game::Scr_GetString(0);
|
||||
|
||||
Game::Scr_AddInt(Script::ScriptStorage.count(key));
|
||||
});
|
||||
|
||||
Script::AddFunction("StorageClear", [](Game::scr_entref_t) // gsc: StorageClear();
|
||||
{
|
||||
Script::ScriptStorage.clear();
|
||||
});
|
||||
}
|
||||
|
||||
Script::Script()
|
||||
{
|
||||
Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x426C2D, Script::StoreScriptBaseProgramNumStub, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x42281B, Script::Scr_PrintPrevCodePosStub, HOOK_JUMP).install()->quick();
|
||||
|
||||
// enable scr_error printing if in developer
|
||||
Dvar::OnInit([]()
|
||||
{
|
||||
int developer = Dvar::Var("developer").get<int>();
|
||||
|
||||
if (developer > 0)
|
||||
Utils::Hook::Set<BYTE>(0x48D8C7, 0x75);
|
||||
});
|
||||
|
||||
Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick();
|
||||
@ -330,18 +560,38 @@ namespace Components
|
||||
Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x44E736, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetFunction
|
||||
//Utils::Hook(0x4EC8E5, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetMethod
|
||||
Utils::Hook(0x4EC8E5, Script::GetFunctionStub, HOOK_JUMP).install()->quick(); // Scr_GetMethod
|
||||
|
||||
Utils::Hook(0x5F41A3, Script::SetExpFogStub, HOOK_CALL).install()->quick();
|
||||
|
||||
Utils::Hook(0x47548B, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x4D06BA, Script::ScrShutdownSystemStub, HOOK_CALL).install()->quick();
|
||||
|
||||
Scheduler::OnFrame([]()
|
||||
{
|
||||
if (!Game::SV_Loaded())
|
||||
return;
|
||||
|
||||
int nowMs = Game::Sys_Milliseconds();
|
||||
|
||||
if (Script::LastFrameTime != -1)
|
||||
{
|
||||
int timeTaken = static_cast<int>((nowMs - Script::LastFrameTime) * Dvar::Var("timescale").get<float>());
|
||||
|
||||
if (timeTaken >= 500)
|
||||
Logger::Print(23, "Hitch warning: %i msec frame time\n", timeTaken);
|
||||
}
|
||||
|
||||
Script::LastFrameTime = nowMs;
|
||||
});
|
||||
|
||||
Script::AddFunction("debugBox", [](Game::scr_entref_t)
|
||||
{
|
||||
MessageBoxA(nullptr, Game::Scr_GetString(0), "DEBUG", 0);
|
||||
}, true);
|
||||
|
||||
Script::AddFunctions();
|
||||
|
||||
// Script::AddFunction("playviewmodelfx", [](Game::scr_entref_t /*index*/)
|
||||
// {
|
||||
// /*auto Scr_Error = Utils::Hook::Call<void(const char*)>(0x42EF40);
|
||||
@ -376,5 +626,8 @@ namespace Components
|
||||
Script::ScriptNameStack.clear();
|
||||
Script::ScriptFunctions.clear();
|
||||
Script::VMShutdownSignal.clear();
|
||||
|
||||
Script::ScriptStorage.clear();
|
||||
Script::ScriptBaseProgramNum.clear();
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +29,18 @@ namespace Components
|
||||
|
||||
static void OnVMShutdown(Utils::Slot<Scheduler::Callback> callback);
|
||||
|
||||
static Game::gentity_t* getEntFromEntRef(Game::scr_entref_t entref);
|
||||
static Game::client_t* getClientFromEnt(Game::gentity_t* gentity);
|
||||
|
||||
private:
|
||||
static std::string ScriptName;
|
||||
static std::vector<int> ScriptHandles;
|
||||
static std::vector<Function> ScriptFunctions;
|
||||
static std::vector<std::string> ScriptNameStack;
|
||||
static unsigned short FunctionName;
|
||||
static std::unordered_map<std::string, std::string> ScriptStorage;
|
||||
static std::unordered_map<int, std::string> ScriptBaseProgramNum;
|
||||
static int LastFrameTime;
|
||||
|
||||
static Utils::Signal<Scheduler::Callback> VMShutdownSignal;
|
||||
|
||||
@ -57,7 +63,13 @@ namespace Components
|
||||
static void GetFunctionStub();
|
||||
|
||||
static void ScrShutdownSystemStub(int);
|
||||
static void StoreScriptBaseProgramNumStub();
|
||||
static void StoreScriptBaseProgramNum();
|
||||
static void Scr_PrintPrevCodePosStub();
|
||||
static void Scr_PrintPrevCodePos(int);
|
||||
|
||||
static int SetExpFogStub();
|
||||
|
||||
static void AddFunctions();
|
||||
};
|
||||
}
|
||||
|
@ -110,6 +110,19 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
Utils::InfoString ServerInfo::GetHostInfo()
|
||||
{
|
||||
Utils::InfoString info;
|
||||
|
||||
// TODO: Possibly add all Dvar starting with _
|
||||
info.set("admin", Dvar::Var("_Admin").get<const char*>());
|
||||
info.set("website", Dvar::Var("_Website").get<const char*>());
|
||||
info.set("email", Dvar::Var("_Email").get<const char*>());
|
||||
info.set("location", Dvar::Var("_Location").get<const char*>());
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
Utils::InfoString ServerInfo::GetInfo()
|
||||
{
|
||||
int maxclientCount = *Game::svs_numclients;
|
||||
|
@ -8,6 +8,7 @@ namespace Components
|
||||
ServerInfo();
|
||||
~ServerInfo();
|
||||
|
||||
static Utils::InfoString GetHostInfo();
|
||||
static Utils::InfoString GetInfo();
|
||||
|
||||
private:
|
||||
|
@ -63,6 +63,18 @@ namespace Components
|
||||
Stats::SendStats();
|
||||
}
|
||||
|
||||
int Stats::SaveStats(char* dest, const char* folder, const char* buffer, size_t length)
|
||||
{
|
||||
const auto fs_game = Game::Dvar_FindVar("fs_game");
|
||||
|
||||
if (fs_game && fs_game->current.string && strlen(fs_game->current.string) && !strncmp(fs_game->current.string, "mods/", 5))
|
||||
{
|
||||
folder = fs_game->current.string;
|
||||
}
|
||||
|
||||
return Utils::Hook::Call<int(char*, const char*, const char*, size_t)>(0x426450)(dest, folder, buffer, length);
|
||||
}
|
||||
|
||||
Stats::Stats()
|
||||
{
|
||||
// This UIScript should be added in the onClose code of the cac_popup menu,
|
||||
@ -91,6 +103,9 @@ namespace Components
|
||||
|
||||
// Don't create stat backup
|
||||
Utils::Hook::Nop(0x402CE6, 2);
|
||||
|
||||
// Write stats to mod folder if a mod is loaded
|
||||
Utils::Hook(0x682F7B, Stats::SaveStats, HOOK_CALL).install()->quick();
|
||||
}
|
||||
|
||||
Stats::~Stats()
|
||||
|
@ -13,6 +13,7 @@ namespace Components
|
||||
private:
|
||||
static void UpdateClasses(UIScript::Token token);
|
||||
static void SendStats();
|
||||
static int SaveStats(char* dest, const char* folder, const char* buffer, size_t length);
|
||||
|
||||
static int64_t* GetStatsID();
|
||||
};
|
||||
|
@ -371,7 +371,7 @@ namespace Components
|
||||
UIFeeder::Add(10.0f, Theatre::GetDemoCount, Theatre::GetDemoText, Theatre::SelectDemo);
|
||||
|
||||
// set the configstrings stuff to load the default (empty) string table; this should allow demo recording on all gametypes/maps
|
||||
if (!Dedicated::IsEnabled()) Utils::Hook::Set<char*>(0x47440B, "mp/defaultStringTable.csv");
|
||||
if (!Dedicated::IsEnabled()) Utils::Hook::Set<const char*>(0x47440B, "mp/defaultStringTable.csv");
|
||||
|
||||
// Change font size
|
||||
Utils::Hook::Set<BYTE>(0x5AC854, 2);
|
||||
|
@ -22,7 +22,7 @@ namespace Components
|
||||
return this->token;
|
||||
}
|
||||
|
||||
return "";
|
||||
return const_cast<char*>("");
|
||||
}
|
||||
|
||||
template<> const char* UIScript::Token::get()
|
||||
|
@ -207,6 +207,9 @@ namespace Components
|
||||
{
|
||||
Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data());
|
||||
|
||||
if (name.find(" ", 0) != std::string::npos)
|
||||
Logger::Print("Warning: asset with name '%s' contains spaces. Check your zone source file to ensure this is correct!\n", name.data());
|
||||
|
||||
// Sanitize name for empty assets
|
||||
if (name[0] == ',') name.erase(name.begin());
|
||||
|
||||
@ -220,8 +223,7 @@ namespace Components
|
||||
|
||||
Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset);
|
||||
if (!assetHeader.data)
|
||||
{
|
||||
Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type));
|
||||
{ Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type));
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -407,6 +409,8 @@ namespace Components
|
||||
}
|
||||
#endif
|
||||
|
||||
Utils::IO::WriteFile("uncompressed", zoneBuffer);
|
||||
|
||||
zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer);
|
||||
outBuffer.append(zoneBuffer);
|
||||
|
||||
@ -506,7 +510,7 @@ namespace Components
|
||||
// Add branding asset
|
||||
void ZoneBuilder::Zone::addBranding()
|
||||
{
|
||||
char* data = "FastFile built using the IW4x ZoneBuilder!";
|
||||
const char* data = "FastFile built using the IW4x ZoneBuilder!";
|
||||
this->branding = { this->zoneName.data(), static_cast<int>(strlen(data)), 0, data };
|
||||
|
||||
if (this->findAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, this->branding.name) != -1)
|
||||
@ -701,21 +705,20 @@ namespace Components
|
||||
|
||||
if (zoneIndex > 0)
|
||||
{
|
||||
Game::DB_EnumXAssetEntries(type, [&](Game::XAssetEntry* entry)
|
||||
{
|
||||
if (!header.data && entry->zoneIndex == zoneIndex && Game::DB_GetXAssetName(&entry->asset) == name)
|
||||
{
|
||||
// Allocate an empty asset (filled with zeros)
|
||||
header.data = builder->getAllocator()->allocate(Game::DB_GetXAssetSizeHandlers[type]());
|
||||
Game::XAssetEntry* entry = Game::DB_FindXAssetEntry(type, name.data());
|
||||
|
||||
// Set the name to the original name, so it can be stored
|
||||
Game::DB_SetXAssetNameHandlers[type](&header, name.data());
|
||||
AssetHandler::StoreTemporaryAsset(type, header);
|
||||
if (entry && entry->zoneIndex == zoneIndex)
|
||||
{
|
||||
// Allocate an empty asset (filled with zeros)
|
||||
header.data = builder->getAllocator()->allocate(Game::DB_GetXAssetSizeHandlers[type]());
|
||||
|
||||
// Set the name to the empty name
|
||||
Game::DB_SetXAssetNameHandlers[type](&header, builder->getAllocator()->duplicateString("," + name));
|
||||
}
|
||||
}, true, true);
|
||||
// Set the name to the original name, so it can be stored
|
||||
Game::DB_SetXAssetNameHandlers[type](&header, name.data());
|
||||
AssetHandler::StoreTemporaryAsset(type, header);
|
||||
|
||||
// Set the name to the empty name
|
||||
Game::DB_SetXAssetNameHandlers[type](&header, builder->getAllocator()->duplicateString("," + name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -868,6 +871,10 @@ namespace Components
|
||||
|
||||
// defaults need to load before we do this
|
||||
Utils::Hook::Call<void()>(0x4E1F30)(); // G_SetupWeaponDef
|
||||
Utils::Hook::Call<void()>(0x4454C0)(); // Item_SetupKeywordHash (for loading menus)
|
||||
Utils::Hook::Call<void()>(0x501BC0)(); // Menu_SetupKeywordHash (for loading menus)
|
||||
Utils::Hook::Call<void()>(0x4A1280)(); // something related to uiInfoArray
|
||||
|
||||
|
||||
Utils::Hook::Call<void(const char*)>(0x464A90)(GetCommandLineA()); // Com_ParseCommandLine
|
||||
Utils::Hook::Call<void()>(0x60C3D0)(); // Com_AddStartupCommands
|
||||
@ -1102,12 +1109,29 @@ namespace Components
|
||||
if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current())
|
||||
{
|
||||
ZoneBuilder::TraceAssets.push_back({ type, name });
|
||||
OutputDebugStringA((name + "\n").data());
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("verifyzone", [](Command::Params* params)
|
||||
{
|
||||
if (params->length() < 2) return;
|
||||
/*
|
||||
Utils::Hook(0x4AE9C2, [] {
|
||||
Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4;
|
||||
Game::WeaponCompleteDef* var = *varPtr;
|
||||
OutputDebugStringA("");
|
||||
Utils::Hook::Call<void()>(0x4D1D60)(); // DB_PopStreamPos
|
||||
}, HOOK_JUMP).install()->quick();
|
||||
|
||||
|
||||
Utils::Hook(0x4AE9B4, [] {
|
||||
Game::WeaponCompleteDef** varPtr = (Game::WeaponCompleteDef**)0x112A9F4;
|
||||
Game::WeaponCompleteDef* var = *varPtr;
|
||||
OutputDebugStringA("");
|
||||
Utils::Hook::Call<void()>(0x4D1D60)(); // DB_PopStreamPos
|
||||
}, HOOK_JUMP).install()->quick();
|
||||
*/
|
||||
|
||||
std::string zone = params->get(1);
|
||||
|
||||
@ -1282,7 +1306,7 @@ namespace Components
|
||||
|
||||
// HACK: set language to 'techsets' to load from that dir
|
||||
char* language = Utils::Hook::Get<char*>(0x649E740);
|
||||
Utils::Hook::Set<char*>(0x649E740, "techsets");
|
||||
Utils::Hook::Set<const char*>(0x649E740, "techsets");
|
||||
|
||||
// load generated techset fastfiles
|
||||
auto list = Utils::IO::ListFiles("zone/techsets");
|
||||
@ -1342,7 +1366,7 @@ namespace Components
|
||||
info.freeFlags = Game::DB_ZONE_MOD;
|
||||
Game::DB_LoadXAssets(&info, 1, true);
|
||||
|
||||
Utils::Hook::Set<char*>(0x649E740, "techsets");
|
||||
Utils::Hook::Set<const char*>(0x649E740, "techsets");
|
||||
|
||||
i = 0;
|
||||
subCount++;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,13 @@ namespace Components
|
||||
class Zones : public Component
|
||||
{
|
||||
public:
|
||||
struct FileData
|
||||
{
|
||||
std::uint32_t readPos;
|
||||
std::uint32_t len;
|
||||
std::string fileContents;
|
||||
};
|
||||
|
||||
Zones();
|
||||
~Zones();
|
||||
|
||||
@ -17,6 +24,7 @@ namespace Components
|
||||
static int Version() { return Zones::ZoneVersion; };
|
||||
|
||||
private:
|
||||
|
||||
static int ZoneVersion;
|
||||
|
||||
static int FxEffectIndex;
|
||||
@ -61,6 +69,36 @@ namespace Components
|
||||
static void LoadImpactFxArray();
|
||||
static int ImpactFxArrayCount();
|
||||
static void LoadPathDataConstant();
|
||||
static void GetCurrentAssetTypeStub();
|
||||
static int LoadRandomFxGarbage(bool atStreamStart, char* buffer, int size);
|
||||
static int LoadGfxXSurfaceArray(bool atStreamStart, char* buffer, int size);
|
||||
static int LoadGfxXSurfaceExtraData(bool atStreamStart);
|
||||
static int LoadGfxReflectionProbes(bool atStreamStart, char* buffer, int size);
|
||||
static void LoadGfxLightMapExtraData();
|
||||
static void LoadXModelColSurfPtr();
|
||||
static int PathDataSize();
|
||||
static int LoadMaterialTechniqueArray(bool atStreamStart, int count);
|
||||
static int LoadMapEnts(bool atStreamStart, Game::MapEnts* buffer, int size);
|
||||
static void Load_ClipInfo(bool atStreamStart);
|
||||
static int LoadClipMap(bool atStreamStart);
|
||||
static uint32_t HashCRC32StringInt(const std::string& Value, uint32_t Initial);
|
||||
static std::unordered_map<int, Zones::FileData> fileDataMap;
|
||||
static std::mutex fileDataMutex;
|
||||
static int FS_FOpenFileReadForThreadOriginal(const char*, int*, int);
|
||||
static int FS_FOpenFileReadForThreadHook(const char* file, int* filePointer, int thread);
|
||||
static int FS_ReadOriginal(void*, size_t, int);
|
||||
static int FS_ReadHook(void* buffer, size_t size, int filePointer);
|
||||
static void FS_FCloseFileOriginal(int);
|
||||
static void FS_FCloseFileHook(int filePointer);
|
||||
static std::uint32_t FS_SeekOriginal(int, int, int);
|
||||
static std::uint32_t FS_SeekHook(int fileHandle, int seekPosition, int seekOrigin);
|
||||
static void LoadMapTriggersModelPointer();
|
||||
static void LoadMapTriggersHullPointer();
|
||||
static void LoadMapTriggersSlabPointer();
|
||||
static void LoadFxWorldAsset(Game::FxWorld** asset);
|
||||
static void LoadXModelAsset(Game::XModel** asset);
|
||||
static void LoadMaterialAsset(Game::Material** asset);
|
||||
static void LoadTracerDef(bool atStreamStart, Game::TracerDef* tracer, int size);
|
||||
static void LoadTracerDefFxEffect();
|
||||
};
|
||||
}
|
||||
|
@ -2,6 +2,26 @@
|
||||
|
||||
namespace Game
|
||||
{
|
||||
std::vector<std::string> Sys_ListFilesWrapper(const std::string& directory, const std::string& extension)
|
||||
{
|
||||
auto fileCount = 0;
|
||||
auto files = Game::Sys_ListFiles(directory.data(), extension.data(), 0, &fileCount, 0);
|
||||
|
||||
std::vector<std::string> result;
|
||||
|
||||
for (auto i = 0; i < fileCount; i++)
|
||||
{
|
||||
if (files[i])
|
||||
{
|
||||
result.push_back(files[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Game::FS_FreeFileList(files);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AddRefToObject_t AddRefToObject = AddRefToObject_t(0x61C360);
|
||||
AllocObject_t AllocObject = AllocObject_t(0x434320);
|
||||
|
||||
@ -116,6 +136,7 @@ namespace Game
|
||||
FS_BuildPathToFile_t FS_BuildPathToFile = FS_BuildPathToFile_t(0x4702C0);
|
||||
FS_IsShippedIWD_t FS_IsShippedIWD = FS_IsShippedIWD_t(0x642440);
|
||||
|
||||
G_GetWeaponIndexForName_t G_GetWeaponIndexForName = G_GetWeaponIndexForName_t(0x49E540);
|
||||
G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = G_SpawnEntitiesFromString_t(0x4D8840);
|
||||
|
||||
GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = GScr_LoadGameTypeScript_t(0x4ED9A0);
|
||||
@ -148,6 +169,7 @@ namespace Game
|
||||
Load_snd_alias_list_nameArray_t Load_snd_alias_list_nameArray = Load_snd_alias_list_nameArray_t(0x4499F0);
|
||||
|
||||
Menus_CloseAll_t Menus_CloseAll = Menus_CloseAll_t(0x4BA5B0);
|
||||
Menus_CloseRequest_t Menus_CloseRequest = Menus_CloseRequest_t(0x430D50);
|
||||
Menus_OpenByName_t Menus_OpenByName = Menus_OpenByName_t(0x4CCE60);
|
||||
Menus_FindByName_t Menus_FindByName = Menus_FindByName_t(0x487240);
|
||||
Menu_IsVisible_t Menu_IsVisible = Menu_IsVisible_t(0x4D77D0);
|
||||
@ -173,6 +195,7 @@ namespace Game
|
||||
|
||||
NET_AdrToString_t NET_AdrToString = NET_AdrToString_t(0x469880);
|
||||
NET_CompareAdr_t NET_CompareAdr = NET_CompareAdr_t(0x4D0AA0);
|
||||
NET_DeferPacketToClient_t NET_DeferPacketToClient = NET_DeferPacketToClient_t(0x4C8AA0);
|
||||
NET_ErrorString_t NET_ErrorString = NET_ErrorString_t(0x4E7720);
|
||||
NET_Init_t NET_Init = NET_Init_t(0x491860);
|
||||
NET_IsLocalAddress_t NET_IsLocalAddress = NET_IsLocalAddress_t(0x402BD0);
|
||||
@ -232,6 +255,8 @@ namespace Game
|
||||
Scr_AddObject_t Scr_AddObject = Scr_AddObject_t(0x430F40);
|
||||
Scr_Notify_t Scr_Notify = Scr_Notify_t(0x4A4750);
|
||||
Scr_NotifyLevel_t Scr_NotifyLevel = Scr_NotifyLevel_t(0x4D9C30);
|
||||
Scr_Error_t Scr_Error = Scr_Error_t(0x61E8B0);
|
||||
Scr_GetType_t Scr_GetType = Scr_GetType_t(0x422900);
|
||||
|
||||
Scr_ClearOutParams_t Scr_ClearOutParams = Scr_ClearOutParams_t(0x4386E0);
|
||||
|
||||
@ -272,6 +297,7 @@ namespace Game
|
||||
SV_DirectConnect_t SV_DirectConnect = SV_DirectConnect_t(0x460480);
|
||||
SV_SetConfigstring_t SV_SetConfigstring = SV_SetConfigstring_t(0x4982E0);
|
||||
SV_Loaded_t SV_Loaded = SV_Loaded_t(0x4EE3E0);
|
||||
SV_ClientThink_t SV_ClientThink = SV_ClientThink_t(0x44ADD0);
|
||||
|
||||
Sys_Error_t Sys_Error = Sys_Error_t(0x4E0200);
|
||||
Sys_FreeFileList_t Sys_FreeFileList = Sys_FreeFileList_t(0x4D8580);
|
||||
@ -321,6 +347,7 @@ namespace Game
|
||||
source_t **sourceFiles = reinterpret_cast<source_t **>(0x7C4A98);
|
||||
keywordHash_t **menuParseKeywordHash = reinterpret_cast<keywordHash_t **>(0x63AE928);
|
||||
|
||||
int* svs_time = reinterpret_cast<int*>(0x31D9384);
|
||||
int* svs_numclients = reinterpret_cast<int*>(0x31D938C);
|
||||
client_t* svs_clients = reinterpret_cast<client_t*>(0x31D9390);
|
||||
|
||||
@ -390,6 +417,8 @@ namespace Game
|
||||
|
||||
ScriptContainer* scriptContainer = reinterpret_cast<ScriptContainer*>(0x2040D00);
|
||||
|
||||
clientstate_t* clcState = reinterpret_cast<clientstate_t*>(0xB2C540);
|
||||
|
||||
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize)
|
||||
{
|
||||
int elSize = DB_GetXAssetSizeHandlers[type]();
|
||||
@ -662,6 +691,43 @@ namespace Game
|
||||
return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7));
|
||||
}
|
||||
|
||||
void Vec3Normalize(vec3_t& vec)
|
||||
{
|
||||
const float length = static_cast<float>(std::sqrt(std::pow(vec[0], 2) + std::pow(vec[1], 2) + std::pow(vec[2], 2)));
|
||||
vec[0] /= length;
|
||||
vec[1] /= length;
|
||||
vec[2] /= length;
|
||||
}
|
||||
|
||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out)
|
||||
{
|
||||
unsigned int v3; // xmm1_4
|
||||
|
||||
if (LOWORD(in.packed))
|
||||
v3 = ((in.packed & 0x8000) << 16) | (((((in.packed & 0x3FFF) << 14) - (~(LOWORD(in.packed) << 14) & 0x10000000)) ^ 0x80000001) >> 1);
|
||||
else
|
||||
v3 = 0;
|
||||
|
||||
(*out)[0] = *reinterpret_cast<float*>(&v3);
|
||||
|
||||
if (HIWORD(in.packed))
|
||||
v3 = ((HIWORD(in.packed) & 0x8000) << 16) | (((((HIWORD(in.packed) & 0x3FFF) << 14)
|
||||
- (~(HIWORD(in.packed) << 14) & 0x10000000)) ^ 0x80000001) >> 1);
|
||||
else
|
||||
v3 = 0;
|
||||
|
||||
(*out)[1] = *reinterpret_cast<float*>(&v3);
|
||||
}
|
||||
|
||||
void MatrixVecMultiply(const float (& mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution)
|
||||
{
|
||||
vec3_t res;
|
||||
res[0] = mulMat[0][0] * mulVec[0] + mulMat[1][0] * mulVec[1] + mulMat[2][0] * mulVec[2];
|
||||
res[1] = mulMat[0][1] * mulVec[0] + mulMat[1][1] * mulVec[1] + mulMat[2][1] * mulVec[2];
|
||||
res[2] = mulMat[0][2] * mulVec[0] + mulMat[1][2] * mulVec[1] + mulMat[2][2] * mulVec[2];
|
||||
std::memmove(&solution[0], &res[0], sizeof(res));
|
||||
}
|
||||
|
||||
void SortWorldSurfaces(GfxWorld* world)
|
||||
{
|
||||
DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C);
|
||||
|
@ -2,6 +2,26 @@
|
||||
|
||||
namespace Game
|
||||
{
|
||||
template <typename T> static void DB_ConvertOffsetToPointer(T* pointer)
|
||||
{
|
||||
Utils::Hook::Call<void(T*)>(0x4A82B0)(pointer);
|
||||
}
|
||||
template <typename T> static T** DB_InsertPointer()
|
||||
{
|
||||
static auto DB_InsertPointer_Address = 0x43B290;
|
||||
T** retval = nullptr;
|
||||
|
||||
__asm
|
||||
{
|
||||
call DB_InsertPointer_Address;
|
||||
mov retval, eax;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
std::vector<std::string> Sys_ListFilesWrapper(const std::string& directory, const std::string& extension);
|
||||
|
||||
typedef void(__cdecl * AddRefToObject_t)(unsigned int id);
|
||||
extern AddRefToObject_t AddRefToObject;
|
||||
|
||||
@ -297,6 +317,9 @@ namespace Game
|
||||
typedef iwd_t*(__cdecl * FS_IsShippedIWD_t)(const char* fullpath, const char* iwd);
|
||||
extern FS_IsShippedIWD_t FS_IsShippedIWD;
|
||||
|
||||
typedef int(__cdecl* G_GetWeaponIndexForName_t)(char*);
|
||||
extern G_GetWeaponIndexForName_t G_GetWeaponIndexForName;
|
||||
|
||||
typedef void(__cdecl* G_SpawnEntitiesFromString_t)();
|
||||
extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString;
|
||||
|
||||
@ -319,7 +342,7 @@ namespace Game
|
||||
typedef void(__cdecl * LargeLocalInit_t)();
|
||||
extern LargeLocalInit_t LargeLocalInit;
|
||||
|
||||
typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void *ptr, int size);
|
||||
typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void *ptr, unsigned int size);
|
||||
extern Load_Stream_t Load_Stream;
|
||||
|
||||
typedef void(__cdecl * Load_XString_t)(bool atStreamStart);
|
||||
@ -373,6 +396,9 @@ namespace Game
|
||||
typedef void(__cdecl * Menus_CloseAll_t)(UiContext *dc);
|
||||
extern Menus_CloseAll_t Menus_CloseAll;
|
||||
|
||||
typedef void(__cdecl * Menus_CloseRequest_t)(UiContext *dc, menuDef_t* menu);
|
||||
extern Menus_CloseRequest_t Menus_CloseRequest;
|
||||
|
||||
typedef int(__cdecl * Menus_OpenByName_t)(UiContext *dc, const char *p);
|
||||
extern Menus_OpenByName_t Menus_OpenByName;
|
||||
|
||||
@ -439,6 +465,9 @@ namespace Game
|
||||
typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b);
|
||||
extern NET_CompareAdr_t NET_CompareAdr;
|
||||
|
||||
typedef void(__cdecl * NET_DeferPacketToClient_t)(netadr_t *, msg_t *);
|
||||
extern NET_DeferPacketToClient_t NET_DeferPacketToClient;
|
||||
|
||||
typedef const char* (__cdecl * NET_ErrorString_t)();
|
||||
extern NET_ErrorString_t NET_ErrorString;
|
||||
|
||||
@ -592,6 +621,12 @@ namespace Game
|
||||
typedef bool(__cdecl * Scr_IsSystemActive_t)();
|
||||
extern Scr_IsSystemActive_t Scr_IsSystemActive;
|
||||
|
||||
typedef int(__cdecl* Scr_GetType_t)(int);
|
||||
extern Scr_GetType_t Scr_GetType;
|
||||
|
||||
typedef void(__cdecl* Scr_Error_t)(const char*);
|
||||
extern Scr_Error_t Scr_Error;
|
||||
|
||||
typedef script_t* (__cdecl * Script_Alloc_t)(int length);
|
||||
extern Script_Alloc_t Script_Alloc;
|
||||
|
||||
@ -655,6 +690,9 @@ namespace Game
|
||||
typedef bool(__cdecl * SV_Loaded_t)();
|
||||
extern SV_Loaded_t SV_Loaded;
|
||||
|
||||
typedef void(__cdecl* SV_ClientThink_t)(client_s*, usercmd_s*);
|
||||
extern SV_ClientThink_t SV_ClientThink;
|
||||
|
||||
typedef int(__cdecl * Sys_Error_t)(int, char *, ...);
|
||||
extern Sys_Error_t Sys_Error;
|
||||
|
||||
@ -746,6 +784,7 @@ namespace Game
|
||||
|
||||
extern cmd_function_t** cmd_functions;
|
||||
|
||||
extern int* svs_time;
|
||||
extern int* svs_numclients;
|
||||
extern client_t* svs_clients;
|
||||
|
||||
@ -817,6 +856,8 @@ namespace Game
|
||||
|
||||
extern ScriptContainer* scriptContainer;
|
||||
|
||||
extern clientstate_t* clcState;
|
||||
|
||||
XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize);
|
||||
void Menu_FreeItemMemory(Game::itemDef_s* item);
|
||||
const char* TableLookup(StringTable* stringtable, int row, int column);
|
||||
@ -862,6 +903,10 @@ namespace Game
|
||||
|
||||
void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format);
|
||||
|
||||
void Vec3Normalize(vec3_t& vec);
|
||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
|
||||
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
|
||||
|
||||
void SortWorldSurfaces(GfxWorld* world);
|
||||
void R_AddDebugLine(float* color, float* v1, float* v2);
|
||||
void R_AddDebugString(float *color, float *pos, float scale, const char *str);
|
||||
|
@ -121,6 +121,16 @@ namespace Game
|
||||
DVAR_TYPE_COUNT = 0xA,
|
||||
} dvar_type;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
CS_FREE = 0x0,
|
||||
CS_UNKNOWN1 = 0x1,
|
||||
CS_UNKNOWN2 = 0x2,
|
||||
CS_CONNECTED = 0x3,
|
||||
CS_CLIENTLOADING = 0x4,
|
||||
CS_ACTIVE = 0x5,
|
||||
} clientstate_t;
|
||||
|
||||
struct FxEffectDef;
|
||||
struct pathnode_t;
|
||||
struct pathnode_tree_t;
|
||||
@ -4469,9 +4479,17 @@ namespace Game
|
||||
typedef struct client_s
|
||||
{
|
||||
// 0
|
||||
int state;
|
||||
clientstate_t state;
|
||||
// 4
|
||||
char pad[36];
|
||||
char _pad[4];
|
||||
// 8
|
||||
int deltaMessage;
|
||||
// 12
|
||||
char __pad[12];
|
||||
// 24
|
||||
int outgoingSequence;
|
||||
// 28
|
||||
char pad[12];
|
||||
// 40
|
||||
netadr_t addr;
|
||||
// 60
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user