diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..412eeda --- /dev/null +++ b/.gitattributes @@ -0,0 +1,22 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp +*.sln merge=union +*.csproj merge=union +*.vbproj merge=union +*.fsproj merge=union +*.dbproj merge=union + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..00e6a04 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,92 @@ +name: GitHub Actions Build + +on: + push: + branches: + - '**' + pull_request: + workflow_dispatch: + inputs: + release: + description: "Create a release" + type: choice + required: false + default: 'false' + options: + - 'false' + - 'true' + +concurrency: + group: ${{ github.ref }} + cancel-in-progress: false + +permissions: + contents: write + +jobs: + build: + runs-on: windows-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@main + + - name: Configure build + run: ./premake5 vs2022 + + - name: Build + run: | + msbuild -m build/GTALCS.GTAVCS.PCSX2F.CLEO.sln /property:Configuration=Release + + - name: Pack binaries + shell: cmd + run: release.bat + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: cleops2 + path: | + data//* + !**/*.map + + - name: Get release info + if: github.event.inputs.release != '' && github.event.inputs.release != 'false' + id: release_info + uses: cardinalby/git-get-release-action@1.2.5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag: latest + + - name: Upload Release + if: | + github.event.inputs.release == 'true' && + github.ref_name == 'main' && + (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && + github.repository == 'cleolibrary/GTALCS.GTAVCS.PCSX2F.CLEO' + uses: ncipollo/release-action@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + allowUpdates: true + name: ${{ steps.release_info.outputs.name }} + body: ${{ steps.release_info.outputs.body }} + tag: ${{ steps.release_info.outputs.tag_name }} + artifacts: cleops2.zip + + - name: Update Tag + if: | + github.event.inputs.release == 'true' && + github.ref_name == 'main' && + (github.event_name == 'push' || github.event_name == 'workflow_dispatch') && + github.repository == 'cleolibrary/GTALCS.GTAVCS.PCSX2F.CLEO' + uses: richardsimko/update-tag@v1.0.6 + with: + tag_name: ${{ steps.release_info.outputs.tag_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25bace6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,323 @@ +## Releases data +data/Archives +data/**/*.asi + +#ASI Loader +data/**/d3d8.dll +data/**/d3d9.dll +data/**/d3d10.dll +data/**/d3d11.dll +data/**/d3d12.dll +data/**/ddraw.dll +data/**/dinput.dll +data/**/dinput8.dll +data/**/dsound.dll +data/**/msacm32.dll +data/**/msvfw32.dll +data/**/version.dll +data/**/wininet.dll +data/**/winmm.dll +data/**/xlive.dll +data/**/bink2w64.dll +data/**/vorbisFile.dll +data/**/binkw32.dll + +#PSP +data/**/*.map +source/**/*.o +source/OBJS +includes/**/*.o + +#PS2 +data/**/*.elf +data/**/*.map +source/**/*.o +source/**/*.map +source/**/linkfile + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Bb]uild/ + +!data/**/[Bb]in/ +!external/**/[Bb]in/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc +*.exp +*.lib + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +!source/*.PPSSPP.*/**/*.exp \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..145768c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/ps2sdk"] + path = external/ps2sdk + url = https://github.com/ThirteenAG/ps2sdk diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.autoaim.csa b/data/PLUGINS/CLEO/lcs/gtalcs.autoaim.csa new file mode 100644 index 0000000..e03ef21 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.autoaim.csa differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.autoaim.txt b/data/PLUGINS/CLEO/lcs/gtalcs.autoaim.txt new file mode 100644 index 0000000..11e598d --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.autoaim.txt @@ -0,0 +1,69 @@ +{$CLEO .csa} + +thread 'AUTOAIM' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char + 2@ : Integer // CPad::GetPad() + 3@ : Integer // frames to keep the aim +end + +wait 1000 + +0DD5: 0@ = get_platform +if + 0@ <> 2 // PSP +then + end_thread +end + +0DD0: 2@ = get_label_addr @eptn_CPad_GetPad +0DDA: 2@ = get_pattern_addr_cstr 2@ index 0 + +3@ = 0 + +while true + wait 0 + + 0DDD: 0@ = get_mutex_var 0 + if + 0@ <> 0 + then + wait 150 + 3@ = 0 + continue + end + + 008B: 1@ = $536 // player handle on PSP + 01FA: get_player_char 1@ store_to 1@ + + if and + 03EE: is_char_in_control 1@ + 0450: is_char_on_foot 1@ + 00E6: is_button_pressed 0 button 17 + then + gosub @set_aim + 3@ = 20 + continue + end + + if + 3@ > 0 + then + gosub @set_aim + 3@ -= 1 + end +end + +:set_aim +//01EA: print_with_number_now 'NUMBER' number 3@ time 100 flag 1 +0DDE: call_func 2@ add_ib 0 __result 'resi' 0@ __pad_index 'i' 0 +0@ += 14 +0DD9: write_mem_addr 0@ value 1 size 2 add_ib 0 protect 0 +return + +:eptn_CPad_GetPad +hex + "BC 00 03 24 ?? ?? 02 3C 18 20 83 00 ?? ?? ?? ?? 08 00 E0 03 21 10 82 00" 00 +end diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.csi b/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.csi new file mode 100644 index 0000000..8859004 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.csi differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.fxt b/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.fxt new file mode 100644 index 0000000..ccd57a3 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.fxt @@ -0,0 +1,2 @@ +BDGSPWN Bodyguards spawned. +BDGOFF Bodyguards removed. \ No newline at end of file diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.txt b/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.txt new file mode 100644 index 0000000..a1323e2 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.bodyguards.txt @@ -0,0 +1,97 @@ +{$CLEO .csi} + +thread 'BDGUARD' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor + 5@ : Float // player/ped x + 6@ : Float // player/ped y + 7@ : Float // player/ped z + 10@ : Integer // bodyguard + 11@ : Integer // bodyguard + 12@ : Integer // bodyguard + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + 008B: 20@ = $537 // player handle on Android +else + 008B: 20@ = $536 // player handle on PSP +end + +if + 825B: not player 20@ defined +then + end_thread +end + +0DDD: 0@ = get_mutex_var 23487 +if + 0@ == 0 +then + gosub @spawn_bodyguards + 03EA: print_help 'BDGSPWN' + 0DDC: set_mutex_var 23487 to 1 +else + gosub @remove_bodyguards + 03EA: print_help 'BDGOFF' + 0DDC: set_mutex_var 23487 to 0 +end + +end_thread + +:spawn_bodyguards +gosub @spawn_ped +10@ = 0@ +gosub @spawn_ped +11@ = 0@ +gosub @spawn_ped +12@ = 0@ +return + +:remove_bodyguards +0@ = 10@ +gosub @remove_ped +0@ = 11@ +gosub @remove_ped +0@ = 12@ +gosub @remove_ped +return + +:spawn_ped +024C: request_model #M4 +0390: load_all_models_now +while 824D: not has_model_loaded #M4 + wait 0 +end +020D: generate_random_float_in_range 1.0 3.0 store_to 5@ +020D: generate_random_float_in_range 1.0 3.0 store_to 6@ +01FA: get_player_char 20@ store_to 1@ +04C9: get_offset_from_char_in_world_coords 1@ offset 5@ 6@ 0.0 store_to 5@ 6@ 7@ +037B: create_random_char 5@ 6@ 7@ store_to 0@ +01CA: dont_remove_char 0@ +01E4: tie_char 0@ to_player 20@ +031E: set_char 0@ running 1 +04FA: set_char_as_player_friend 0@ player 20@ on 1 +056D: set_char_never_targetted 0@ to 1 +0541: set_char_in_players_group_can_fight 0@ can_fight 1 +044B: set_char_suffers_critical_hits 0@ enable 0 +0296: set_char_heed_threats 0@ flag 1 +02E7: set_char_accuracy 0@ to 100 +0630: set_char_max_health 0@ to 2000 +0228: set_char_health 0@ to 2000 +01B7: give_weapon_to_char 0@ weapon 26 ammo 9999 +024E: release_model #M4 +return + +:remove_ped +if + 0572: does_char_exist 0@ +then + 0354: remove_char_elegantly 0@ +end +return diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.carspawner.csi b/data/PLUGINS/CLEO/lcs/gtalcs.carspawner.csi new file mode 100644 index 0000000..0166d60 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.carspawner.csi differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.carspawner.txt b/data/PLUGINS/CLEO/lcs/gtalcs.carspawner.txt new file mode 100644 index 0000000..eb7e9c6 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.carspawner.txt @@ -0,0 +1,205 @@ +{$CLEO .csi} + +thread 'CARSPWN' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // general purporse + 5@ : Float // player/vehicle x + 6@ : Float // player/vehicle y + 7@ : Float // player/vehicle z + 8@ : Float // player/vehicle heading + 10@ : Integer // selected item index + 11@ : Integer // active item index + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + 008B: 20@ = $537 // player handle on Android +else + 008B: 20@ = $536 // player handle on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 825B: not player 20@ defined +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 78 +0DF5: set_menu_active_item_index 11@ + +01B9: set_player_control 20@ to 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // spawn car + if + 10@ >= 0 + then + 0@ = 10@ + 0@ += 130 + if + 10@ >= 39 + then + 0@ += 1 + end + if + 10@ >= 61 + then + 0@ += 1 + end + if + 10@ >= 65 + then + 0@ += 5 + end + if + 10@ >= 74 + then + 0@ += 2 + end + 024C: request_model 0@ + 0390: load_requested_models + while 824D: not model 0@ available + wait 0 + end + 01FA: get_player_char 20@ store_to 1@ + 04C9: get_offset_from_char_in_world_coords 1@ offset 0.2 4.8 0.0 store_to 5@ 6@ 7@ + 0175: get_player_heading 20@ store_to 8@ + if + 8@ > 180.0 + then + 8@ -= 180.0 + else + 8@ += 180.0 + end + 8@ -= 90.0 + 00A5: create_car 0@ at 5@ 6@ 7@ store_to 2@ + 017A: set_car_heading 2@ to 8@ + 020F: lock_car_doors 2@ mode 0 + 01C8: remove_references_to_car 2@ + 024E: release_model 0@ + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 11@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +01B9: set_player_control 20@ to 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:menu_desc +hex + // flags + 03 00 00 00 + // title + "CARSPAWNER" 00 + // close button + "CLOSE" 00 + // items + "SPIDER" 00 + "LANDSTK" 00 + "IDAHO" 00 + "STINGER" 00 + "LINERUN" 00 + "PEREN" 00 + "SENTINL" 00 + "PATRIOT" 00 + "FIRETRK" 00 + "TRASHM" 00 + "STRETCH" 00 + "MANANA" 00 + "INFERNS" 00 + "BLISTA" 00 + "PONY" 00 + "MULE" 00 + "CHEETAH" 00 + "AMBULAN" 00 + "FBICAR" 00 + "MOONBM" 00 + "ESPERAN" 00 + "TAXI" 00 + "KURUMA" 00 + "BOBCAT" 00 + "WHOOPEE" 00 + "BFINJC" 00 + "HEARSE" 00 + "POLICAR" 00 + "ENFORCR" 00 + "SECURI" 00 + "BANSHEE" 00 + "BUS" 00 + "RHINO" 00 + "BARRCKS" 00 + "DODO" 00 + "COACH" 00 + "CABBIE" 00 + "STALION" 00 + "RUMPO" 00 + "BELLYUP" 00 + "MRWONGS" 00 + "MAFIACR" 00 + "YARDICR" 00 + "YAKUZCR" 00 + "DIABLCR" 00 + "COLOMCR" 00 + "HOODSCR" 00 + "PANLANT" 00 + "FLATBED" 00 + "YANKEE" 00 + "BORGNIN" 00 + "Toyz" 20 "Van" 00 // string name instead of gxt one + "CAMPVAN" 00 + "BALLOT" 00 + "SHELBY" 00 + "PONTIAC" 00 + "ESPRIT" 00 + "BARRACK" 00 + "HOTROD" 00 + "SINDACO" 00 + "FORELLI" 00 + "GHOST" 00 + "SPEEDER" 00 + "REEFER" 00 + "PREDATR" 00 + "ANGEL" 00 + "PIZZABO" 00 + "NOODLBO" 00 + "PCJ600" 00 + "FAGGIO" 00 + "FREEWAY" 00 + "ANGEL2" 00 + "SANCH2" 00 + "SANCHEZ" 00 + "Hunter" 00 // no gxt entry + "Maveric" 00 // no gxt entry + "Polmav" 00 // no gxt entry + "Vcnmav" 00 // no gxt entry + 00 +end + + diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.cheats.csi b/data/PLUGINS/CLEO/lcs/gtalcs.cheats.csi new file mode 100644 index 0000000..b73ce39 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.cheats.csi differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.cheats.txt b/data/PLUGINS/CLEO/lcs/gtalcs.cheats.txt new file mode 100644 index 0000000..a3eeded --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.cheats.txt @@ -0,0 +1,207 @@ +{$CLEO .csi} + +thread 'CHEATS' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // general purporse + 7@ : Integer // cheat input ptr + 8@ : Integer // iterator + 10@ : Integer // selected item index + 12@ : Integer // active item index + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + 008B: 20@ = $537 // player handle on Android +else + 008B: 20@ = $536 // player handle on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 825B: not player 20@ defined +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 37 +0DF5: set_menu_active_item_index 12@ + +01B9: set_player_control 20@ to 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // activate cheat + if + 10@ >= 0 + then + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + 0DD0: 2@ = get_label_addr @efunc_CPad_AddToCheatString + 0DD1: 2@ = get_func_addr_by_cstr_name 2@ + else + 0DD0: 2@ = get_label_addr @eptn_CPad_AddToCheatString + 0DDA: 2@ = get_pattern_addr_cstr 2@ index 0 + end + 0DD0: 0@ = get_label_addr @inst_CPad + 0DD0: 7@ = get_label_addr @cheat_inputs + 10@ *= 8 + 7@ += 10@ + for 8@ = 0 to 7 + 0DD8: 1@ = read_mem_addr 7@ size 1 add_ib 0 + 7@ += 1 + 0DDE: call_func 2@ add_ib 0 __pad_inst 'i' 0@ __char 'i' 1@ + end + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 12@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +01B9: set_player 20@ frozen_state 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:menu_desc +hex + // flags + 00 00 00 00 + // title + "CHEATS" 00 + // close button + "CLOSE" 00 + // items + "Weapon" 20 "1" 00 + "Weapon" 20 "2" 00 + "Weapon" 20 "3" 00 + "Armour" 00 + "Money" 00 + "Health" 00 + "Wanted" 20 "Up" 00 + "Wanted" 20 "Down" 00 + "Sunny" 20 "Weather" 00 + "Rainy" 20 "Weather" 00 + "Cloudy" 20 "Weather" 00 + "Extra" 20 "Sunny" 00 + "Foggy" 20 "Weather" 00 + "Slow" 20 "Time" 00 + "Fast" 20 "Time" 00 + "Glass" 20 "Cars" 00 + "Bike" 20 "Wheels" 00 + "Big" 20 "Heads" 00 + "Fanny" 20 "Magnet" 00 + "PickUp" 20 "Chicks" 00 + "Seaways" 00 + "Trashmaster" 00 + "Black" 20 "Cars" 00 + "Pink" 20 "Cars" 00 + "Mad" 20 "Cars" 00 + "Traffic" 20 "Lights" 00 + "Suicide" 00 + "Chase" 20 "Stat" 00 + "Strong" 20 "Grip" 00 + "Weapon" 20 "For" 20 "All" 00 + "Peds" 20 "Attack" 20 "You" 00 + "Mayhem" 00 + "Change" 20 "Player" 00 + "BlowUp" 20 "Cars" 00 + "Fast" 20 "Weather" 00 + "Random" 20 "Vehicle" 00 + "Topsy" 20 "Turvy" 00 + 00 +end + +:cheat_inputs +hex + "USSDLSSR" + "UCCDLCCR" + "UXXDLXXR" + "12C12X12" + "12T12C12" + "12X12S12" + "12S12T12" + "11T22XSC" + "11C22STX" + "UDSUDC12" + "UDXUDT12" + "UDCUDS12" + "UDTUDX12" + "2TX2SCLR" + "221221DX" + "T21DD22T" + "CRXURX1S" + "DDDCCX12" + "DDDTTC12" + "XSDXSU22" + "CXDCXU11" + "TCDTCU11" + "CC2TT1SS" + "XX2CC1TT" + "SS2XX1CC" + "TT2SS1XX" + "1DL2XCUT" + "1UR2TSDX" + "1UL2TCDX" + "221221RC" + "112112UT" + "112112LS" + "11L11RST" + "11L11RXS" + "11L11RCX" + "11L11RTC" + "DDDXXS21" +end + +:efunc_CPad_AddToCheatString +hex + "_ZN4CPad16AddToCheatStringEc" 00 +end + +:eptn_CPad_AddToCheatString +hex + "?? ?? ?? ?? 00 2E 05 00 08 00 B1 FF" 00 +end + +:inst_CPad +hex + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +end diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.clear_wanted.csi b/data/PLUGINS/CLEO/lcs/gtalcs.clear_wanted.csi new file mode 100644 index 0000000..ef48cac Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.clear_wanted.csi differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.clear_wanted.txt b/data/PLUGINS/CLEO/lcs/gtalcs.clear_wanted.txt new file mode 100644 index 0000000..87c602d --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.clear_wanted.txt @@ -0,0 +1,24 @@ +{$CLEO .csi} + +thread 'CLRWNTD' + +var + 0@ : Integer // general purporse +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + 008B: 0@ = $537 // player handle on Android +else + 008B: 0@ = $536 // player handle on PSP +end + +if + 025B: player 0@ defined +then + 0115: clear_wanted_level 0@ +end + +end_thread diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.invincibility.csa b/data/PLUGINS/CLEO/lcs/gtalcs.invincibility.csa new file mode 100644 index 0000000..d11527e Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.invincibility.csa differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.invincibility.txt b/data/PLUGINS/CLEO/lcs/gtalcs.invincibility.txt new file mode 100644 index 0000000..559cbdb --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.invincibility.txt @@ -0,0 +1,61 @@ +{$CLEO .csa} + +thread 'INVINC' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char + 2@ : Integer // player's car + 3@ : Float // player/vehicle x + 4@ : Float // player/vehicle y + 5@ : Float // player/vehicle z + 20@ : Integer // player handle +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + 008B: 20@ = $537 // player handle on Android + else + 008B: 20@ = $536 // player handle on PSP + end + if + 825B: not player 20@ defined + then + continue + end + 01FA: get_player_char 20@ store_to 1@ + 0228: set_actor 1@ health_to 1000 + 02B0: set_actor 1@ immunities 1 1 1 1 1 + if + 00E4: is_char_in_any_car 1@ + then + 03C5: store_car_char_is_in_no_save 1@ store_to 2@ + 0229: set_car 2@ health_to 2000 + 02B1: set_car 2@ immunities 1 1 1 1 1 + if and + 02C4: is_car_in_water 2@ + 84AD: not player 20@ driving_boat + then + 0054: get_player_coordinates 20@ store_to 3@ 4@ 5@ + 02C6: get_closest_car_node 3@ 4@ 5@ store_to 3@ 4@ 5@ + 5@ += 0.5 + 0055: set_player_coordinates 20@ to 3@ 4@ 5@ + continue + end + end + if + 04B2: is_char_in_water 1@ + then + 0054: get_player_coordinates 20@ store_to 3@ 4@ 5@ + 02C5: get_closest_char_node 3@ 4@ 5@ store_to 3@ 4@ 5@ + 5@ += 0.5 + 0055: set_player_coordinates 20@ to 3@ 4@ 5@ + end +end + diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.nevertired.csa b/data/PLUGINS/CLEO/lcs/gtalcs.nevertired.csa new file mode 100644 index 0000000..b455f28 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.nevertired.csa differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.nevertired.txt b/data/PLUGINS/CLEO/lcs/gtalcs.nevertired.txt new file mode 100644 index 0000000..767cd49 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.nevertired.txt @@ -0,0 +1,29 @@ +{$CLEO .csa} + +thread 'NOTIRE' + +var + 0@ : Integer // general purporse + 1@ : Integer // player handle +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + 008B: 1@ = $537 // player handle on Android + else + 008B: 1@ = $536 // player handle on PSP + end + if + 825B: not player 1@ defined + then + continue + end + 0335: set_player_never_gets_tired 1@ set 1 +end + diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.regen.csa b/data/PLUGINS/CLEO/lcs/gtalcs.regen.csa new file mode 100644 index 0000000..6cecd17 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.regen.csa differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.regen.txt b/data/PLUGINS/CLEO/lcs/gtalcs.regen.txt new file mode 100644 index 0000000..fd5b178 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.regen.txt @@ -0,0 +1,38 @@ +{$CLEO .csa} + +thread 'REGEN' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor + 2@ : Integer // player handle +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + 008B: 2@ = $537 // player handle on Android + else + 008B: 2@ = $536 // player handle on PSP + end + if + 825B: not player 2@ defined + then + continue + end + 01FA: get_player_char 2@ store_to 1@ + 022B: get_char_health 1@ store_to 0@ + if and + 0@ <> 200 + 0@ <> 0 + then + 0@ += 1 + 0228: set_actor 1@ health_to 0@ + end +end + diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.select_mission.csi b/data/PLUGINS/CLEO/lcs/gtalcs.select_mission.csi new file mode 100644 index 0000000..ef52e6b Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.select_mission.csi differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.select_mission.txt b/data/PLUGINS/CLEO/lcs/gtalcs.select_mission.txt new file mode 100644 index 0000000..fc11052 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.select_mission.txt @@ -0,0 +1,170 @@ +{$CLEO .csi} + +thread 'SLMSN' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // player handle + 3@ : Integer // onmission handle + 10@ : Integer // selected item index + 11@ : Integer // active item index +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + 008B: 2@ = $537 // player handle on Android + 008B: 3@ = $561 // onmission on Android +else + 008B: 2@ = $536 // player handle on PSP + 008B: 3@ = $560 // onmission on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 3@ == 1 // on mission + 825B: not player 2@ defined +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 70 +0DF5: set_menu_active_item_index 11@ + +01B9: set_player_control 2@ to 0 + +1@ = 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // teleport to coords + if and + 10@ >= 0 + 10@ < 70 + then + 1@ = 10@ + 1@ += 48 + if + 1@ >= 62 + then + 1@ += 1 + end + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 11@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +01B9: set_player_control 2@ to 1 +0DDC: set_mutex_var 0 to 0 + +// start mission +if + 1@ > 0 +then + wait 100 + 041C: start_mission 1@ +end + +end_thread + +:menu_desc +hex + // flags + 03 00 00 00 + // title + "SELECT" 20 "MISSION" 00 + // close button + "CLOSE" 00 + // items + "VIC2" 00 // Slacker (Vincenzo) + "VIC3" 00 // Dealing Revenge (Vincenzo) + "VIC4" 00 // Snuff (Vincenzo) + "VIC5" 00 // Smash and Grab (Vincenzo) + "VIC6" 00 // Hot Wheels (Vincenzo) + "VIC7" 00 // The Portland Chainsaw Masquerade (Vincenzo) + "SAL1" 00 // The Offer (Salvatore) + "SAL2" 00 // Ho Selecta! (Salvatore) + "SAL3" 00 // Frighteners (Salvatore) + "SAL4" 00 // Rollercoaster Ride (Salvatore) + "SAL5" 00 // Contra-Banned (Salvatore) + "SAL6" 00 // Sindacco Sabotage (Salvatore) + "SAL7" 00 // The Trouble with Triads (Salvatore) + "SAL8" 00 // Driving Mr Leone (Salvatore) + "JDT1" 00 // Bone Voyeur! (JD) + "JDT2" 00 // Don in 60 Seconds (JD) + "JDT3" 00 // A Volatile Situation (JD) + "JDT4" 00 // Blow up 'Dolls' (JD) + "JDT5" 00 // Salvatore's Salvation (JD) + "JDT6" 00 // The Guns of Leone (JD) + "JDT7" 00 // Calm before the Storm (JD) + "JDT8" 00 // The Made Man (JD) + "MAC1" 00 // Snappy Dresser (Ma Cipriani) + "MAC2" 00 // Big Rumble in Little China (Ma Cipriani) + "MAC3" 00 // Grease Sucho (Ma Cipriani) + "MAC4" 00 // Dead Meat (Ma Cipriani) + "MAC5" 00 // No Son of Mine (Ma Cipriani) + "MAR1" 00 // Shop 'til you Strop (Maria) + "MAR2" 00 // Taken for a Ride (Maria) + "MAR3" 00 // Booby Prize (Maria) + "MAR4" 00 // Biker Heat (Maria) + "MAR5" 00 // Overdose of Trouble (Maria) + "SALS2" 00 // Making Toni (Salvatore) + "SALS1" 00 // A Walk In The Park (Salvatore) + "SALS3" 00 // Caught In The Act (Salvatore) + "SALS4" 00 // Search And Rescue (Salvatore) + "SALS5" 00 // Taking The Peace (Salvatore) + "SALS6" 00 // Shoot The Messenger (Salvatore) + "RAYS1" 00 // Sayonara Sindaccos (Leon McAffrey) + "RAYS2" 00 // The Whole 9 Yardies (Leon McAffrey) + "RAYS3" 00 // Crazy '69' (Leon McAffrey) + "RAYS4" 00 // Night Of The Livid Dreads (Leon McAffrey) + "RAYS5" 00 // Munitions Dump (Leon McAffrey) + "DONS1" 00 // The Morgue Party Candidate (Donald Love) + "DONS2" 00 // Steering The Vote (Donald Love) + "DONS3" 00 // Cam-Pain (Donald Love) + "DONS4" 00 // Friggin' The Riggin' (Donald Love) + "DONS5" 00 // Love & Bullets (Donald Love) + "DONS6" 00 // Counterfeit Count (Donald Love) + "DONS7" 00 // Love On The Rocks (Donald Love) + "NEDS1" 00 // L.C. Confidential (Church Confessional) + "NEDS2" 00 // The Passion Of The Heist (Church Confessional) + "NEDS3" 00 // Karmageddon (Church Confessional) + "NEDS4" 00 // False Idols (Church Confessional) + "SALH1" 00 // Rough Justice (Salvatore) + "SALH2" 00 // Dead Reckoning (Salvatore) + "SALH3" 00 // Shogun Showdown (Salvatore) + "SALH4" 00 // The Shoreside Redemption (Salvatore) + "SALH5" 00 // The Sicilian Gambit (Salvatore) + "DONH1" 00 // Panlantic Land Grab (Donald Love) + "DONH2" 00 // Stop the Press (Donald Love) + "DONH3" 00 // Morgue Party Resurrection (Donald Love) + "DONH4" 00 // No Money, Mo' Problems (Donald Love) + "DONH5" 00 // Bringing the House Down (Donald Love) + "DONH6" 00 // Love on the Run (Donald Love) + "TOSH1" 00 // More Deadly than the Male (Toshiko Kasen) + "TOSH2" 00 // Cash Clash (Toshiko Kasen) + "TOSH3" 00 // A Date with Death (Toshiko Kasen) + "TOSH4" 00 // Cash in Kazuki's Chips (Toshiko Kasen) + 00 +end + + \ No newline at end of file diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.strongtyres.csa b/data/PLUGINS/CLEO/lcs/gtalcs.strongtyres.csa new file mode 100644 index 0000000..4cdd015 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.strongtyres.csa differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.strongtyres.txt b/data/PLUGINS/CLEO/lcs/gtalcs.strongtyres.txt new file mode 100644 index 0000000..9e80e61 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.strongtyres.txt @@ -0,0 +1,43 @@ +{$CLEO .csa} + +thread 'STRTYRE' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char + 2@ : Integer // player's car + 20@ : Integer // player handle +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + 008B: 20@ = $537 // player handle on Android + else + 008B: 20@ = $536 // player handle on PSP + end + if + 825B: not player 20@ defined + then + continue + end + 01FA: get_player_char 20@ store_to 1@ + if + 00E4: is_char_in_any_car 1@ + then + 03C5: store_car_char_is_in_no_save 1@ store_to 2@ + 0471: get_driver_of_car 2@ store_to 0@ + if + 0@ <> 1@ + then + continue + end + 0544: set_can_burst_car_tyres 2@ to 0 + end +end + diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.teleport.csi b/data/PLUGINS/CLEO/lcs/gtalcs.teleport.csi new file mode 100644 index 0000000..91850ea Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.teleport.csi differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.teleport.fxt b/data/PLUGINS/CLEO/lcs/gtalcs.teleport.fxt new file mode 100644 index 0000000..a4116db --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.teleport.fxt @@ -0,0 +1 @@ +TPNMRK Place marker on the map first! \ No newline at end of file diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.teleport.txt b/data/PLUGINS/CLEO/lcs/gtalcs.teleport.txt new file mode 100644 index 0000000..73e9f24 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.teleport.txt @@ -0,0 +1,440 @@ +{$CLEO .csi} + +thread 'TLPRT' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // general purporse + 3@ : Float // player/area coords + 4@ : Float // player/area coords + 5@ : Float // player/area coords + 6@ : Float // player/area coords + 7@ : Float // player/area coords + 8@ : Float // player/area coords + 9@ : Float // player/area coords + 10@ : Integer // selected item index + 11@ : Integer // active item index + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + 008B: 20@ = $537 // player handle on Android + 1@ = 30 // menu item count on Android +else + 008B: 20@ = $536 // player handle on PSP + 1@ = 29 // menu item count on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 825B: not player 20@ defined +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 1@ +0DF5: set_menu_active_item_index 11@ + +01B9: set_player_control 20@ to 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // teleport to coords + if and + 10@ >= 0 + 10@ < 29 + then + 10@ += 1 + gosub @get_coords + 4@ += 7@ + 4@ /= 2.0 + 5@ += 8@ + 5@ /= 2.0 + 6@ += 9@ + 6@ /= 2.0 + 02C6: get_closest_car_node 4@ 5@ 6@ store_to 4@ 5@ 6@ + 6@ += 1.0 + 0055: set_player_coordinates 20@ to 4@ 5@ 6@ + wait 100 + 0055: set_player_coordinates 20@ to 4@ 5@ 6@ + break + end + // teleport to marker (Android only) + if + 10@ == 29 + then + 0DD0: 0@ = get_label_addr @edata_GRadarMap + 0DD1: 0@ = get_func_addr_by_cstr_name 0@ + 0DD8: 0@ = read_mem_addr 0@ size 4 add_ib 0 + if + 0@ <> 0 + then + 0@ += 80 + // check if the marker is set + 0DD8: 1@ = read_mem_addr 0@ size 1 add_ib 0 + if + 1@ == 1 + then + // remove the marker + 0DD9: write_mem_addr 0@ value 0 size 1 add_ib 0 protect 0 + // read coords + 0@ += 8 + 0DD8: 3@ = read_mem_addr 0@ size 4 add_ib 0 + 0@ += 4 + 0DD8: 4@ = read_mem_addr 0@ size 4 add_ib 0 + // get z coord + 5@ = 1000.0 + 02D3: get_ground_z_for_3d_coord 3@ 4@ 5@ store_to 5@ + 5@ += 0.5 + 0055: set_player_coordinates 20@ to 3@ 4@ 5@ + // teleport may cause player drop because cols are not yet loaded + wait 100 + 5@ = 1000.0 + 02D3: get_ground_z_for_3d_coord 3@ 4@ 5@ store_to 5@ + 5@ += 0.5 + 0055: set_player_coordinates 20@ to 3@ 4@ 5@ + else + 03EA: text_box 'TPNMRK' + end + end + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 11@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +01B9: set_player_control 20@ to 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:get_coords +if 10@ == 1 +then + 4@ = 751.68 + 5@ = -1178.22 + 6@ = -13.8723 + 7@ = 1065.68 + 8@ = -958.725 + 9@ = 136.128 +end +if 10@ == 2 +then + 4@ = -1394.5 + 5@ = 93.4441 + 6@ = -46.7412 + 7@ = -867.52 + 8@ = 704.544 + 9@ = 253.344 +end +if 10@ == 3 +then + 4@ = 1065.88 + 5@ = -1251.55 + 6@ = -13.5049 + 7@ = 1501.88 + 8@ = -1069.93 + 9@ = 136.495 +end +if 10@ == 4 +then + 4@ = 1363.68 + 5@ = -1069.65 + 6@ = -18.8643 + 7@ = 1815.68 + 8@ = -613.646 + 9@ = 131.136 +end +if 10@ == 5 +then + 4@ = 1065.88 + 5@ = -1069.85 + 6@ = 1.49868 + 7@ = 1363.38 + 8@ = -742.054 + 9@ = 151.499 +end +if 10@ == 6 +then + 4@ = 745.421 + 5@ = -908.289 + 6@ = -21.203 + 7@ = 1065.42 + 8@ = -463.69 + 9@ = 129.593 +end +if 10@ == 7 +then + 4@ = 745.378 + 5@ = -463.616 + 6@ = -22.6676 + 7@ = 1065.38 + 8@ = -282.616 + 9@ = 147.332 +end +if 10@ == 8 +then + 4@ = 745.421 + 5@ = -282.4 + 6@ = -13.4117 + 7@ = 1065.42 + 8@ = -78.7699 + 9@ = 136.588 +end +if 10@ == 9 +then + 4@ = 1065.9 + 5@ = -512.324 + 6@ = -14.296 + 7@ = 1388.9 + 8@ = -78.324 + 9@ = 135.704 +end +if 10@ == 10 +then + 4@ = 745.979 + 5@ = -78.1778 + 6@ = -48.5832 + 7@ = 1388.98 + 8@ = 322.676 + 9@ = 101.417 +end +if 10@ == 11 +then + 4@ = 1389.37 + 5@ = -613.467 + 6@ = -29.883 + 7@ = 1797.6 + 8@ = 199.628 + 9@ = 120.117 +end +if 10@ == 12 +then + 4@ = 1066.1 + 5@ = -741.806 + 6@ = -34.2068 + 7@ = 1363.6 + 8@ = -512.806 + 9@ = 115.793 +end +if 10@ == 13 +then + 4@ = 1135.8 + 5@ = -695.021 + 6@ = 6.9661 + 7@ = 1182.36 + 8@ = -631.021 + 9@ = 56.9661 +end +if 10@ == 14 +then + 4@ = 1136.09 + 5@ = -609.976 + 6@ = 6.287 + 7@ = 1182.09 + 8@ = -521.167 + 9@ = 56.287 +end +if 10@ == 15 +then + 4@ = 617.151 + 5@ = -1329.72 + 6@ = -117.535 + 7@ = 1902.66 + 8@ = 434.115 + 9@ = 482.465 +end +if 10@ == 16 +then + 4@ = 444.768 + 5@ = -958.298 + 6@ = 30.7441 + 7@ = 614.878 + 8@ = -908.298 + 9@ = 180.744 +end +if 10@ == 17 +then + 4@ = 1363.77 + 5@ = -613.339 + 6@ = -4.43849 + 7@ = 1389.17 + 8@ = -512.539 + 9@ = 70.4322 +end +if 10@ == 18 +then + 4@ = 239.878 + 5@ = -411.617 + 6@ = 7.629 + 7@ = 614.322 + 8@ = -61.6167 + 9@ = 163.819 +end +if 10@ == 19 +then + 4@ = -225.764 + 5@ = -412.604 + 6@ = -9.536 + 7@ = 116.236 + 8@ = 160.496 + 9@ = 120.271 +end +if 10@ == 20 +then + 4@ = 199.766 + 5@ = -1672.42 + 6@ = -61.7588 + 7@ = 577.766 + 8@ = -1059.93 + 9@ = 432.688 +end +if 10@ == 21 +then + 4@ = -224.438 + 5@ = -1672.05 + 6@ = -61.3183 + 7@ = 199.562 + 8@ = -1004.45 + 9@ = 432.352 +end +if 10@ == 22 +then + 4@ = 200.107 + 5@ = -1059.19 + 6@ = -0.000 + 7@ = 615.107 + 8@ = -412.193 + 9@ = 198.864 +end +if 10@ == 23 +then + 4@ = -121.567 + 5@ = -1003.07 + 6@ = -46.7463 + 7@ = 199.271 + 8@ = -413.068 + 9@ = 224.163 +end +if 10@ == 24 +then + 4@ = 117.268 + 5@ = -411.622 + 6@ = 0.0001 + 7@ = 239.268 + 8@ = -61.6218 + 9@ = 166.36 +end +if 10@ == 25 +then + 4@ = 117.236 + 5@ = -61.1105 + 6@ = -17.071 + 7@ = 615.236 + 8@ = 268.889 + 9@ = 83.754 +end +if 10@ == 26 +then + 4@ = -1632.97 + 5@ = -1344.71 + 6@ = -45.9404 + 7@ = -468.629 + 8@ = -268.443 + 9@ = 254.696 +end +if 10@ == 27 +then + 4@ = -811.835 + 5@ = -268.074 + 6@ = -45.8745 + 7@ = -371.041 + 8@ = 92.7263 + 9@ = 254.241 +end +if 10@ == 28 +then + 4@ = -867.229 + 5@ = 93.3882 + 6@ = -50.1134 + 7@ = -266.914 + 8@ = 650.058 + 9@ = 250.426 +end +if 10@ == 29 +then + 4@ = -1407.57 + 5@ = -267.966 + 6@ = -49.6792 + 7@ = -812.306 + 8@ = 92.7559 + 9@ = 250.437 +end +return + +:menu_desc +hex + // flags + 03 00 00 00 + // title + "TELEPORT" 00 + // close button + "CLOSE" 00 + // items + "PORT_W" 00 + "BIG_DAM" 00 + "PORT_S" 00 + "PORT_E" 00 + "PORT_I" 00 + "CHINA" 00 + "REDLIGH" 00 + "TOWERS" 00 + "LITTLEI" 00 + "HARWOOD" 00 + "EASTBAY" 00 + "S_VIEW" 00 + "Portland" 20 "police" 00 + "Portland" 20 "hospital" 00 + "IND_ZON" 00 + "ROADBR2" 00 + "Marko's" 20 "bistro" 00 + "CONSTRU" 00 + "STADIUM" 00 + "YAKUSA" 00 + "SHOPING" 00 + "COM_EAS" 00 + "PARK" 00 + "UNIVERS" 00 + "HOSPI_2" 00 + "AIRPORT" 00 + "PROJECT" 00 + "SWANKS" 00 + "SUB_IND" 00 + "Map" 20 "Marker" 00 + 00 +end + +:edata_GRadarMap +hex + "GRadarMap" 00 +end \ No newline at end of file diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.unlimammo.csa b/data/PLUGINS/CLEO/lcs/gtalcs.unlimammo.csa new file mode 100644 index 0000000..8ff5a62 Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.unlimammo.csa differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.unlimammo.txt b/data/PLUGINS/CLEO/lcs/gtalcs.unlimammo.txt new file mode 100644 index 0000000..6dd05ec --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.unlimammo.txt @@ -0,0 +1,41 @@ +{$CLEO .csa} + +thread 'UNLAMMO' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // player handle +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + 008B: 2@ = $537 // player handle on Android + else + 008B: 2@ = $536 // player handle on PSP + end + if + 825B: not player 2@ defined + then + continue + end + 0474: store_player 2@ currently_armed_weapon_to 0@ + 041E: get_ammo_in_player_weapon 2@ weapon 0@ store_to 1@ + if and + 0@ <> 0 + 1@ <> 0 + 1@ <> 1 + 1@ <> 500 + then + //01EA: print_with_number_now 'NUMBER' number 0@ time 100 flag 1 + 01B6: give_player 2@ weapon 0@ ammo 500 + 017F: set_player 2@ weapon 0@ ammo 500 + end +end + diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.weather.csi b/data/PLUGINS/CLEO/lcs/gtalcs.weather.csi new file mode 100644 index 0000000..1b7582b Binary files /dev/null and b/data/PLUGINS/CLEO/lcs/gtalcs.weather.csi differ diff --git a/data/PLUGINS/CLEO/lcs/gtalcs.weather.txt b/data/PLUGINS/CLEO/lcs/gtalcs.weather.txt new file mode 100644 index 0000000..cfb48a6 --- /dev/null +++ b/data/PLUGINS/CLEO/lcs/gtalcs.weather.txt @@ -0,0 +1,94 @@ +{$CLEO .csi} + +thread 'WEATHER' + +var + 0@ : Integer // general purporse + 10@ : Integer // selected item index + 12@ : Integer // active item index + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + 008B: 20@ = $537 // player handle on Android +else + 008B: 20@ = $536 // player handle on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 825B: not player 20@ defined +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 9 +0DF5: set_menu_active_item_index 12@ + +01B9: set_player_control 20@ to 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // set weather + if and + 10@ >= 0 + 10@ < 8 + then + 01BB: force_weather_now 10@ + break + end + // release weather + if + 10@ == 8 + then + 01BC: release_weather + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 12@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +01B9: set_player 20@ frozen_state 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:menu_desc +hex + // flags + 00 00 00 00 + // title + "WEATHER" 00 + // close button + "CLOSE" 00 + // items + "Sunny" 00 + "Cloudy" 00 + "Rainy" 00 + "Foggy" 00 + "Extra" 20 "sunny" 00 + "Hurricane" 00 + "Extra" 20 "colours" 00 + "Snow" 00 + "Release" 20 "weather" 00 + 00 +end \ No newline at end of file diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.autoaim.csa b/data/PLUGINS/CLEO/vcs/gtavcs.autoaim.csa new file mode 100644 index 0000000..4291a8e Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.autoaim.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.autoaim.txt b/data/PLUGINS/CLEO/vcs/gtavcs.autoaim.txt new file mode 100644 index 0000000..cc58062 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.autoaim.txt @@ -0,0 +1,68 @@ +{$CLEO .csa} + +thread 'AUTOAIM' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char + 2@ : Integer // CPad::GetPad() + 3@ : Integer // frames to keep the aim +end + +wait 1000 + +0DD5: 0@ = get_platform +if + 0@ <> 2 // PSP +then + end_thread +end + +0DD0: 2@ = get_label_addr @eptn_CPad_GetPad +0DDA: 2@ = get_pattern_addr_cstr 2@ index 0 + +3@ = 0 + +while true + wait 0 + + 0DDD: 0@ = get_mutex_var 0 + if + 0@ <> 0 + then + wait 150 + 3@ = 0 + continue + end + + 0004: 1@ = $PLAYER_CHAR + + if and + 026A: is_char_in_control 1@ + 02A8: is_char_on_foot 1@ + 007F: is_button_pressed 0 button 17 + then + gosub @set_aim + 3@ = 20 + continue + end + + if + 3@ > 0 + then + gosub @set_aim + 3@ -= 1 + end +end + +:set_aim +//011F: print_with_number_now 'NUMBER' number 3@ time 100 flag 1 +0DDE: call_func 2@ add_ib 0 __result 'resi' 0@ __pad_index 'i' 0 +0@ += 14 +0DD9: write_mem_addr 0@ value 1 size 2 add_ib 0 protect 0 +return + +:eptn_CPad_GetPad +hex + "?? ?? ?? ?? 51 00 02 3C 18 20 83 00 ?? ?? ?? ?? 08 00 E0 03" 00 +end diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.csi b/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.csi new file mode 100644 index 0000000..fc266f8 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.csi differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.fxt b/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.fxt new file mode 100644 index 0000000..ccd57a3 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.fxt @@ -0,0 +1,2 @@ +BDGSPWN Bodyguards spawned. +BDGOFF Bodyguards removed. \ No newline at end of file diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.txt b/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.txt new file mode 100644 index 0000000..7e987a7 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.bodyguards.txt @@ -0,0 +1,99 @@ +{$CLEO .csi} + +thread 'BDGUARD' + +var + 0@ : Integer // general purporse + 5@ : Float // player/ped x + 6@ : Float // player/ped y + 7@ : Float // player/ped z + 10@ : Integer // bodyguard + 11@ : Integer // bodyguard + 12@ : Integer // bodyguard + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + // 0004: 20@ = $537 // player handle on Android +else + 0004: 20@ = $PLAYER_CHAR // player handle on PSP +end + +if or + 826E: not is_player_control_on 20@ + 02E1: is_char_in_water 20@ +then + end_thread +end + +0DDD: 0@ = get_mutex_var 23487 +if + 0@ == 0 +then + gosub @spawn_bodyguards + 0267: print_help 'BDGSPWN' + 0DDC: set_mutex_var 23487 to 1 +else + gosub @remove_bodyguards + 0267: print_help 'BDGOFF' + 0DDC: set_mutex_var 23487 to 0 +end + +end_thread + +:spawn_bodyguards +gosub @spawn_ped +10@ = 0@ +gosub @spawn_ped +11@ = 0@ +gosub @spawn_ped +12@ = 0@ +return + +:remove_bodyguards +0@ = 10@ +gosub @remove_ped +0@ = 11@ +gosub @remove_ped +0@ = 12@ +gosub @remove_ped +return + +:spawn_ped +0160: request_model #M16 +0228: load_all_models_now +while 8161: not has_model_loaded #M16 + wait 0 +end +0138: generate_random_float_in_range 1.0 3.0 store_to 5@ +0138: generate_random_float_in_range 1.0 3.0 store_to 6@ +02EE: get_offset_from_char_in_world_coords 20@ offset 5@ 6@ 0.0 store_to 5@ 6@ 7@ +0222: create_random_char 5@ 6@ 7@ store_to 0@ +0115: dont_remove_char 0@ +0119: tie_char 0@ to_char 20@ +01EC: set_char 0@ running 1 +0310: set_char_as_player_friend 0@ player 20@ on 1 +0347: set_char_never_targetted 0@ to 1 +0331: set_char_in_players_group_can_fight 0@ can_fight 1 +02A5: set_char_suffers_critical_hits 0@ enable 0 +0324: set_char_can_be_damaged_by_members_of_gang 0@ gang_ID 6 unk3 0 +01A4: set_char_heed_threats 0@ flag 1 +0442: unknown_char_set_command_2691 0@ flag 1 +04D3: clear_char_leadership 0@ +01C9: set_char_accuracy 0@ to 100 +03D5: set_char_max_health 0@ to 2000 +014B: set_char_health 0@ to 2000 +0106: give_weapon_to_char 0@ weapon 28 ammo 9999 +0162: release_model #M16 +return + +:remove_ped +if + 034A: does_char_exist 0@ +then + 020D: remove_char_elegantly 0@ +end +return diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.carspawner.csi b/data/PLUGINS/CLEO/vcs/gtavcs.carspawner.csi new file mode 100644 index 0000000..847e32f Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.carspawner.csi differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.carspawner.txt b/data/PLUGINS/CLEO/vcs/gtavcs.carspawner.txt new file mode 100644 index 0000000..0f0fc82 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.carspawner.txt @@ -0,0 +1,221 @@ +{$CLEO .csi} + +thread 'CARSPWN' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // general purporse + 5@ : Float // player/vehicle x + 6@ : Float // player/vehicle y + 7@ : Float // player/vehicle z + 8@ : Float // player/vehicle heading + 10@ : Integer // selected item index + 11@ : Integer // active item index + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + // 0004: 20@ = $537 // player handle on Android +else + 0004: 20@ = $PLAYER_CHAR // player handle on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 826E: not is_player_control_on 20@ +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 110 +0DF5: set_menu_active_item_index 11@ + +0107: set_player_control 20@ to 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // spawn car + if + 10@ >= 0 + then + 0@ = 10@ + 0@ += 170 + if + 10@ >= 72 + then + 0@ += 1 + end + 0160: request_model 0@ + 0228: load_all_models_now + while 8161: not has_model_loaded 0@ + wait 0 + end + 02EE: get_offset_from_char_in_world_coords 20@ offset 0.2 4.8 0.0 store_to 5@ 6@ 7@ + 00CE: get_char_heading 20@ store_to 8@ + if + 8@ > 180.0 + then + 8@ -= 180.0 + else + 8@ += 180.0 + end + 8@ -= 90.0 + 0048: create_car 0@ at 5@ 6@ 7@ store_to 1@ + 00D1: set_car_heading 1@ to 8@ + 013A: lock_car_doors 1@ mode 0 + 0113: remove_references_to_car 1@ + 0162: release_model 0@ + break + //wait 2000 + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 11@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +0107: set_player_control 20@ to 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:menu_desc +hex + // flags + 00 00 00 00 + // title + "CARSPAWNER" 00 + // close button + "CLOSE" 00 + // items + "6ATV" 00 + "ADMIRAL" 00 + "CHEETAH" 00 + "AUTOGYRO" 00 + "BAGGAGE" 00 + "BANSHEE" 00 + "PEREN" 00 + "BLISTAC" 00 + "BMXBOY" 00 + "BMXGIRL" 00 + "BOBCAT" 00 + "BULLDOZE" 00 + "BURRITO" 00 + "CABBIE" 00 + "CADDY" 00 + "SPEEDER2" 00 + "PIMP" 00 + "DELUXO" 00 + "HUEY" 00 + "HUEYHOSP" 00 + "ELECTRAG" 00 + "ELECTRAP" 00 + "ESPERANT" 00 + "FBICAR" 00 + "FIRETRUK" 00 + "GLENDALE" 00 + "GREENWOO" 00 + "HERMES" 00 + "HOVERCR" 00 + "IDAHO" 00 + "LANDSTAL" 00 + "MANANA" 00 + "MOP50" 00 + "OCEANIC" 00 + "VICECHEE" 00 + "SANCHEZ" 00 + "STALLION" 00 + "POLICEM" 00 + "BOBO" 00 + "PATRIOT" 00 + "PONY" 00 + "SENTINEL" 00 + "PCJ600" 00 + "MAVERICK" 00 + "REEFER" 00 + "SPEEDER" 00 + "LINERUN" 00 + "WALTON" 00 + "BARRACKS" 00 + "PREDATOR" 00 + "FLATBED" 00 + "AMMOTRUK" 00 + "BIPLANE" 00 + "MOONBEAM" 00 + "RUMPO" 00 + "YOLA" 00 + "TAXI" 00 + "AMBULAN" 00 + "STRETCH" 00 + "FAGGIO" 00 + "QUAD" 00 + "ANGEL" 00 + "FREEWAY" 00 + "JETSKI" 00 + "ENFORCE" 00 + "BOXVIL" 00 + "BENSON" 00 + "COACH" 00 + "MULE" 00 + "VOODOO" 00 + "SECURICA" 00 + "TRASH" 00 + "YANKEE" 00 + "MRWHOO" 00 + "SANDKING" 00 + "RHINO" 00 + "DINGHY" 00 + "MARQUIS" 00 + "RIO" 00 + "TROPIC" 00 + "FORKLIFT" 00 + "STREETFI" 00 + "VIRGO" 00 + "STINGER" 00 + "BFINJECT" 00 + "PHEONIX" 00 + "SQUALO" 00 + "JETMAX" 00 + "MESA" 00 + "VCNMAV" 00 + "POLMAV" 00 + "SPARROW" 00 + "SESPAROW" 00 + "SCARAB" 00 + "CHOLLO" 00 + "COMET" 00 + "CUBAN" 00 + "FBIRAN" 00 + "GANGBUR" 00 + "INFERNUS" 00 + "REGINA" 00 + "SABRE" 00 + "SABRETB" 00 + "SENTXS" 00 + "HUNTER" 00 + "WASHIN" 00 + "COASTG" 00 + "SKIMMER" 00 + "CHOPPER" 00 + 00 +end + + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.cheats.csi b/data/PLUGINS/CLEO/vcs/gtavcs.cheats.csi new file mode 100644 index 0000000..38d5684 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.cheats.csi differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.cheats.txt b/data/PLUGINS/CLEO/vcs/gtavcs.cheats.txt new file mode 100644 index 0000000..23982c5 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.cheats.txt @@ -0,0 +1,190 @@ +{$CLEO .csi} + +thread 'CHEATS' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // general purporse + 7@ : Integer // cheat input ptr + 8@ : Integer // iterator + 10@ : Integer // selected item index + 12@ : Integer // active item index + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + // 0004: 20@ = $537 // player handle on Android +else + 0004: 20@ = $PLAYER_CHAR // player handle on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 826E: not is_player_control_on 20@ +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 31 +0DF5: set_menu_active_item_index 12@ + +0107: set_player_control 20@ to 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // activate cheat + if + 10@ >= 0 + then + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0DD0: 2@ = get_label_addr @efunc_CPad_AddToCheatString + // 0DD1: 2@ = get_func_addr_by_cstr_name 2@ + else + 0DD0: 2@ = get_label_addr @eptn_CPad_AddToCheatString + 0DDA: 2@ = get_pattern_addr_cstr 2@ index 0 + end + 0DD0: 0@ = get_label_addr @inst_CPad + 0DD0: 7@ = get_label_addr @cheat_inputs + 10@ *= 8 + 7@ += 10@ + for 8@ = 0 to 7 + 0DD8: 1@ = read_mem_addr 7@ size 1 add_ib 0 + 7@ += 1 + 0DDE: call_func 2@ add_ib 0 __pad_inst 'i' 0@ __char 'i' 1@ + end + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 12@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +0107: set_player_control 20@ to 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:menu_desc +hex + // flags + 00 00 00 00 + // title + "CHEATS" 00 + // close button + "CLOSE" 00 + // items + "Weapon" 20 "1" 00 + "Weapon" 20 "2" 00 + "Weapon" 20 "3" 00 + "Money" 00 + "Armour" 00 + "Health" 00 + "Wanted" 20 "Up" 00 + "Never" 20 "Wanted" 00 + "Sunny" 20 "Weather" 00 + "Extra" 20 "Sunny" 00 + "Cloudy" 20 "Weather" 00 + "Rainy" 20 "Weather" 00 + "Foggy" 20 "Weather" 00 + "Tank" 00 + "Fast" 20 "Weather" 00 + "BlowUp" 20 "Cars" 00 + "Mayhem" 00 + "Peds" 20 "Attack" 20 "Vic" 00 + "Weapons" 20 "For" 20 "All" 00 + "Fast" 20 "Time" 00 + "Slow" 20 "Time" 00 + "Strong" 20 "Grip" 00 + "Suicide" 00 + "Traffic" 20 "Lights" 00 + "Mad" 20 "Cars" 00 + "Black" 20 "Cars" 00 + "Trashmaster" 00 + "PickUp" 20 "Chicks" 00 + "Fanny" 20 "Magnet" 00 + "Glass" 20 "Cars" 00 + "Topsy" 20 "Turvy" 00 + 00 +end + +:cheat_inputs +hex + "LRXUDSLR" + "LRSUDTLR" + "LRTUDCLR" + "UDLRXX12" + "UDLRSS12" + "UDLRCC12" + "URSSDLCC" + "URTTDLXX" + "LD21RULC" + "LD21RULX" + "LD12RULS" + "LD12RULT" + "LDTXRUL1" + "U1D2L1R2" + "211DUXD1" + "122LRSD2" + "211DLCD1" + "DTUX1212" + "U1D2LCRT" + "LL22UTDX" + "LLCCDUTX" + "DLU12TCX" + "RRCC12DX" + "UDTX12LC" + "UURLTCCS" + "1212LCUX" + "DURT1T1T" + "DUR11SU1" + "R1D1CU1S" + "RULDTT12" + "SSS112LR" +end + +:eptn_CPad_AddToCheatString +hex + "?? ?? ?? ?? 00 2E 05 00 08 00 B1 FF 03 2E 05 00 2D 88 80 00" 00 +end + +:inst_CPad +hex + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +end diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.clear_wanted.csi b/data/PLUGINS/CLEO/vcs/gtavcs.clear_wanted.csi new file mode 100644 index 0000000..8befe39 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.clear_wanted.csi differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.clear_wanted.txt b/data/PLUGINS/CLEO/vcs/gtavcs.clear_wanted.txt new file mode 100644 index 0000000..044de9f --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.clear_wanted.txt @@ -0,0 +1,23 @@ +{$CLEO .csi} + +thread 'CLRWNTD' + +var + 0@ : Integer // general purporse +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + // 0004: 0@ = $537 // player handle on Android +else + 0004: 0@ = $PLAYER_CHAR // player handle on PSP +end +if + 026E: is_player_control_on 0@ +then + 009B: clear_wanted_level 0@ +end + +end_thread diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.infswim.csa b/data/PLUGINS/CLEO/vcs/gtavcs.infswim.csa new file mode 100644 index 0000000..c4e90c2 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.infswim.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.infswim.txt b/data/PLUGINS/CLEO/vcs/gtavcs.infswim.txt new file mode 100644 index 0000000..7bf9ea4 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.infswim.txt @@ -0,0 +1,29 @@ +{$CLEO .csa} + +thread 'INFSWIM' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 1@ = $537 // player handle on Android + else + 0004: 1@ = $PLAYER_CHAR // player handle on PSP + end + if + 826E: not is_player_control_on 1@ + then + continue + end + 02F9: set_char_drowns_in_water 1@ to 0 +end + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.invincibility.csa b/data/PLUGINS/CLEO/vcs/gtavcs.invincibility.csa new file mode 100644 index 0000000..3b9abe9 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.invincibility.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.invincibility.txt b/data/PLUGINS/CLEO/vcs/gtavcs.invincibility.txt new file mode 100644 index 0000000..b90af65 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.invincibility.txt @@ -0,0 +1,39 @@ +{$CLEO .csa} + +thread 'INVINC' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char + 2@ : Integer // player's car +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 1@ = $537 // player handle on Android + else + 0004: 1@ = $PLAYER_CHAR // player handle on PSP + end + if + 826E: not is_player_control_on 1@ + then + continue + end + 014B: set_char_health 1@ to 1000 + 01B1: set_char_proofs 1@ BP 1 FP 1 EP 1 CP 1 MP 1 + 02F9: set_char_drowns_in_water 1@ to 0 + if + 007E: is_char_in_any_car 1@ + then + 024B: store_car_char_is_in_no_save 1@ store_to 2@ + 014C: set_car_health 2@ to 2000 + 01B2: set_car_proofs 2@ BP 1 FP 1 EP 1 CP 1 MP 1 + end +end + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.missionfixes.csa b/data/PLUGINS/CLEO/vcs/gtavcs.missionfixes.csa new file mode 100644 index 0000000..59f28df Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.missionfixes.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.missionfixes.txt b/data/PLUGINS/CLEO/vcs/gtavcs.missionfixes.txt new file mode 100644 index 0000000..e4527c9 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.missionfixes.txt @@ -0,0 +1,44 @@ +{$CLEO .csa} + +thread 'MSNFIX' + +var + 0@ : Integer // general purporse + 1@ : Integer // player handle + 2@ : Integer // onmission handle +end + +while true + wait 1000 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 1@ = $537 // player handle on Android + // 0004: 2@ = $561 // onmission on Android + else + 0004: 1@ = $PLAYER_CHAR // player handle on PSP + 0004: 2@ = $ONMISSION // onmission on PSP + end + if or + 826E: not is_player_control_on 1@ + 2@ == 0 + then + continue + end + // Boomshine Blowout, timer fix + 040F: get_length_of_text_entry 'PHI2_F0' store_to 0@ + if + 0@ > 0 + then + $5530 = 0 + $5531 = 0 + end + // The Exchange, timer fix + 040F: get_length_of_text_entry 'DIA2_04' store_to 0@ + if + 0@ > 0 + then + $5436 = 100.0 + end +end \ No newline at end of file diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.nevertired.csa b/data/PLUGINS/CLEO/vcs/gtavcs.nevertired.csa new file mode 100644 index 0000000..9391a0b Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.nevertired.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.nevertired.txt b/data/PLUGINS/CLEO/vcs/gtavcs.nevertired.txt new file mode 100644 index 0000000..649bb07 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.nevertired.txt @@ -0,0 +1,29 @@ +{$CLEO .csa} + +thread 'NOTIRE' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 1@ = $537 // player handle on Android + else + 0004: 1@ = $PLAYER_CHAR // player handle on PSP + end + if + 826E: not is_player_control_on 1@ + then + continue + end + 01FB: set_player_never_gets_tired 1@ set 1 +end + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.regen.csa b/data/PLUGINS/CLEO/vcs/gtavcs.regen.csa new file mode 100644 index 0000000..b67e132 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.regen.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.regen.txt b/data/PLUGINS/CLEO/vcs/gtavcs.regen.txt new file mode 100644 index 0000000..c985fd6 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.regen.txt @@ -0,0 +1,36 @@ +{$CLEO .csa} + +thread 'REGEN' + +var + 0@ : Integer // general purporse + 1@ : Integer // player handle +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 1@ = $537 // player handle on Android + else + 0004: 1@ = $PLAYER_CHAR // player handle on PSP + end + if + 826E: not is_player_control_on 1@ + then + continue + end + 014D: get_char_health 1@ store_to 0@ + if and + 0@ <> 200 + 0@ <> 0 + then + 0@ += 1 + 014B: set_char_health 1@ to 0@ + end +end + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.select_mission.csi b/data/PLUGINS/CLEO/vcs/gtavcs.select_mission.csi new file mode 100644 index 0000000..d7229cd Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.select_mission.csi differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.select_mission.txt b/data/PLUGINS/CLEO/vcs/gtavcs.select_mission.txt new file mode 100644 index 0000000..1cfd355 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.select_mission.txt @@ -0,0 +1,153 @@ +{$CLEO .csi} + +thread 'SLMSN' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // player handle + 3@ : Integer // onmission handle + 10@ : Integer // selected item index + 11@ : Integer // active item index +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + // 0004: 2@ = $537 // player handle on Android + // 0004: 3@ = $561 // onmission on Android +else + 0004: 2@ = $PLAYER_CHAR // player handle on PSP + 0004: 3@ = $ONMISSION // onmission on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 3@ == 1 // on mission + 826E: not is_player_control_on 2@ +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 57 +0DF5: set_menu_active_item_index 11@ + +0107: set_player_control 2@ to 0 + +1@ = 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // teleport to coords + if and + 10@ >= 0 + 10@ < 70 + then + 1@ = 10@ + 1@ += 42 + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 11@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +0107: set_player_control 2@ to 1 +0DDC: set_mutex_var 0 to 0 + +// start mission +if + 1@ > 0 +then + wait 100 + 0289: load_and_launch_mission_internal 1@ +end + +end_thread + +:menu_desc +hex + // flags + 03 00 00 00 + // title + "SELECT" 20 "MISSION" 00 + // close button + "CLOSE" 00 + // items + "JER_A02" 00 + "JER_A03" 00 + "PHI_A01" 00 + "PHI_A02" 00 + "PHI_A03" 00 + "PHI_A04" 00 + "MAR_A01" 00 + "MAR_A02" 00 + "MAR_A03" 00 + "MAR_A04" 00 + "MAR_A05" 00 + "LOU_A01" 00 + "LOU_A02" 00 + "LOU_A03" 00 + "LOU_A04" 00 + "LOU_B01" 00 + "LOU_B02" 00 + "LAN_B01" 00 + "LAN_B02" 00 + "LAN_B04" 00 + "LAN_B05" 00 + "LAN_B06" 00 + "UMB_B01" 00 + "UMB_B02" 00 + "UMB_B03" 00 + "UMB_B04" 00 + "BRY_B01" 00 + "BRY_B03" 00 + "BRY_B04" 00 + "MEN_C01" 00 + "MEN_C02" 00 + "MEN_C03" 00 + "MEN_C05" 00 + "MEN_C06" 00 + "REN_C01" 00 + "REN_C02" 00 + "REN_C03" 00 + "REN_C04" 00 + "REN_C05" 00 + "REN_C06" 00 + "LAN_C01" 00 + "LAN_C03" 00 + "LAN_C04" 00 + "LAN_C05" 00 + "LAN_C06" 00 + "LAN_C07" 00 + "LAN_C08" 00 + "LAN_C09" 00 + "GON_C01" 00 + "GON_C02" 00 + "GON_C03" 00 + "GON_C04" 00 + "DIA_C01" 00 + "DIA_C02" 00 + "DIA_C03" 00 + "DIA_C04" 00 + "DIA_C05" 00 + 00 +end + + \ No newline at end of file diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.strongtyres.csa b/data/PLUGINS/CLEO/vcs/gtavcs.strongtyres.csa new file mode 100644 index 0000000..bd991da Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.strongtyres.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.strongtyres.txt b/data/PLUGINS/CLEO/vcs/gtavcs.strongtyres.txt new file mode 100644 index 0000000..ad1e605 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.strongtyres.txt @@ -0,0 +1,41 @@ +{$CLEO .csa} + +thread 'STRTYRE' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char + 2@ : Integer // player's car +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 1@ = $537 // player handle on Android + else + 0004: 1@ = $PLAYER_CHAR // player handle on PSP + end + if + 826E: not is_player_control_on 1@ + then + continue + end + if + 007E: is_char_in_any_car 1@ + then + 024B: store_car_char_is_in_no_save 1@ store_to 2@ + 02BD: get_driver_of_car 2@ store_to 0@ + if + 0@ <> 1@ + then + continue + end + 0334: set_can_burst_car_tyres 2@ to 0 + end +end + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.teleport.csi b/data/PLUGINS/CLEO/vcs/gtavcs.teleport.csi new file mode 100644 index 0000000..fe5dde4 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.teleport.csi differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.teleport.txt b/data/PLUGINS/CLEO/vcs/gtavcs.teleport.txt new file mode 100644 index 0000000..2968ce6 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.teleport.txt @@ -0,0 +1,154 @@ +{$CLEO .csi} + +thread 'TLPRT' +var + 0@ : Integer + 3@ : Integer + 4@ : Float + 5@ : Float + 6@ : Float + 10@ : Integer // selected item index + 12@ : Integer // active item index + 20@ : Integer // player actor/char +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + // 0004: 20@ = $537 // player handle on Android +else + 0004: 20@ = $PLAYER_CHAR // player handle on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 826E: not is_player_control_on 20@ +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 +0107: set_player_control 20@ to 0 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 9 +0DF5: set_menu_active_item_index 12@ + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // activate + if and + 10@ >= 0 + 10@ < 9 + then + 3@ = 10@ + gosub @get_coords + 01B5: get_closest_car_node 4@ 5@ 6@ store_to 4@ 5@ 6@ + 6@ += 1.0 + 0044: set_char_coordinates 20@ to 4@ 5@ 6@ + wait 100 + 0044: set_char_coordinates 20@ to 4@ 5@ 6@ + 0221: set_camera_behind_player + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 12@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 + +0DF3: delete_menu +0107: set_player_control 20@ to 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:get_coords +if 3@ == 0 +then + 4@ = -271.0 + 5@ = -501.0 + 6@ = 11.0 +end +if 3@ == 1 +then + 4@ = -681.0 + 5@ = -1432.0 + 6@ = 10.0 +end +if 3@ == 2 +then + 4@ = -1431.0 + 5@ = -807.0 + 6@ = 14.0 +end +if 3@ == 3 +then + 4@ = -1686.0 + 5@ = -336.0 + 6@ = 14.0 +end +if 3@ == 4 +then + 4@ = -664.0 + 5@ = 739.0 + 6@ = 11.0 +end +if 3@ == 5 +then + 4@ = -1099.0 + 5@ = 1328.0 + 6@ = 20.0 +end +if 3@ == 6 +then + 4@ = 32.0 + 5@ = 961.0 + 6@ = 10.0 +end +if 3@ == 7 +then + 4@ = 504.0 + 5@ = -81.0 + 6@ = 10.0 +end +if 3@ == 8 +then + 4@ = 238.0 + 5@ = -1280.0 + 6@ = 11.0 +end +return + +:menu_desc +hex + // flags + 00 00 00 00 + // title + "TELEPORT" 00 + // close button + "CLOSE" 00 + // items + "Starfish" 20 "island" 00 + "Docks" 00 + "Airport" 00 + "Military" 20 "base" 00 + "Downtown" 00 + "Stadium" 00 + "Movie" 20 "studio" 00 + "Malibu" 00 + "Ocean" 20 "view" 00 + 00 +end \ No newline at end of file diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.unlimammo.csa b/data/PLUGINS/CLEO/vcs/gtavcs.unlimammo.csa new file mode 100644 index 0000000..ed7f14c Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.unlimammo.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.unlimammo.txt b/data/PLUGINS/CLEO/vcs/gtavcs.unlimammo.txt new file mode 100644 index 0000000..48c08da --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.unlimammo.txt @@ -0,0 +1,41 @@ +{$CLEO .csa} + +thread 'UNLAMMO' + +var + 0@ : Integer // general purporse + 1@ : Integer // general purporse + 2@ : Integer // player handle +end + +wait 1000 + +while true + wait 100 + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 2@ = $537 // player handle on Android + else + 0004: 2@ = $PLAYER_CHAR // player handle on PSP + end + if + 826A: not is_char_in_control 2@ + then + continue + end + 02C0: get_current_char_weapon 2@ store_to 0@ + 028B: get_ammo_in_char_weapon 2@ weapon 0@ store_to 1@ + if and + 0@ <> 0 + 1@ <> 0 + 1@ <> 1 + 1@ <> 500 + then + //011F: print_with_number_now 'NUMBER' number 0@ time 100 flag 1 + 0106: give_weapon_to_char 2@ weapon 0@ ammo 500 + 00D5: set_char_ammo 2@ weapon 0@ ammo 500 + end +end + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.vehweapon.csa b/data/PLUGINS/CLEO/vcs/gtavcs.vehweapon.csa new file mode 100644 index 0000000..887fbf2 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.vehweapon.csa differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.vehweapon.txt b/data/PLUGINS/CLEO/vcs/gtavcs.vehweapon.txt new file mode 100644 index 0000000..899a022 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.vehweapon.txt @@ -0,0 +1,79 @@ +{$CLEO .csa} + +thread 'VEHWEAP' + +var + 0@ : Integer // general purporse + 1@ : Integer // player actor/char + 2@ : Integer // player's car + 3@ : Float // coords from + 4@ : Float // coords from + 5@ : Float // coords from + 6@ : Float // coords to + 7@ : Float // coords to + 8@ : Float // coords to +end + +wait 1000 + +while true + wait 0 + 0DDD: 0@ = get_mutex_var 0 + if + 0@ == 1 + then + wait 1000 + continue + end + 0DD5: 0@ = get_platform + if + 0@ == 1 + then + // 0004: 1@ = $537 // player handle on Android + else + 0004: 1@ = $PLAYER_CHAR // player handle on PSP + end + if or + 826E: not is_player_control_on 1@ + 807E: not is_char_in_any_car 1@ + then + continue + end + 024B: store_car_char_is_in_no_save 1@ store_to 2@ + 02BD: get_driver_of_car 2@ store_to 0@ + if or + 0@ <> 1@ + 807F: not is_button_pressed 0 button 17 + 007F: is_button_pressed 0 button 4 + 007F: is_button_pressed 0 button 6 + then + continue + end + if or + 00A6: is_car_model 2@ model #HUNTER + 00A6: is_car_model 2@ model #SESPAROW + 00A6: is_car_model 2@ model #AUTOGYRO + 00A6: is_car_model 2@ model #RHINO + 00A6: is_car_model 2@ model #FIRETRUK + 00A6: is_car_model 2@ model #PREDATOR + then + continue + end + if or + 00A6: is_car_model 2@ model #BIPLANE + 00A6: is_car_model 2@ model #REEFER + 00A6: is_car_model 2@ model #MARQUIS + 00A6: is_car_model 2@ model #RIO + 00A6: is_car_model 2@ model #TROPIC + then + continue + end + 027F: get_offset_from_car_in_world_coords 2@ offset 2.4 1.8 0.1 store_to 3@ 4@ 5@ + 027F: get_offset_from_car_in_world_coords 2@ offset 2.4 10.0 0.1 store_to 6@ 7@ 8@ + 04BC: create_projectile weapon 18 from 3@ 4@ 5@ to 6@ 7@ 8@ power 1.0 car_handle 2@ flag 0 store_to 0@ + 027F: get_offset_from_car_in_world_coords 2@ offset -2.4 1.8 0.1 store_to 3@ 4@ 5@ + 027F: get_offset_from_car_in_world_coords 2@ offset -2.4 10.0 0.1 store_to 6@ 7@ 8@ + 04BC: create_projectile weapon 18 from 3@ 4@ 5@ to 6@ 7@ 8@ power 1.0 car_handle 2@ flag 0 store_to 0@ + wait 300 +end + diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.weather.csi b/data/PLUGINS/CLEO/vcs/gtavcs.weather.csi new file mode 100644 index 0000000..bc38337 Binary files /dev/null and b/data/PLUGINS/CLEO/vcs/gtavcs.weather.csi differ diff --git a/data/PLUGINS/CLEO/vcs/gtavcs.weather.txt b/data/PLUGINS/CLEO/vcs/gtavcs.weather.txt new file mode 100644 index 0000000..46213e8 --- /dev/null +++ b/data/PLUGINS/CLEO/vcs/gtavcs.weather.txt @@ -0,0 +1,94 @@ +{$CLEO .csi} + +thread 'WEATHER' + +var + 0@ : Integer // general purporse + 10@ : Integer // selected item index + 12@ : Integer // active item index + 20@ : Integer // player handle +end + +0DD5: 0@ = get_platform +if + 0@ == 1 +then + // 0004: 20@ = $537 // player handle on Android +else + 0004: 20@ = $PLAYER_CHAR // player handle on PSP +end + +0DDD: 0@ = get_mutex_var 0 +if or + 0@ == 1 + 826E: not is_player_control_on 20@ +then + end_thread +end + +0DDC: set_mutex_var 0 to 1 + +0DD0: 0@ = get_label_addr @menu_desc +0DF2: create_menu 0@ items 9 +0DF5: set_menu_active_item_index 12@ + +0107: set_player_control 20@ to 0 + +// touch handle loop +while true + wait 0 + // resets menu touch item index as well + 0DF4: 10@ = get_menu_touched_item_index maxtime 100 + // set weather + if and + 10@ >= 0 + 10@ < 8 + then + 0109: force_weather_now 10@ + break + end + // release weather + if + 10@ == 8 + then + 010A: release_weather + break + end + // close menu + if + 10@ == -2 + then + break + end +end + +0DF6: 12@ = get_menu_active_item_index + +// wait to show what was selected and not to trigger player controls with current touch +wait 100 +0DF3: delete_menu +0107: set_player_control 20@ to 1 +0DDC: set_mutex_var 0 to 0 + +end_thread + +:menu_desc +hex + // flags + 00 00 00 00 + // title + "WEATHER" 00 + // close button + "CLOSE" 00 + // items + "Sunny" 00 + "Cloudy" 00 + "Rainy" 00 + "Foggy" 00 + "Extra" 20 "sunny" 00 + "Hurricane" 00 + "Extra" 20 "colours" 00 + "Ultra" 20 "sunny" 00 + "Release" 20 "weather" 00 + 00 +end diff --git a/includes/pcsx2/log.c b/includes/pcsx2/log.c new file mode 100644 index 0000000..3ab3940 --- /dev/null +++ b/includes/pcsx2/log.c @@ -0,0 +1,81 @@ +#include "log.h" +#include +#ifndef NANOPRINTF_IMPLEMENTATION +#define NANOPRINTF_IMPLEMENTATION +#include "nanoprintf.h" +#endif + +struct circular_buffer_t cb; + +void cb_init(struct circular_buffer_t* cb, void* buf, size_t capacity, size_t sz) +{ + cb->buffer = buf; + cb->buffer_end = (char*)cb->buffer + capacity * sz; + cb->capacity = capacity; + cb->count = 0; + cb->sz = sz; + cb->head = cb->buffer; + cb->tail = cb->buffer; +} + +void cb_push_back(struct circular_buffer_t* cb, const void* item) +{ + if (cb->buffer) + { + if (cb->count < cb->capacity) + { + memcpy(cb->head, item, cb->sz); + cb->head = (char*)cb->head + cb->sz; + if (cb->head == cb->buffer_end) + cb->head = cb->buffer; + cb->count++; + } + else + { + memcpy(cb->buffer, (char*)cb->buffer + cb->sz, (size_t)((char*)cb->buffer_end - (char*)cb->buffer - cb->sz)); + cb->head = (char*)cb->buffer_end - cb->sz; + memcpy(cb->head, item, cb->sz); + if (cb->head == cb->buffer_end) + cb->head = cb->buffer; + cb->count++; + } + } +} + +void SetBuffer(void* buffer, size_t capacity, size_t elem_size) +{ + cb_init(&cb, buffer, capacity, elem_size); +} + +void Write(char* message) +{ + if (cb.buffer) + cb_push_back(&cb, message); +} + +char logf_buffer[255] = { 1 }; +void WriteFormatted(const char* format, ...) +{ + if (cb.buffer) + { + va_list val; + va_start(val, format); + npf_vsnprintf(logf_buffer, sizeof(logf_buffer), format, val); + logger.Write(logf_buffer); + va_end(val); + } +} + +void ClearLog() +{ + if (cb.buffer) + memset(cb.buffer, 0, cb.buffer_end - cb.buffer); +} + +struct logger_t logger = +{ + .SetBuffer = SetBuffer, + .Write = Write, + .WriteF = WriteFormatted, + .ClearLog = ClearLog, +}; \ No newline at end of file diff --git a/includes/pcsx2/log.h b/includes/pcsx2/log.h new file mode 100644 index 0000000..a1e93b3 --- /dev/null +++ b/includes/pcsx2/log.h @@ -0,0 +1,30 @@ +#ifndef H_LOG +#define H_LOG +#include +#include +#include +#include "nanoprintf.h" + +struct circular_buffer_t +{ + void* buffer; // data buffer + void* buffer_end; // end of data buffer + size_t capacity; // maximum number of items in the buffer + size_t count; // number of items in the buffer + size_t sz; // size of each item in the buffer + void* head; // pointer to head + void* tail; // pointer to tail +}; + +void cb_init(struct circular_buffer_t* cb, void* buf, size_t capacity, size_t sz); +void cb_push_back(struct circular_buffer_t* cb, const void* item); + +struct logger_t { + void (*SetBuffer)(void* buffer, size_t capacity, size_t elem_size); + void (*Write)(char* message); + void (*WriteF)(const char* format, ...); + void (*ClearLog)(); +}; +extern struct circular_buffer_t circular_buffer; +extern struct logger_t logger; +#endif diff --git a/includes/pcsx2/memalloc.c b/includes/pcsx2/memalloc.c new file mode 100644 index 0000000..811338e --- /dev/null +++ b/includes/pcsx2/memalloc.c @@ -0,0 +1,205 @@ +/* + * pmpa.c + * Part of pmpa + * Copyright (c) 2014 Philip Wernersbach + * + * Dual-Licensed under the Public Domain and the Unlicense. + * Choose the one that you prefer. + */ + +#include +#include +#include +#include "memalloc.h" + +typedef struct { + pmpa_memory_int size; + bool allocated; + char data; +} pmpa_memory_block; + +#define PMPA_MEMORY_BLOCK_HEADER_SIZE ( offsetof(pmpa_memory_block, data) ) +#define PMPA_FIRST_VALID_ADDRESS_IN_POOL master_memory_block +#define PMPA_LAST_VALID_ADDRESS_IN_POOL (PMPA_FIRST_VALID_ADDRESS_IN_POOL + master_memory_block_size) +#define PMPA_POINTER_IS_IN_RANGE(a, b, c) ( ((a) < ((b) + (c))) && ((a) >= (b)) ) +#define PMPA_POINTER_IS_IN_POOL(a) PMPA_POINTER_IS_IN_RANGE(a, PMPA_FIRST_VALID_ADDRESS_IN_POOL, master_memory_block_size) + +#ifndef MEM_CUSTOM_TOTAL_SIZE +#define MEM_CUSTOM_TOTAL_SIZE 1000000 +#endif + +static pmpa_memory_block master_memory_block[MEM_CUSTOM_TOTAL_SIZE] = { 0 }; +static pmpa_memory_int master_memory_block_size = MEM_CUSTOM_TOTAL_SIZE; + +/* + * Internal functions. + */ + +static void concat_sequential_blocks(pmpa_memory_block *memory_block, bool is_allocated) +{ + pmpa_memory_block *current_memory_block = memory_block; + pmpa_memory_block *next_memory_block = NULL; + + if (current_memory_block->allocated != is_allocated) + return; + + while ( (next_memory_block = current_memory_block + current_memory_block->size + PMPA_MEMORY_BLOCK_HEADER_SIZE) && + PMPA_POINTER_IS_IN_POOL(next_memory_block + PMPA_MEMORY_BLOCK_HEADER_SIZE) && + (next_memory_block->allocated == is_allocated) ) + current_memory_block->size += next_memory_block->size + PMPA_MEMORY_BLOCK_HEADER_SIZE; + + +} + +static pmpa_memory_block *find_first_block(bool is_allocated, pmpa_memory_int min_size) +{ + pmpa_memory_block *memory_block = master_memory_block; + + while (PMPA_POINTER_IS_IN_POOL(memory_block + sizeof(pmpa_memory_block))) { + /* If we're trying to find an block, then defragment the pool as we go along. + * This incurs a minor speed penalty, but not having to spend time + * iterating over a fragmented pool makes up for it. */ + if (is_allocated == false) + concat_sequential_blocks(memory_block, is_allocated); + + if ( (memory_block->allocated == is_allocated) && (memory_block->size >= min_size) ) { + return memory_block; + } else { + memory_block += memory_block->size + PMPA_MEMORY_BLOCK_HEADER_SIZE; + } + } + + return NULL; +} + +static void split_block(pmpa_memory_block *memory_block, pmpa_memory_int size) +{ + pmpa_memory_block *second_memory_block = memory_block + size + PMPA_MEMORY_BLOCK_HEADER_SIZE; + pmpa_memory_block *original_second_memory_block = memory_block + memory_block->size + PMPA_MEMORY_BLOCK_HEADER_SIZE; + pmpa_memory_int original_memory_block_size = memory_block->size; + + memory_block->allocated = false; + + /* We can't split this block if there's not enough room to create another one. */ + if ( PMPA_POINTER_IS_IN_RANGE((second_memory_block + PMPA_MEMORY_BLOCK_HEADER_SIZE), 0, original_second_memory_block) && + ( PMPA_POINTER_IS_IN_POOL(second_memory_block + sizeof(pmpa_memory_block)) ) ) { + memory_block->size = size; + + second_memory_block->size = original_memory_block_size - (size + PMPA_MEMORY_BLOCK_HEADER_SIZE); + second_memory_block->allocated = false; + } +} + +/* + * Externally accessible C memory functions. + */ + +void *pmpa_malloc(size_t size) +{ + pmpa_memory_block *memory_block = find_first_block(false, size); + + if (memory_block) { + split_block(memory_block, size); + memory_block->allocated = true; + + return &(memory_block->data); + } else { + return NULL; + } +} + +void *pmpa_calloc(size_t nelem, size_t elsize) +{ + pmpa_memory_int ptr_size = nelem * elsize; + void *ptr = pmpa_malloc(ptr_size); + + if (ptr) { + memset(ptr, 0, ptr_size); + + return ptr; + } else { + return NULL; + } +} + +void *pmpa_realloc(void *ptr, size_t size) +{ + pmpa_memory_block *memory_block = NULL; + pmpa_memory_block *new_memory_block = NULL; + + pmpa_memory_int memory_block_original_size = 0; + + /* If ptr is NULL, realloc() behaves like malloc(). */ + if (!ptr) + return pmpa_malloc(size); + + memory_block = (pmpa_memory_block*)((char*)ptr - PMPA_MEMORY_BLOCK_HEADER_SIZE); + memory_block_original_size = memory_block->size; + + /* Try to cheat by concatenating the current block with contiguous + * empty blocks after it, and seeing if the new block is big enough. */ + memory_block->allocated = false; + concat_sequential_blocks(memory_block, memory_block->allocated); + memory_block->allocated = true; + + if (memory_block->size >= size) { + /* The new block is big enough, split it and use it. */ + split_block(memory_block, size); + memory_block->allocated = true; + + return &(memory_block->data); + } else { + /* The new block is not big enough. */ + + /* Restore the memory block's original size. */ + split_block(memory_block, memory_block_original_size); + memory_block->allocated = true; + + /* Find another block and try to use that. */ + if ( !(new_memory_block = find_first_block(false, size)) ) + return NULL; + + split_block(new_memory_block, size); + new_memory_block->allocated = true; + + memcpy(&(new_memory_block->data), &(memory_block->data), memory_block->size); + + pmpa_free(&(memory_block->data)); + return &(new_memory_block->data); + } + + return NULL; +} + +void pmpa_free(void *ptr) +{ + pmpa_memory_block* memory_block = (pmpa_memory_block*)((char*)ptr - PMPA_MEMORY_BLOCK_HEADER_SIZE); + + if (ptr == NULL) + return; + + memory_block->allocated = false; +} + +void* AllocMemBlock(size_t noOfBytes) +{ + return pmpa_malloc(noOfBytes); +} + +void FreeMemBlock(void* ptr) +{ + return pmpa_free(ptr); +} + +#ifdef __cplusplus +void* operator new(size_t size) +{ + void* p = AllocMemBlock(size); + return p; +} + +void operator delete(void* p) +{ + FreeMemBlock(p); +} +#endif \ No newline at end of file diff --git a/includes/pcsx2/memalloc.h b/includes/pcsx2/memalloc.h new file mode 100644 index 0000000..d5aa28b --- /dev/null +++ b/includes/pcsx2/memalloc.h @@ -0,0 +1,42 @@ +/* + * pmpa.h + * Part of pmpa + * Copyright (c) 2014 Philip Wernersbach + * + * Dual-Licensed under the Public Domain and the Unlicense. + * Choose the one that you prefer. + */ + +#ifndef HAVE_PMPA_H + +#include +#include +#include + +typedef uint32_t pmpa_memory_int; + +/* + * Externally accessible C memory functions. + */ + +#ifdef __cplusplus +extern "C" +{ +#endif +void* pmpa_malloc(size_t size); +void* pmpa_calloc(size_t nelem, size_t elsize); +void* pmpa_realloc(void* ptr, size_t size); +void pmpa_free(void* ptr); +void* AllocMemBlock(size_t noOfBytes); +void FreeMemBlock(void* ptr); +#ifdef __cplusplus +} +#endif + +#define malloc(a) pmpa_malloc(a) +#define calloc(a, b) pmpa_calloc(a, b) +#define realloc(a, b) pmpa_realloc(a, b) +#define free(a) pmpa_free(a) + +#define HAVE_PMPA_H +#endif \ No newline at end of file diff --git a/includes/pcsx2/nanoprintf.h b/includes/pcsx2/nanoprintf.h new file mode 100644 index 0000000..7b19acd --- /dev/null +++ b/includes/pcsx2/nanoprintf.h @@ -0,0 +1,1078 @@ +/* + nanoprintf: a tiny embeddable printf replacement written in C. + https://github.com/charlesnicholson/nanoprintf + charles.nicholson+nanoprintf@gmail.com + + LICENSE: + -------- + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ + +#ifndef NANOPRINTF_H_INCLUDED +#define NANOPRINTF_H_INCLUDED + +#include +#include + +// Define this to fully sandbox nanoprintf inside of a translation unit. +#ifdef NANOPRINTF_VISIBILITY_STATIC + #define NPF_VISIBILITY static +#else + #define NPF_VISIBILITY extern +#endif + +#if defined(__clang__) + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \ + __attribute__((__format__(__printf__, FORMAT_INDEX, VARGS_INDEX))) +#elif defined(__GNUC__) || defined(__GNUG__) + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) \ + __attribute__((format(printf, FORMAT_INDEX, VARGS_INDEX))) +#else + #define NPF_PRINTF_ATTR(FORMAT_INDEX, VARGS_INDEX) +#endif + +// Public API + +#ifdef __cplusplus +extern "C" { +#endif + +NPF_VISIBILITY int npf_snprintf(char *buffer, size_t bufsz, const char *format, + ...) NPF_PRINTF_ATTR(3, 4); + +NPF_VISIBILITY int npf_vsnprintf(char *buffer, size_t bufsz, char const *format, + va_list vlist) NPF_PRINTF_ATTR(3, 0); + +typedef void (*npf_putc)(int c, void *ctx); +NPF_VISIBILITY int npf_pprintf(npf_putc pc, void *pc_ctx, char const *format, + ...) NPF_PRINTF_ATTR(3, 4); + +NPF_VISIBILITY int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, + va_list vlist) NPF_PRINTF_ATTR(3, 0); + +#ifdef __cplusplus +} +#endif + +#endif // NANOPRINTF_H_INCLUDED + +/* The implementation of nanoprintf begins here, to be compiled only if + NANOPRINTF_IMPLEMENTATION is defined. In a multi-file library what follows would + be nanoprintf.c. */ + +#ifdef NANOPRINTF_IMPLEMENTATION + +#ifndef NANOPRINTF_IMPLEMENTATION_INCLUDED +#define NANOPRINTF_IMPLEMENTATION_INCLUDED + +#include +#include + +// Pick reasonable defaults if nothing's been configured. +#if !defined(NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS) && \ + !defined(NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS) + #define NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS 1 + #define NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS 0 + #define NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS 0 +#endif + +// If anything's been configured, everything must be configured. +#ifndef NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif +#ifndef NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS + #error NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS must be #defined to 0 or 1 +#endif + +// Ensure flags are compatible. +#if (NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1) && \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 0) + #error Precision format specifiers must be enabled if float support is enabled. +#endif + +#if defined(NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW) && \ + defined(NANOPRINTF_SNPRINTF_SAFE_TRIM_STRING_ON_OVERFLOW) + #error snprintf safety flags are mutually exclusive. +#endif + +// intmax_t / uintmax_t require stdint from c99 / c++11 +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #ifndef _MSC_VER + #ifdef __cplusplus + #if __cplusplus < 201103L + #error large format specifier support requires C++11 or later. + #endif + #else + #if __STDC_VERSION__ < 199409L + #error nanoprintf requires C99 or later. + #endif + #endif + #endif +#endif + +// Figure out if we can disable warnings with pragmas. +#ifdef __clang__ + #define NANOPRINTF_CLANG 1 + #define NANOPRINTF_GCC_PAST_4_6 0 +#else + #define NANOPRINTF_CLANG 0 + #if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 6))) + #define NANOPRINTF_GCC_PAST_4_6 1 + #else + #define NANOPRINTF_GCC_PAST_4_6 0 + #endif +#endif + +#if NANOPRINTF_CLANG || NANOPRINTF_GCC_PAST_4_6 + #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 1 +#else + #define NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS 0 +#endif + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" + #ifdef __cplusplus + #pragma GCC diagnostic ignored "-Wold-style-cast" + #endif + #pragma GCC diagnostic ignored "-Wpadded" + #pragma GCC diagnostic ignored "-Wfloat-equal" + #if NANOPRINTF_CLANG + #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" + #pragma GCC diagnostic ignored "-Wcovered-switch-default" + #elif NANOPRINTF_GCC_PAST_4_6 + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + #endif +#endif + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4514) // unreferenced inline function removed + #pragma warning(disable:4505) // unreferenced function removed + #pragma warning(disable:4820) // padding after data member + #pragma warning(disable:5039) // extern "C" throw + #pragma warning(disable:5045) // spectre mitigation + #pragma warning(disable:4701) // possibly uninitialized + #pragma warning(disable:4706) // assignment in conditional + #pragma warning(disable:4710) // not inlined + #pragma warning(disable:4711) // selected for inline +#endif + +#if (NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1) || \ + (NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1) +typedef enum { + NPF_FMT_SPEC_OPT_NONE, + NPF_FMT_SPEC_OPT_LITERAL, + NPF_FMT_SPEC_OPT_STAR, +} npf_fmt_spec_opt_t; +#endif + +typedef enum { + NPF_FMT_SPEC_LEN_MOD_NONE, + NPF_FMT_SPEC_LEN_MOD_SHORT, // 'h' + NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE, // 'L' + NPF_FMT_SPEC_LEN_MOD_CHAR, // 'hh' + NPF_FMT_SPEC_LEN_MOD_LONG // 'l' +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + , + NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG, // 'll' + NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX, // 'j' + NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET, // 'z' + NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT // 't' +#endif +} npf_format_spec_length_modifier_t; + +typedef enum { + NPF_FMT_SPEC_CONV_PERCENT, // '%' + NPF_FMT_SPEC_CONV_CHAR, // 'c' + NPF_FMT_SPEC_CONV_STRING, // 's' + NPF_FMT_SPEC_CONV_SIGNED_INT, // 'i', 'd' +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + NPF_FMT_SPEC_CONV_BINARY, // 'b' +#endif + NPF_FMT_SPEC_CONV_OCTAL, // 'o' + NPF_FMT_SPEC_CONV_HEX_INT, // 'x', 'X' + NPF_FMT_SPEC_CONV_UNSIGNED_INT, // 'u' + NPF_FMT_SPEC_CONV_POINTER // 'p' +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + , NPF_FMT_SPEC_CONV_WRITEBACK // 'n' +#endif +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + , NPF_FMT_SPEC_CONV_FLOAT_DECIMAL // 'f', 'F' +#endif +} npf_format_spec_conversion_t; + +typedef struct { + char prepend; // ' ' or '+' + char alt_form; // '#' + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + npf_fmt_spec_opt_t field_width_opt; + int field_width; + char left_justified; // '-' + char leading_zero_pad; // '0' +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + npf_fmt_spec_opt_t prec_opt; + int prec; +#endif + + npf_format_spec_length_modifier_t length_modifier; + npf_format_spec_conversion_t conv_spec; + char case_adjust; +} npf_format_spec_t; + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 + typedef long npf_int_t; + typedef unsigned long npf_uint_t; +#else + typedef intmax_t npf_int_t; + typedef uintmax_t npf_uint_t; +#endif + +typedef struct { + char *dst; + size_t len; + size_t cur; +} npf_bufputc_ctx_t; + +static int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec); +static void npf_bufputc(int c, void *ctx); +static void npf_bufputc_nop(int c, void *ctx); +static int npf_itoa_rev(char *buf, npf_int_t i); +static int npf_utoa_rev(char *buf, npf_uint_t i, unsigned base, unsigned case_adjust); + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 +static int npf_fsplit_abs(float f, + uint64_t *out_int_part, + uint64_t *out_frac_part, + int *out_frac_base10_neg_e); +static int npf_ftoa_rev(char *buf, float f, char case_adj, int *out_frac_chars); +#endif + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 +static int npf_bin_len(npf_uint_t i); +#endif + +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #ifdef _MSC_VER + #include + typedef SSIZE_T ssize_t; + #else + #include + #endif +#endif + +#ifdef _MSC_VER + #include +#endif + +static int npf_min(int x, int y) { return (x < y) ? x : y; } +static int npf_max(int x, int y) { return (x > y) ? x : y; } + +int npf_parse_format_spec(char const *format, npf_format_spec_t *out_spec) { + char const *cur = format; + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->left_justified = 0; + out_spec->leading_zero_pad = 0; +#endif + out_spec->case_adjust = 'a'-'A'; // lowercase + out_spec->prepend = 0; + out_spec->alt_form = 0; + + while (*++cur) { // cur points at the leading '%' character + switch (*cur) { // Optional flags +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + case '-': + out_spec->left_justified = '-'; + out_spec->leading_zero_pad = 0; + continue; + case '0': + out_spec->leading_zero_pad = !out_spec->left_justified; + continue; +#endif + case '+': + out_spec->prepend = '+'; + continue; + case ' ': + if (out_spec->prepend == 0) { out_spec->prepend = ' '; } + continue; + case '#': + out_spec->alt_form = '#'; + continue; + default: break; + } + break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '*') { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + out_spec->field_width = 0; + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; + out_spec->field_width = (out_spec->field_width * 10) + (*cur++ - '0'); + } + } +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec = 0; + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; + if (*cur == '.') { + ++cur; + if (*cur == '*') { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_STAR; + ++cur; + } else { + if (*cur == '-') { + ++cur; + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; + } else { + out_spec->prec_opt = NPF_FMT_SPEC_OPT_LITERAL; + } + while ((*cur >= '0') && (*cur <= '9')) { + out_spec->prec = (out_spec->prec * 10) + (*cur++ - '0'); + } + } + } +#endif + + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_NONE; + switch (*cur++) { // Length modifier + case 'h': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_SHORT; + if (*cur == 'h') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_CHAR; + ++cur; + } + break; + case 'l': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG; +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + if (*cur == 'l') { + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_LONG_LONG; + ++cur; + } +#endif + break; +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'L': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE; + break; +#endif +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + case 'j': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_INTMAX; + break; + case 'z': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_SIZET; + break; + case 't': + out_spec->length_modifier = NPF_FMT_SPEC_LEN_MOD_LARGE_PTRDIFFT; + break; +#endif + default: --cur; break; + } + + switch (*cur++) { // Conversion specifier + case '%': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_PERCENT; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + case 'c': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_CHAR; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + case 's': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_STRING; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + out_spec->leading_zero_pad = 0; +#endif + break; + + case 'i': + case 'd': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_SIGNED_INT; + break; + + case 'o': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_OCTAL; + break; + case 'u': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_UNSIGNED_INT; + break; + + case 'X': + out_spec->case_adjust = 0; + case 'x': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_HEX_INT; + break; + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case 'F': + out_spec->case_adjust = 0; + case 'f': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_FLOAT_DECIMAL; + if (out_spec->prec_opt == NPF_FMT_SPEC_OPT_NONE) { out_spec->prec = 6; } + break; +#endif + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case 'n': + // todo: reject string if flags or width or precision exist + out_spec->conv_spec = NPF_FMT_SPEC_CONV_WRITEBACK; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; +#endif + + case 'p': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_POINTER; +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + out_spec->prec_opt = NPF_FMT_SPEC_OPT_NONE; +#endif + break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case 'B': + out_spec->case_adjust = 0; + case 'b': + out_spec->conv_spec = NPF_FMT_SPEC_CONV_BINARY; + break; +#endif + + default: return 0; + } + + return (int)(cur - format); +} + +int npf_itoa_rev(char *buf, npf_int_t i) { + int n = 0; + int const sign = (i >= 0) ? 1 : -1; + do { *buf++ = (char)('0' + (sign * (i % 10))); i /= 10; ++n; } while (i); + return n; +} + +int npf_utoa_rev(char *buf, npf_uint_t i, unsigned base, unsigned case_adj) { + int n = 0; + do { + unsigned const d = (unsigned)(i % base); + *buf++ = (char)((d < 10) ? ('0' + d) : ('A' + case_adj + (d - 10))); + i /= base; + ++n; + } while (i); + return n; +} + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 +enum { + NPF_MANTISSA_BITS = 23, + NPF_EXPONENT_BITS = 8, + NPF_EXPONENT_BIAS = 127, + NPF_FRACTION_BIN_DIGITS = 64, + NPF_MAX_FRACTION_DEC_DIGITS = 8 +}; + +int npf_fsplit_abs(float f, uint64_t *out_int_part, uint64_t *out_frac_part, + int *out_frac_base10_neg_exp) { + /* conversion algorithm by Wojciech Muła (zdjęcia@garnek.pl) + http://0x80.pl/notesen/2015-12-29-float-to-string.html + grisu2 (https://bit.ly/2JgMggX) and ryu (https://bit.ly/2RLXSg0) + are fast + precise + round, but require large lookup tables. */ + + uint32_t f_bits; { // union-cast is UB, let compiler optimize byte-copy loop. + char const *src = (char const *)&f; + char *dst = (char *)&f_bits; + for (unsigned i = 0; i < sizeof(f_bits); ++i) { dst[i] = src[i]; } + } + + int const exponent = + ((int)((f_bits >> NPF_MANTISSA_BITS) & ((1u << NPF_EXPONENT_BITS) - 1u)) - + NPF_EXPONENT_BIAS) - NPF_MANTISSA_BITS; + + if (exponent >= (64 - NPF_MANTISSA_BITS)) { return 0; } // value is out of range + + uint32_t const implicit_one = 1u << NPF_MANTISSA_BITS; + uint32_t const mantissa = f_bits & (implicit_one - 1); + uint32_t const mantissa_norm = mantissa | implicit_one; + + if (exponent > 0) { + *out_int_part = (uint64_t)mantissa_norm << exponent; + } else if (exponent < 0) { + if (-exponent > NPF_MANTISSA_BITS) { + *out_int_part = 0; + } else { + *out_int_part = mantissa_norm >> -exponent; + } + } else { + *out_int_part = mantissa_norm; + } + + uint64_t frac; { + int const shift = NPF_FRACTION_BIN_DIGITS + exponent - 4; + if ((shift >= (NPF_FRACTION_BIN_DIGITS - 4)) || (shift < 0)) { + frac = 0; + } else { + frac = ((uint64_t)mantissa_norm) << shift; + } + // multiply off the leading one's digit + frac &= 0x0fffffffffffffffllu; + frac *= 10; + } + + { // Count the number of 0s at the beginning of the fractional part. + int frac_base10_neg_exp = 0; + while (frac && ((frac >> (NPF_FRACTION_BIN_DIGITS - 4))) == 0) { + ++frac_base10_neg_exp; + frac &= 0x0fffffffffffffffllu; + frac *= 10; + } + *out_frac_base10_neg_exp = frac_base10_neg_exp; + } + + { // Convert the fractional part to base 10. + unsigned frac_part = 0; + for (int i = 0; frac && (i < NPF_MAX_FRACTION_DEC_DIGITS); ++i) { + frac_part *= 10; + frac_part += (unsigned)(frac >> (NPF_FRACTION_BIN_DIGITS - 4)); + frac &= 0x0fffffffffffffffllu; + frac *= 10; + } + *out_frac_part = frac_part; + } + return 1; +} + +int npf_ftoa_rev(char *buf, float f, char case_adj, int *out_frac_chars) { + uint32_t f_bits; { // union-cast is UB, let compiler optimize byte-copy loop. + char const *src = (char const *)&f; + char *dst = (char *)&f_bits; + for (unsigned i = 0; i < sizeof(f_bits); ++i) { dst[i] = src[i]; } + } + + if ((uint8_t)(f_bits >> 23) == 0xFF) { + if (f_bits & 0x7fffff) { + for (int i = 0; i < 3; ++i) { *buf++ = (char)("NAN"[i] + case_adj); } + } else { + for (int i = 0; i < 3; ++i) { *buf++ = (char)("FNI"[i] + case_adj); } + } + return -3; + } + + uint64_t int_part, frac_part; + int frac_base10_neg_exp; + if (npf_fsplit_abs(f, &int_part, &frac_part, &frac_base10_neg_exp) == 0) { + for (int i = 0; i < 3; ++i) { *buf++ = (char)("ROO"[i] + case_adj); } + return -3; + } + + char *dst = buf; + + while (frac_part) { // write the fractional digits + *dst++ = (char)('0' + (frac_part % 10)); + frac_part /= 10; + } + + // write the 0 digits between the . and the first fractional digit + while (frac_base10_neg_exp-- > 0) { *dst++ = '0'; } + *out_frac_chars = (int)(dst - buf); + *dst++ = '.'; + + // write the integer digits + do { *dst++ = (char)('0' + (int_part % 10)); int_part /= 10; } while (int_part); + return (int)(dst - buf); +} + +#endif // NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 +int npf_bin_len(npf_uint_t u) { + // Return the length of the binary string format of 'u', preferring intrinsics. + if (!u) { return 1; } + +#ifdef _MSC_VER // Win64, use _BSR64 for everything. If x86, use _BSR when non-large. + #ifdef _M_X64 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse64 + #elif NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 0 + #define NPF_HAVE_BUILTIN_CLZ + #define NPF_CLZ _BitScanReverse + #endif + #ifdef NPF_HAVE_BUILTIN_CLZ + unsigned long idx; + NPF_CLZ(&idx, u); + return (int)(idx + 1); + #endif +#elif defined(NANOPRINTF_CLANG) || defined(NANOPRINTF_GCC_PAST_4_6) + #define NPF_HAVE_BUILTIN_CLZ + #if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + #define NPF_CLZ(X) ((sizeof(long long) * 8) - (size_t)__builtin_clzll(X)) + #else + #define NPF_CLZ(X) ((sizeof(long) * 8) - (size_t)__builtin_clzl(X)) + #endif + return (int)NPF_CLZ(u); +#endif + +#ifndef NPF_HAVE_BUILTIN_CLZ + int n; + for (n = 0; u; ++n, u >>= 1); // slow but small software fallback + return n; +#else + #undef NPF_HAVE_BUILTIN_CLZ + #undef NPF_CLZ +#endif +} +#endif + +void npf_bufputc(int c, void *ctx) { + npf_bufputc_ctx_t *bpc = (npf_bufputc_ctx_t *)ctx; + if (bpc->cur < bpc->len) { bpc->dst[bpc->cur++] = (char)c; } +} + +void npf_bufputc_nop(int c, void *ctx) { (void)c; (void)ctx; } + +typedef struct npf_cnt_putc_ctx { + npf_putc pc; + void *ctx; + int n; +} npf_cnt_putc_ctx_t; + +static void npf_putc_cnt(int c, void *ctx) { + npf_cnt_putc_ctx_t *pc_cnt = (npf_cnt_putc_ctx_t *)ctx; + ++pc_cnt->n; + pc_cnt->pc(c, pc_cnt->ctx); // sibling-call optimization +} + +#define NPF_PUTC(VAL) do { npf_putc_cnt((int)(VAL), &pc_cnt); } while (0) + +#define NPF_EXTRACT(MOD, CAST_TO, EXTRACT_AS) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: val = (CAST_TO)va_arg(args, EXTRACT_AS); break + +#define NPF_WRITEBACK(MOD, TYPE) \ + case NPF_FMT_SPEC_LEN_MOD_##MOD: *(va_arg(args, TYPE *)) = (TYPE)pc_cnt.n; break + +int npf_vpprintf(npf_putc pc, void *pc_ctx, char const *format, va_list args) { + npf_format_spec_t fs; + char const *cur = format; + npf_cnt_putc_ctx_t pc_cnt; + pc_cnt.pc = pc; + pc_cnt.ctx = pc_ctx; + pc_cnt.n = 0; + + while (*cur) { + int const fs_len = (*cur != '%') ? 0 : npf_parse_format_spec(cur, &fs); + if (!fs_len) { NPF_PUTC(*cur++); continue; } + cur += fs_len; + + // Extract star-args immediately +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.field_width_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.field_width_opt = NPF_FMT_SPEC_OPT_LITERAL; + fs.field_width = va_arg(args, int); + if (fs.field_width < 0) { + fs.field_width = -fs.field_width; + fs.left_justified = 1; + } + } +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.prec_opt == NPF_FMT_SPEC_OPT_STAR) { + fs.prec_opt = NPF_FMT_SPEC_OPT_NONE; + fs.prec = va_arg(args, int); + if (fs.prec >= 0) { fs.prec_opt = NPF_FMT_SPEC_OPT_LITERAL; } + } +#endif + + union { char cbuf_mem[32]; npf_uint_t binval; } u; + char *cbuf = u.cbuf_mem, sign_c = 0; + int cbuf_len = 0, need_0x = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int field_pad = 0; + char pad_c = 0; +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + int prec_pad = 0; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + int zero = 0; +#endif +#endif +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + int frac_chars = 0, inf_or_nan = 0; +#endif + + // Extract and convert the argument to string, point cbuf at the text. + switch (fs.conv_spec) { + case NPF_FMT_SPEC_CONV_PERCENT: + *cbuf = '%'; + ++cbuf_len; + break; + + case NPF_FMT_SPEC_CONV_CHAR: + *cbuf = (char)va_arg(args, int); + ++cbuf_len; + break; + + case NPF_FMT_SPEC_CONV_STRING: { + cbuf = va_arg(args, char *); + for (char const *s = cbuf; *s; ++s, ++cbuf_len); // strlen +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if (fs.prec_opt == NPF_FMT_SPEC_OPT_LITERAL) { + cbuf_len = npf_min(fs.prec, cbuf_len); // prec truncates strings + } +#endif + } break; + + case NPF_FMT_SPEC_CONV_SIGNED_INT: { + npf_int_t val = 0; + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, int, int); + NPF_EXTRACT(SHORT, short, int); + NPF_EXTRACT(LONG_DOUBLE, int, int); + NPF_EXTRACT(CHAR, char, int); + NPF_EXTRACT(LONG, long, long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, long long, long long); + NPF_EXTRACT(LARGE_INTMAX, intmax_t, intmax_t); + NPF_EXTRACT(LARGE_SIZET, ssize_t, ssize_t); + NPF_EXTRACT(LARGE_PTRDIFFT, ptrdiff_t, ptrdiff_t); +#endif + default: break; + } + + sign_c = (val < 0) ? '-' : fs.prepend; + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + // special case, if prec and value are 0, skip + if (!val && (fs.prec_opt == NPF_FMT_SPEC_OPT_LITERAL) && !fs.prec) { + cbuf_len = 0; + } else +#endif + { cbuf_len = npf_itoa_rev(cbuf, val); } + } break; + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_BINARY: +#endif + case NPF_FMT_SPEC_CONV_OCTAL: + case NPF_FMT_SPEC_CONV_HEX_INT: + case NPF_FMT_SPEC_CONV_UNSIGNED_INT: { + npf_uint_t val = 0; + + switch (fs.length_modifier) { + NPF_EXTRACT(NONE, unsigned, unsigned); + NPF_EXTRACT(SHORT, unsigned short, unsigned); + NPF_EXTRACT(LONG_DOUBLE, unsigned, unsigned); + NPF_EXTRACT(CHAR, unsigned char, unsigned); + NPF_EXTRACT(LONG, unsigned long, unsigned long); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_EXTRACT(LARGE_LONG_LONG, unsigned long long, unsigned long long); + NPF_EXTRACT(LARGE_INTMAX, uintmax_t, uintmax_t); + NPF_EXTRACT(LARGE_SIZET, size_t, size_t); + NPF_EXTRACT(LARGE_PTRDIFFT, size_t, size_t); +#endif + default: break; + } + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = !val; +#endif + if (!val && (fs.prec_opt == NPF_FMT_SPEC_OPT_LITERAL) && !fs.prec) { + // Zero value and explicitly-requested zero precision means "print nothing". + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) && fs.alt_form) { + fs.prec = 1; // octal special case, print a single '0' + } + } else +#endif +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + cbuf_len = npf_bin_len(val); u.binval = val; + } else +#endif + { + unsigned const base = (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL) ? + 8u : ((fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) ? 16u : 10u); + cbuf_len = npf_utoa_rev(cbuf, val, base, (unsigned)fs.case_adjust); + } + + if (val && fs.alt_form && (fs.conv_spec == NPF_FMT_SPEC_CONV_OCTAL)) { + cbuf[cbuf_len++] = '0'; // OK to add leading octal '0' immediately. + } + + if (val && fs.alt_form) { // 0x or 0b but can't write it yet. + if (fs.conv_spec == NPF_FMT_SPEC_CONV_HEX_INT) { need_0x = 'X'; } +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + else if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { need_0x = 'B'; } +#endif + if (need_0x) { need_0x += fs.case_adjust; } + } + } break; + + case NPF_FMT_SPEC_CONV_POINTER: { + cbuf_len = + npf_utoa_rev(cbuf, (npf_uint_t)(uintptr_t)va_arg(args, void *), 16, 'a'-'A'); + need_0x = 'x'; + } break; + +#if NANOPRINTF_USE_WRITEBACK_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_WRITEBACK: + switch (fs.length_modifier) { + NPF_WRITEBACK(NONE, int); + NPF_WRITEBACK(SHORT, short); + NPF_WRITEBACK(LONG, long); + NPF_WRITEBACK(LONG_DOUBLE, double); + NPF_WRITEBACK(CHAR, signed char); +#if NANOPRINTF_USE_LARGE_FORMAT_SPECIFIERS == 1 + NPF_WRITEBACK(LARGE_LONG_LONG, long long); + NPF_WRITEBACK(LARGE_INTMAX, intmax_t); + NPF_WRITEBACK(LARGE_SIZET, size_t); + NPF_WRITEBACK(LARGE_PTRDIFFT, ptrdiff_t); +#endif + default: break; + } break; +#endif + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + case NPF_FMT_SPEC_CONV_FLOAT_DECIMAL: { + float val; + if (fs.length_modifier == NPF_FMT_SPEC_LEN_MOD_LONG_DOUBLE) { + val = (float)va_arg(args, long double); + } else { + val = (float)va_arg(args, double); + } + + sign_c = (val < 0.f) ? '-' : fs.prepend; +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + zero = (val == 0.f); +#endif + cbuf_len = npf_ftoa_rev(cbuf, val, fs.case_adjust, &frac_chars); + + if (cbuf_len < 0) { + cbuf_len = -cbuf_len; + inf_or_nan = 1; + } else { + int const prec_adj = npf_max(0, frac_chars - fs.prec); + cbuf += prec_adj; + cbuf_len -= prec_adj; + } + } break; +#endif + default: break; + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Compute the field width pad character + if (fs.field_width_opt == NPF_FMT_SPEC_OPT_LITERAL) { + if (fs.leading_zero_pad) { // '0' flag is only legal with numeric types + if ((fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_CHAR) && + (fs.conv_spec != NPF_FMT_SPEC_CONV_PERCENT)) { +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + if ((fs.prec_opt == NPF_FMT_SPEC_OPT_LITERAL) && !fs.prec && zero) { + pad_c = ' '; + } else +#endif + { pad_c = '0'; } + } + } else { pad_c = ' '; } + } +#endif + + // Compute the number of bytes to truncate or '0'-pad. + if (fs.conv_spec != NPF_FMT_SPEC_CONV_STRING) { +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + if (!inf_or_nan) { // float precision is after the decimal point + int const prec_start = + (fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_DECIMAL) ? frac_chars : cbuf_len; + prec_pad = npf_max(0, fs.prec - prec_start); + } +#elif NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + prec_pad = npf_max(0, fs.prec - cbuf_len); +#endif + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Given the full converted length, how many pad bytes? + field_pad = fs.field_width - cbuf_len - !!sign_c; + if (need_0x) { field_pad -= 2; } + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_DECIMAL) && !fs.prec && !fs.alt_form) { + ++field_pad; // 0-pad, no decimal point. + } +#endif +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + field_pad -= prec_pad; +#endif + field_pad = npf_max(0, field_pad); +#endif // NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + // Apply right-justified field width if requested + if (!fs.left_justified && pad_c) { // If leading zeros pad, sign goes first. + if (pad_c == '0') { + if (sign_c) { NPF_PUTC(sign_c); sign_c = 0; } + // Pad byte is '0', write '0x' before '0' pad chars. + if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } + } + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + // Pad byte is ' ', write '0x' after ' ' pad chars but before number. + if ((pad_c != '0') && need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } + } else +#endif + { if (need_0x) { NPF_PUTC('0'); NPF_PUTC(need_0x); } } // no pad, '0x' requested. + + // Write the converted payload + if (fs.conv_spec == NPF_FMT_SPEC_CONV_STRING) { + for (int i = 0; i < cbuf_len; ++i) { NPF_PUTC(cbuf[i]); } + } else { + if (sign_c) { NPF_PUTC(sign_c); } +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec != NPF_FMT_SPEC_CONV_FLOAT_DECIMAL) { +#endif + +#if NANOPRINTF_USE_PRECISION_FORMAT_SPECIFIERS == 1 + while (prec_pad-- > 0) { NPF_PUTC('0'); } // int precision leads. +#endif + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + } else { + // if 0 precision, skip the fractional part and '.' + // if 0 prec + alternative form, keep the '.' + if (!fs.prec && !fs.alt_form) { ++cbuf; --cbuf_len; } + } +#endif + +#if NANOPRINTF_USE_BINARY_FORMAT_SPECIFIERS == 1 + if (fs.conv_spec == NPF_FMT_SPEC_CONV_BINARY) { + while (cbuf_len) { NPF_PUTC('0' + ((u.binval >> --cbuf_len) & 1)); } + } else +#endif + { while (cbuf_len-- > 0) { NPF_PUTC(cbuf[cbuf_len]); } } // payload is reversed + +#if NANOPRINTF_USE_FLOAT_FORMAT_SPECIFIERS == 1 + // real precision comes after the number. + if ((fs.conv_spec == NPF_FMT_SPEC_CONV_FLOAT_DECIMAL) && !inf_or_nan) { + while (prec_pad-- > 0) { NPF_PUTC('0'); } + } +#endif + } + +#if NANOPRINTF_USE_FIELD_WIDTH_FORMAT_SPECIFIERS == 1 + if (fs.left_justified && pad_c) { // Apply left-justified field width + while (field_pad-- > 0) { NPF_PUTC(pad_c); } + } +#endif + } + + return pc_cnt.n; +} + +#undef NPF_PUTC +#undef NPF_EXTRACT +#undef NPF_WRITEBACK + +int npf_pprintf(npf_putc pc, void *pc_ctx, char const *format, ...) { + va_list val; + va_start(val, format); + int const rv = npf_vpprintf(pc, pc_ctx, format, val); + va_end(val); + return rv; +} + +int npf_snprintf(char *buffer, size_t bufsz, const char *format, ...) { + va_list val; + va_start(val, format); + int const rv = npf_vsnprintf(buffer, bufsz, format, val); + va_end(val); + return rv; +} + +int npf_vsnprintf(char *buffer, size_t bufsz, char const *format, va_list vlist) { + npf_bufputc_ctx_t bufputc_ctx; + bufputc_ctx.dst = buffer; + bufputc_ctx.len = bufsz; + bufputc_ctx.cur = 0; + + npf_putc const pc = buffer ? npf_bufputc : npf_bufputc_nop; + int const n = npf_vpprintf(pc, &bufputc_ctx, format, vlist); + pc('\0', &bufputc_ctx); + +#ifdef NANOPRINTF_SNPRINTF_SAFE_EMPTY_STRING_ON_OVERFLOW + if (bufsz && (n >= (int)bufsz)) { buffer[0] = '\0'; } +#elif defined(NANOPRINTF_SNPRINTF_SAFE_TRIM_STRING_ON_OVERFLOW) + if (bufsz && (n >= (int)bufsz)) { buffer[bufsz - 1] = '\0'; } +#endif + + return n; +} + +#if NANOPRINTF_HAVE_GCC_WARNING_PRAGMAS + #pragma GCC diagnostic pop +#endif + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif // NANOPRINTF_IMPLEMENTATION_INCLUDED +#endif // NANOPRINTF_IMPLEMENTATION + diff --git a/includes/pcsx2/pcsx2f_api.h b/includes/pcsx2/pcsx2f_api.h new file mode 100644 index 0000000..e29f650 --- /dev/null +++ b/includes/pcsx2/pcsx2f_api.h @@ -0,0 +1,124 @@ +#ifndef PCSX2F_API_H_INCLUDED +#define PCSX2F_API_H_INCLUDED +#include + +struct PluginInfo +{ + uint32_t Base; + uint32_t EntryPoint; + uint32_t SegmentFileOffset; + uint32_t Size; + uint32_t PluginDataAddr; + uint32_t PluginDataSize; + uint32_t PCSX2DataAddr; + uint32_t PCSX2DataSize; + uint32_t CompatibleCRCListAddr; + uint32_t CompatibleCRCListSize; + uint32_t CompatibleElfCRCListAddr; + uint32_t CompatibleElfCRCListSize; + uint32_t KeyboardStateAddr; + uint32_t KeyboardStateSize; + uint32_t MouseStateAddr; + uint32_t MouseStateSize; + uint32_t CheatStringAddr; + uint32_t CheatStringSize; + uint32_t OSDTextAddr; + uint32_t OSDTextSize; + uint32_t FrameLimitUnthrottleAddr; + uint32_t FrameLimitUnthrottleSize; + uint32_t CLEOScriptsAddr; + uint32_t CLEOScriptsSize; + + uint32_t ps2sdk_libc_init; + uint32_t ps2sdk_libcpp_init; + uint32_t __cxa_atexit; + +#ifdef __cplusplus + bool isValid() { return (Base != 0 && EntryPoint != 0 && Size != 0); } +#endif +}; + +enum PCSX2DataType +{ + PCSX2Data_DesktopSizeX, + PCSX2Data_DesktopSizeY, + PCSX2Data_WindowSizeX, + PCSX2Data_WindowSizeY, + PCSX2Data_IsFullscreen, + PCSX2Data_AspectRatioSetting, + + PCSX2Data_Size +}; + +enum AspectRatioType +{ + Stretch, + RAuto4_3_3_2, + R4_3, + R16_9, + MaxCount +}; + +enum KeyboardBufState +{ + CurrentState, + PreviousState, + + StateNum, + + CheatStringLen = 25, + StateSize = 256 +}; + +struct CMouseControllerState +{ + int8_t lmb; + int8_t rmb; + int8_t mmb; + int8_t wheelUp; + int8_t wheelDown; + int8_t bmx1; + int8_t bmx2; + float Z; + float X; + float Y; +}; + +enum OSDString +{ + OSDStringNum = 10, + OSDStringSize = 255 +}; + +enum PtrType +{ + KeyboardData, + MouseData, + CheatStringData +}; + +enum +{ + MaxIniSize = 5000 +}; + +enum +{ + PluginsMaxNum = 100 +}; + +extern int CompatibleCRCList[]; +extern char ElfPattern[]; +#ifdef PLUGIN_INVOKER +struct PluginInfo PluginData[PluginsMaxNum]; +#else +extern char PluginData[MaxIniSize]; +#endif +extern int PCSX2Data[PCSX2Data_Size]; +extern char KeyboardState[StateNum][StateSize]; +extern struct CMouseControllerState MouseState[StateNum]; +extern char CheatString[CheatStringLen]; +extern char OSDText[OSDStringNum][OSDStringSize]; +extern char FrameLimitUnthrottle; + +#endif diff --git a/license b/license new file mode 100644 index 0000000..66fa51a --- /dev/null +++ b/license @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2024, Alexander Blade +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of CLEO, CLEO Android nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/makefile.lua b/makefile.lua new file mode 100644 index 0000000..3864fdf --- /dev/null +++ b/makefile.lua @@ -0,0 +1,139 @@ +function writemakefile_ps2(prj_name, scripts_addr, base, libs, ...) + local args = {...} + local files = "main.o"; + for i, v in ipairs( args ) do + files = files .. " " .. v:match("(.+)%..+") .. ".o" + end + files = string.gsub(files, "^%s*(.-)%s*$", "%1") + file = io.open("source/makefile", "w") + if (file) then +str = [[ +EE_BIN = ../data/%s.elf +EE_OBJS = %s + +CFLAGS = -O0 -Os -G0 -Wall -fshort-wchar -fno-pic -mno-check-zero-division -fpack-struct=16 +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +BASE_ADDRESS = %s +EE_LINKFILE = linkfile +EE_LIBS += %s +EE_LDFLAGS = -Wl,--entry=init -Wl,--no-relax -Wl,-Map,../data/%s.map -Wl,'--defsym=BASE_ADDRESS=$(BASE_ADDRESS)' + +all: clean main-build + +main-build: $(EE_BIN) + +clean: + rm -f $(EE_OBJS) $(EE_BIN) + +PS2SDK = ../external/ps2sdk/ps2sdk +include $(PS2SDK)/samples/Makefile.pref +include $(PS2SDK)/samples/Makefile.eeglobal +]] + file:write(string.format(str, scripts_addr .. prj_name, files, base, libs, scripts_addr .. prj_name)) + file:close() + end +end + +function writelinkfile_ps2() + file = io.open("source/linkfile", "w") + if (file) then +str = [[ +ENTRY(__start); + +PHDRS { + text PT_LOAD; +} + +SECTIONS { + .text BASE_ADDRESS : { + _ftext = . ; + *(.text) + *(.text.*) + *(.gnu.linkonce.t*) + KEEP(*(.init)) + KEEP(*(.fini)) + QUAD(0) + } :text + + PROVIDE(_etext = .); + PROVIDE(etext = .); + + .reginfo : { *(.reginfo) } + + /* Global/static constructors and deconstructors. */ + .ctors ALIGN(16): { + KEEP(*crtbegin*.o(.ctors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .ctors)) + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + } + .dtors ALIGN(16): { + KEEP(*crtbegin*.o(.dtors)) + KEEP(*(EXCLUDE_FILE(*crtend*.o) .dtors)) + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + } + + /* Static data. */ + .rodata ALIGN(128): { + *(.rodata) + *(.rodata.*) + *(.gnu.linkonce.r*) + } + + .data ALIGN(128): { + _fdata = . ; + *(.data) + *(.data.*) + *(.gnu.linkonce.d*) + SORT(CONSTRUCTORS) + } + + .rdata ALIGN(128): { *(.rdata) } + .gcc_except_table ALIGN(128): { *(.gcc_except_table) } + + _gp = ALIGN(128) + 0x7ff0; + .lit4 ALIGN(128): { *(.lit4) } + .lit8 ALIGN(128): { *(.lit8) } + + .sdata ALIGN(128): { + *(.sdata) + *(.sdata.*) + *(.gnu.linkonce.s*) + } + + _edata = .; + PROVIDE(edata = .); + + /* Uninitialized data. */ + .sbss ALIGN(128) : { + _fbss = . ; + *(.sbss) + *(.sbss.*) + *(.gnu.linkonce.sb*) + *(.scommon) + } + + .bss ALIGN(128) : { + *(.bss) + *(.bss.*) + *(.gnu.linkonce.b*) + *(COMMON) + } + _end_bss = .; + + _end = . ; + PROVIDE(end = .); + + /* Symbols needed by crt0.s. */ + PROVIDE(_heap_size = -1); + PROVIDE(_stack = -1); + PROVIDE(_stack_size = 128 * 1024); +} +]] + file:write(str) + file:close() + end +end \ No newline at end of file diff --git a/premake5.bat b/premake5.bat new file mode 100644 index 0000000..3b476e7 --- /dev/null +++ b/premake5.bat @@ -0,0 +1 @@ +premake5 vs2022 \ No newline at end of file diff --git a/premake5.exe b/premake5.exe new file mode 100644 index 0000000..c73da1f Binary files /dev/null and b/premake5.exe differ diff --git a/premake5.lua b/premake5.lua new file mode 100644 index 0000000..f2b2cb8 --- /dev/null +++ b/premake5.lua @@ -0,0 +1,78 @@ +workspace "GTALCS.GTAVCS.PCSX2F.CLEO" + configurations { "Release", "Debug" } + platforms { "Win64" } + architecture "x64" + location "build" + objdir ("build/obj") + buildlog ("build/log/%{prj.name}.log") + cppdialect "C++latest" + include "makefile.lua" + + kind "SharedLib" + language "C++" + targetdir "data/scripts" + targetextension ".asi" + characterset ("UNICODE") + staticruntime "On" + + files { "source/*.h" } + files { "source/*.cpp" } + files { "Resources/*.rc" } + includedirs { "source" } + + pbcommands = { + "setlocal EnableDelayedExpansion", + --"set \"path=" .. (gamepath) .. "\"", + "set file=$(TargetPath)", + "FOR %%i IN (\"%file%\") DO (", + "set filename=%%~ni", + "set fileextension=%%~xi", + "set target=!path!!filename!!fileextension!", + "if exist \"!target!\" copy /y \"%%~fi\" \"!target!\"", + ")" } + + function setbuildpaths_ps2(gamepath, exepath, scriptspath, ps2sdkpath, sourcepath, prj_name) + local pbcmd = {} + for k,v in pairs(pbcommands) do + pbcmd[k] = v + end + if (gamepath) then + cmdcopy = { "set \"path=" .. gamepath .. scriptspath .. "\"" } + pbcmd[2] = "set \"file=../data/" .. scriptspath .. prj_name ..".elf\"" + table.insert(cmdcopy, pbcmd) + buildcommands { "call " .. ps2sdkpath .. " -C " .. sourcepath, cmdcopy } + rebuildcommands { "call " .. ps2sdkpath .. " -C " .. sourcepath .. " clean && " .. ps2sdkpath .. " -C " .. sourcepath, cmdcopy } + cleancommands { "call " .. ps2sdkpath .. " -C " .. sourcepath .. " clean" } + debugdir (gamepath) + if (exepath) then + debugcommand (gamepath .. exepath) + dir, file = exepath:match'(.*/)(.*)' + debugdir (gamepath .. (dir or "")) + end + end + targetdir ("data/" .. scriptspath) + end + + function add_ps2sdk() + includedirs { "external/ps2sdk/ps2sdk/ee" } + files { "source/*.h", "source/*.c", "source/*.cpp", "source/makefile" } + end + + filter "configurations:Debug*" + defines "DEBUG" + symbols "On" + + filter "configurations:Release*" + defines "NDEBUG" + optimize "On" + + +project "GTALCS.GTAVCS.PCSX2F.CLEO" + kind "Makefile" + targetname "cleo" + add_ps2sdk() + targetextension ".elf" + setbuildpaths_ps2("Z:/GitHub/PCSX2-Fork-With-Plugins/bin/", "pcsx2-qtx64-clang.exe", "PLUGINS/", "%{wks.location}/../external/ps2sdk/ee/bin/vsmake", "%{wks.location}/../source/", "cleo") + writemakefile_ps2("cleo", "PLUGINS/", "0x05000000", "-l:libstdc++.a -l:libm.a", + "../includes/pcsx2/memalloc.o", "../includes/pcsx2/log.o", "utils.o", "text.o", "libres.o", "armhook.o", "touch.o", "ui.o", "pattern.o", "core.o", "mutex.o", "strutils.o", "memutils.o", "psplang.o") + writelinkfile_ps2() diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..b15776e --- /dev/null +++ b/readme.md @@ -0,0 +1,240 @@ +# CLEO PS2 + +Version: 2.0.2 + +Date: Oct 2024 + +# Description + +CLEO PS2 is a version of the top-most popular GTA PC version add-on - CLEO library, which enhances game's script engine with ability to load custom scripts. Use cheats, spawn cars and use lots of cool features. + +# Disclaimer and Limitation of Liability + +You are using this application at your own risk, you agree to take full responsibility for anything that this application can cause. You are not allowed to install the application or any of the application components if you don't agree with these terms. + +## Changes in 2.0.2 + +- the library has been ported to PS2, supports both LCS and VCS +- Menu opening behavior is changed, press [start] to open the main menu, press [start] again to open cleo menu, press [circle] to close main menu without opening cleo menu. +- Popup information is hidden, to allow vanilla game experience + +## Requirements + +- Download and extract [PCSX2 Fork With Plugins](https://github.com/ASI-Factory/PCSX2-Fork-With-Plugins/releases/tag/latest). +- Download and extract plugin archive to the root directory, where exe is located. +- Launch the game. + +## Supported game versions + +- LCS SLUS-21423 / SLES-54135 (crc: 4F32A11F / B3AD1EA4) +- VCS SLUS-21590 / SLES-54622 (crc: 7EA439F5 / D693D4CF) + +## Scripts + +CLEO PS2 is using *.csa and *.csi script extensions, *.csa scripts start just after the game loads and *.csi ones only when you directly invoke them via ingame script menu. To open the menu perform a slide from screen top to bottom thru center, arrow popup at start should give a tip, this one is related to the mobile game series only. In order to open the menu on PS2, press [start], and then [start] again, and you will see cleo menu. +In order to install scripts manually, put scripts in `PCSX2F/PLUGINS/cleo/%game%` where the `%game%` is either "lcs" or "vcs". + +# Developers + +## Scripts + +A lot of PC CLEO *.cs scripts must be manually rewritten before they can be used with CLEO PS2 since *.cs scripts use PC opcodes and PC oriented controls. CLEO PS2 is using *.csa and *.csi script extensions, which internally are the same as PC *.cs scripts, while *.csa scripts start when the game loads and *.csi when user directly invokes a script using the menu. + +The name which appears in the menu is a decorated version of an original script file name - game prefix gets cleaned out (e.g. "gtasa."), extension is removed too (".csi"), dots and underlines get replaced with spaces. For instance "gtasa.car_spawner.csi" will appear in the menu as "car spawner". + +You can compile and decompile CLEO PS2 scripts with Sanny Builder, in order to do so add the list of CLEO PS2 opcode declarations in the corresponding SCM.INI file: + +``` +; CLEO ANDROID (PSP) +0DD0=2,%1d% = get_label_addr %2p% +0DD1=2,%1d% = get_func_addr_by_cstr_name %2d% +0DD2=1,context_call_func %1d% ; deprecated, use 0DDE instead +0DD3=2,context_set_reg %1d% value %2d% ; deprecated, use 0DDE instead +0DD4=2,%1d% = context_get_reg %2d% ; deprecated, use 0DDE instead +0DD5=1,%1d% = get_platform +0DD6=1,%1d% = get_game_version +0DD7=1,%1d% = get_image_base +0DD8=4,%1d% = read_mem_addr %2d% size %3d% add_ib %4d% +0DD9=5,write_mem_addr %1d% value %2d% size %3d% add_ib %4d% protect %5d% +0DDA=3,%1d% = get_pattern_addr_cstr %2d% index %3d% +0DDB=3,get_game_ver_ex name_hash %1d% ver_hash %2d% ver_code %3d% +0DDC=2,set_mutex_var %1d% to %2d% +0DDD=2,%1d% = get_mutex_var %2d% +0DDE=-1, call_func %1d% add_ib %2d% +0DE0=3,%1d% = get_touch_point_state %2d% mintime %3d% +0DE1=5,%1d% = get_touch_slide_state from %2d% to %3d% mintime %4d% maxtime %5d% +0DE2=1,%1d% = get_menu_button_state +0DE3=2,%1d% = get_menu_button_pressed mintime %2d% +0DE4=2,%1d% = psp_get_control_state %2d% +0DE5=3,%1d% = psp_get_control_pressed %2d% mintime %3d% +0DF2=2,create_menu %1d% items %2d% +0DF3=0,delete_menu +0DF4=2,%1d% = get_menu_touched_item_index maxtime %2d% +0DF5=1,set_menu_active_item_index %1d% +0DF6=1,%1d% = get_menu_active_item_index +1000=-1, opcode_func +``` + +Example scripts compilation for LCS/VCS requires basic keyword definitions, if your current version of SB doesn't have these already defined then you have to define and add keywords.txt files manually: +- Open SB/data/modes.xml +- Find the mode with id="sa" and see how are defined there +- Now find the mode with id="lcs" +- Define the keywords as @sb:\data\lcs\keywords.txt +- Do the same for id="vcs_psp" using @sb:\data\vcs\keywords.txt +- Create required keywords.txt files and put the respective definitions there + +``` +; keywords.txt for lcs +0001=wait +0002=goto +0002=jump +004d=jf +004e=end_thread +0050=gosub +0051=return +00db=if +03a9=thread +``` + +``` +; keywords.txt for vcs +0001=wait +0002=goto +0002=jump +0022=jf +0023=end_thread +0025=gosub +0026=return +0078=if +0238=thread +``` + +## Opcodes explained + +`0DD0=2,%1d% = get_label_addr %2p%` + +`0DD1=2,%1d% = get_func_addr_by_cstr_name %2d%` + +These two are usually used together, first one gets real address of the target label where string or anything else is written and the second gets exported symbol name in the game's main library by C-style string name. It was done in the way like this (not using long strings) in order for opcode to be universal and also work with games which do not support long string type. When used with PS2 game series 0DD1 will return 0. + +`0DD2=1,context_call_func %1d%` + +`0DD3=2,context_set_reg %1d% value %2d%` + +`0DD4=2,%1d% = context_get_reg %2d%` + +Deprecated, use 0DDE instead. + +`0DD5=1,%1d% = get_platform` + +Returns current platform: Android = 1, PS2 = 2, primarely used for LCS scripts. + +`0DD6=1,%1d% = get_game_version` + +Returns internal game version, see sdk -> cleo.h -> eGameVerInternal for the details. + +`0DD7=1,%1d% = get_image_base` + +Returns image base of the main game library. + +`0DD8=4,%1d% = read_mem_addr %2d% size %3d% add_ib %4d%` + +`0DD9=5,write_mem_addr %1d% value %2d% size %3d% add_ib %4d% protect %5d%` + +Reads and writes memory, add_ib should be set only when raw address specified and it needs to be added to the imagebase in order to access needed memory location. + +`0DDA=3,%1d% = get_pattern_addr_cstr %2d% index %3d%` + +Searches for the specified byte pattern in main library, if there are few addresses which match this pattern the index is used, see example scripts for the details. Works on both Android and PS2, the search if performed within all executable sections. Max length for the pattern is 32 bytes. Returns 0 if no pattern was found. The thing to keep in mind while using this on PS2 - if you are searching for a function start bytes you have to mark first 4 as ?? since PPSSPP can override this for JIT purporses. + +`0DDB=3,get_game_ver_ex name_hash %1d% ver_hash %2d% ver_code %3d%` + +Returns current game version: +* name_hash is a jenkins hash of an Android package name, on PS2 not a hash while an integer part of a title id +* ver_hash is always a jenkins hash of version string, both platforms have this thing +* ver_code is a package version code on Android, on PS2 this is a WORD where lower byte is a minor version and higher is a major version of a title. + +`0DDC=2,set_mutex_var %1d% to %2d%` + +`0DDD=2,%1d% = get_mutex_var %2d%` + +Mutex vars are being used as global variables between scripts, mutex id can be any integer value, for example all scripts that use GUI use mutex variable with id 0 which indicates the state of anything being onscreen, if it's value is 0 then script can show GUI, if script is about to show GUI then it sets this var to 1. If mutex variable isn't set then 0 is returned. + +`0DDE=-1, call_func %1d% add_ib %2d%` + +Calls a game function using specified address, if add_ib is set to 1 then adds an imagebase. Pairs of ['type', value] follow and indicate with which result type and params this function must be called. Specifying result is optional, just as specifing params if function has none. +Type can be one of these: +'i' - integer param +'f' - float param +'ref' - passes a reference to a variable as param +'resi' - result of integer type +'resf' - result of float type +Let's look how this one works on a simple example: +0DDE: call_func 2@ add_ib 0 __result 'resi' 0@ __pad_index 'i' 0 +Here we are performing a call to a function which absolute address is stored in 2@, this is why we don't need to add an imagebase here, so add_ib is set to 0, this function returns integer which get's stored in 0@ and takes one integer param which is set to 0, can be a variable instead if required. + +`0DE0=3,%1d% = get_touch_point_state %2d% mintime %3d%` + +`0DE1=5,%1d% = get_touch_slide_state from %2d% to %3d% mintime %4d% maxtime %5d%` + +Returns result of the requested touch action check, can be 0 or 1. On PS2 always returns 0. Touch points is the CLEO PS2 engine implemented touch detection system which divides touchscreen into 9 parts called touch points, they can be used to detect timed slides and touches. + +Touch point ids: + +``` +#1 LEFT-TOP #4 CENTER-TOP #7 RIGHT-TOP +#2 LEFT-CENTER #5 CENTER #8 RIGHT-CENTER +#3 LEFT-BOTTOM #6 CENTER-BOTTOM #9 RIGHT-BOTTOM +``` + +`0DE2=1,%1d% = get_menu_button_state` + +`0DE3=2,%1d% = get_menu_button_pressed mintime %2d%` + +Returns state of the Android hardware menu button, can be 0 or 1, a lot of devices have no button like this. On PS2 always returns 0. + +`0DE4=2,%1d% = psp_get_control_state %2d%` + +`0DE5=3,%1d% = psp_get_control_pressed %2d% mintime %3d%` + +Returns state of the PS2 control, can be 0 or 1. On Android always returns 0. + +``` +enum ePspControl { + SELECT = 0, UP, RIGHT, DOWN, LEFT, LTRIGGER, RTRIGGER, + TRIANGLE, CIRCLE, CROSS, SQUARE, HOLD, + STICK_UP, STICK_DOWN, STICK_RIGHT, STICK_LEFT +} +``` + +`0DF2=2,create_menu %1d% items %2d%` + +Creates the menu gui which handles most of the things like drawing and input automatically. With first param being a label like @menu_desc where you describe the menu and second param indicating menu item count, can be set to a higher value than actual items if the menu has last item described as 00 byte. On Android the menu is touch based while on PS2 uses controls. To get the point see the example scripts. +Menu flags go as following: + +``` +enum eMenuFlags +{ + // item names are considered being gxt/fxt entries, if no such entry exists then + // item name is the name which will be displayed in the menu + USE_GXT = 1 << 0, + + // if gxt/fxt entry won't fit the menu bounds then text will be end-trimmed, + // sizes: III, VC, SA = 20, LCS = 18, VCS = 19 + TRIM_GXT = 1 << 1 +} +``` + +`0DF3=0,delete_menu` + +Removes active menu. + +`0DF4=2,%1d% = get_menu_touched_item_index maxtime %2d%` + +Returns 0-based menu touched item index if condition is met, on PS2 returns selected item index which is the same, if no item was touched or selected then the result is -1, if menu has to be closed then the result is -2. + +`0DF5=1,set_menu_active_item_index %1d%` + +`0DF6=1,%1d% = get_menu_active_item_index` + +Gets and sets active menu index with scrolling menu pages if necessary, on Android this involves only menu pages while on PS2 also sets last active item on the corresponding page. Mostly used in order to pass menu states between different launches of the same *.csi script which uses a menu. diff --git a/release.bat b/release.bat new file mode 100644 index 0000000..c74fbe3 --- /dev/null +++ b/release.bat @@ -0,0 +1 @@ +7z a -tzip ".\cleops2.zip" ".\data\*" -xr!*.map \ No newline at end of file diff --git a/source/armhook.cpp b/source/armhook.cpp new file mode 100644 index 0000000..16428cc --- /dev/null +++ b/source/armhook.cpp @@ -0,0 +1,58 @@ +#include "armhook.h" +#include "libres.h" +#include "memutils.h" +#include "utils.h" + +#include "../includes/pcsx2/memalloc.h" + +namespace armhook +{ + void init() + { + } + + void replace_mips_call(ptr addr, ptr func_to) + { + memutils::mem_write_mips_call(addr, func_to, false); + } + + void hook_mips_func(ptr func, uint32_t startSize, ptr func_to, ptr *func_orig) + { + uint8_t *space = cast(AllocMemBlock(startSize + 32)); + while ((cast(space) & 0x0F) != (cast(func) & 0x0F)) + space++; + memutils::mem_write_arr(space, func, startSize); + memutils::mem_write_mips_jmp(space + startSize, func + startSize, true); + *func_orig = space; + memutils::mem_write_mips_jmp(func, func_to, true); + } + + std::vector find_mips_func_calls(ptr func) + { + uint32_t code = 0x0C000000 | ((cast(func) >> 2) & 0x03FFFFFF); + std::vector res; + + void *imageBase = (void*)0x100000; + + uint8_t* addrStart = (uint8_t*)imageBase; + uint8_t* addrMax = (uint8_t*)0xFFFFFF; + for (uint8_t* addr = addrStart; addr < addrMax; addr += 4) + if (*cast(addr) == code) + res.push_back(addr); + + return res; + } + + std::vector find_mips_func_calls_in_func(ptr func, ptr func_in) + { + uint32_t code = 0x0C000000 | ((cast(func) >> 2) & 0x03FFFFFF); + std::vector res; + + // let's assume first jr $ra is func end + for (uint8_t *addr = func_in; *cast(addr) != 0x03E00008; addr += 4) + if (*cast(addr) == code) + res.push_back(addr); + + return res; + } +} diff --git a/source/armhook.h b/source/armhook.h new file mode 100644 index 0000000..3a84715 --- /dev/null +++ b/source/armhook.h @@ -0,0 +1,44 @@ +#pragma once + +#include "common.h" + +namespace armhook +{ + // initialize + void init(); + // replace call + void replace_mips_call(ptr addr, ptr func_to); + // templated proc + template + void replace_mips_call(T1 addr, T2 func_to) + { + replace_mips_call(cast(addr), cast(func_to)); + } + // common hook proc + void hook_mips_func(ptr func, uint32_t startSize, ptr func_to, ptr *func_orig); + // templated proc + template + void hook_mips_func(T func, uint32_t startSize, T func_to, T *func_orig) + { + hook_mips_func(cast(func), + startSize, + cast(func_to), + cast(func_orig)); + } + // common find func calls + std::vector find_mips_func_calls(ptr func); + // templated find func calls + template + std::vector find_mips_func_calls(T func) + { + return find_mips_func_calls(cast(func)); + } + // common find func calls in another func + std::vector find_mips_func_calls_in_func(ptr func, ptr func_in); + template + // templated find func calls in another func + std::vector find_mips_func_calls_in_func(T1 func, T2 func_in) + { + return find_mips_func_calls_in_func(cast(func), cast(func_in)); + } +} diff --git a/source/common.h b/source/common.h new file mode 100644 index 0000000..d5746ed --- /dev/null +++ b/source/common.h @@ -0,0 +1,34 @@ +#pragma once + +#ifdef __INTELLISENSE__ +#define __STDC__ +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define cast reinterpret_cast + +typedef char *LPSTR; +typedef const char *LPCSTR; +typedef uint8_t *ptr; + +#define __STATIC_ASSERT(expr) typedef char __static_assert[expr ? 1 : -1]; + +#define STRUCT_TYPE(x) x +#define STRUCT_SIZE(struc, size) __STATIC_ASSERT(sizeof(STRUCT_TYPE(struc)) == size) + +typedef std::basic_string, std::allocator > wide_string; + +#pragma GCC diagnostic ignored "-Wswitch" +#pragma GCC diagnostic ignored "-Wsign-compare" +#pragma GCC diagnostic ignored "-Wunused-function" +#pragma GCC diagnostic ignored "-Wreturn-type" +#pragma GCC diagnostic ignored "-Wuninitialized" + diff --git a/source/core.cpp b/source/core.cpp new file mode 100644 index 0000000..cb5299e --- /dev/null +++ b/source/core.cpp @@ -0,0 +1,1559 @@ +#include "core.h" +#include "core_asm.h" +#include "core_menu.h" +#include "armhook.h" +#include "utils.h" +#include "strutils.h" +#include "text.h" +#include "touch.h" +#include "ui.h" +#include "libres.h" +#include "pattern.h" +#include "mutex.h" +#include "memutils.h" +#include "psplang.h" + +#include "../includes/pcsx2/memalloc.h" + +uint8_t CLEOScripts[1000000] = { 1 }; + +namespace core +{ + e_game game; + e_game GetGame() { return game; } + + enum e_game_version + { + VER_NONE, + VER_GTA3_1_4, + VER_GTAVC_1_03, + VER_GTASA_1_00, + VER_GTASA_1_01, + VER_GTASA_1_02, + VER_GTASA_1_03, + VER_GTASA_1_05, + VER_GTASA_1_06, + VER_GTASA_1_05_GER, + VER_GTASA_1_07, + VER_GTA3_1_6, + VER_GTAVC_1_06, + VER_GTASA_1_08, + VER_GTALCS_2_2, + VER_GTA3_1_8_OR_HIGHER, + VER_GTAVC_1_09_OR_HIGHER, + VER_GTASA_2_00_OR_HIGHER, + VER_GTALCS_2_4_OR_HIGHER, + VER_GTALCS_PSP_1_05_OR_HIGHER, + VER_GTAVCS_PSP_1_02_OR_HIGHER + }; + + const char *str_game_version[] = { + "VER_NONE", + "VER_GTA3_1_4", + "VER_GTAVC_1_03", + "VER_GTASA_1_00", + "VER_GTASA_1_01", + "VER_GTASA_1_02", + "VER_GTASA_1_03", + "VER_GTASA_1_05", + "VER_GTASA_1_06", + "VER_GTASA_1_05_GER", + "VER_GTASA_1_07", + "VER_GTA3_1_6", + "VER_GTAVC_1_06", + "VER_GTASA_1_08", + "VER_GTALCS_2_2", + "VER_GTA3_1_8_OR_HIGHER", + "VER_GTAVC_1_09_OR_HIGHER", + "VER_GTASA_2_00_OR_HIGHER", + "VER_GTALCS_2_4_OR_HIGHER", + "VER_GTALCS_PSP_1_05_OR_HIGHER", + "VER_GTAVCS_PSP_1_02_OR_HIGHER" + }; + + int game_ver = VER_NONE; + int GetGameVersion() { return game_ver; } + + std::string package_name; + const char *GetPackageName() { return package_name.c_str(); } + + std::string package_version_name; + const char *GetPackageVersionStr() { return package_version_name.c_str(); } + + int package_version_code; + int GetPackageVersionCode() { return package_version_code; } + + void preload_scripts(); + void launch_scripts(); + void save_scripts(uint32_t a = 0, uint32_t b = 0); // params not used for SA + void process_opcodes(ptr handle); + + struct t_script + { + std::string name; + int32_t invokable_id; + uint8_t *handle; // returned by CTheScripts::StartNewScript + uint8_t *code; // script code + uint32_t code_size; + uint32_t offset; // start ip for script vm, based on ScriptSpace overrun + bool wait_passed; + uint32_t context[32]; + + t_script() : handle(NULL), code(NULL), offset(0) {} + }; + + std::vector scripts; + + t_script *get_script_using_handle(uint8_t *handle) + { + for (int32_t i = 0; i < scripts.size(); i++) + if (scripts[i]->handle == handle) + return scripts[i]; + return NULL; + } + + int32_t image_base; // image base + + struct t_mutex_var + { + uint32_t id, value; + }; + + std::vector mutex_vars; + + template inline T getfield(ptr structure, uint32_t offset) + { + return *cast(structure + offset); + } + + template inline void setfield(ptr structure, uint32_t offset, T val) + { + *cast(structure + offset) = val; + } + + template inline T select(T val_3, T val_vc, T val_sa, T val_lcs, T val_vcs) + { + switch (game) + { + case GTA3: return val_3; + case GTAVC: return val_vc; + case GTASA: return val_sa; + case GTALCS: return val_lcs; + case GTAVCS: return val_vcs; + } + } + + // @windowSize + typedef struct { uint32_t w, h; } _windowSize; + _windowSize *windowSize; + + // @windowSize LCS + typedef struct { uint32_t h, w; } _windowSizeLCS; + _windowSizeLCS *windowSizeLCS; + + // @CTimer::m_snTimeInMilliseconds + uint32_t *CTimer__m_snTimeInMilliseconds; + + // @ScriptParams + uint32_t *ScriptParams; + uint32_t *GetScriptParams() { return ScriptParams; } + + // @CTheScripts::ScriptSpace, @CTheScripts::pActiveScripts + ptr CTheScripts__ScriptSpace, CTheScripts__pActiveScripts; + ptr *CTheScripts__ScriptSpace_LCS; + + // @CTheScripts::StartNewScript + typedef ptr (*fn_CTheScripts__StartNewScript)(uint32_t); + fn_CTheScripts__StartNewScript CTheScripts__StartNewScript; + + // @CRunningScript::GetPointerToScriptVariable SA + typedef ptr (*fn_SA_CRunningScript__GetPointerToScriptVariable)(ptr thiz, uint8_t); + fn_SA_CRunningScript__GetPointerToScriptVariable SA_CRunningScript__GetPointerToScriptVariable; + + // @CRunningScript::GetPointerToScriptVariable VC3LCS + typedef ptr (*fn_VC3LCS_CRunningScript__GetPointerToScriptVariable)(ptr thiz, uint32_t *ip, uint8_t); + fn_VC3LCS_CRunningScript__GetPointerToScriptVariable VC3LCS_CRunningScript__GetPointerToScriptVariable; + + ptr CRunningScript__GetPointerToScriptVariable(ptr thiz, uint8_t param) + { + switch (game) + { + case GTA3: + case GTAVC: + return VC3LCS_CRunningScript__GetPointerToScriptVariable(thiz, cast(thiz + 0x10), param); + case GTASA: + return SA_CRunningScript__GetPointerToScriptVariable(thiz, param); + case GTALCS: + return VC3LCS_CRunningScript__GetPointerToScriptVariable(thiz, cast(thiz + 0x18), param); + case GTAVCS: + return VC3LCS_CRunningScript__GetPointerToScriptVariable(thiz, cast(thiz + 0x10), param); + + } + } + + // @CRunningScript::CollectParameters SA + typedef void (*fn_SA_CRunningScript__CollectParameters)(ptr thiz, uint32_t count); + fn_SA_CRunningScript__CollectParameters SA_CRunningScript__CollectParameters; + + // @CRunningScript::CollectParameters VC3 + typedef void (*fn_VC3_CRunningScript__CollectParameters)(ptr thiz, uint32_t *ip, uint32_t count); + fn_VC3_CRunningScript__CollectParameters VC3_CRunningScript__CollectParameters; + + // @CRunningScript::CollectParameters LCS + typedef int (*fn_LCS_CRunningScript__CollectParameters)(ptr thiz, uint32_t *ip, uint32_t count, uint32_t *p); + fn_LCS_CRunningScript__CollectParameters LCS_CRunningScript__CollectParameters; + + void CRunningScript__CollectParameters(ptr thiz, uint32_t count) + { + switch (game) + { + case GTA3: + case GTAVC: + VC3_CRunningScript__CollectParameters(thiz, cast(thiz + 0x10), count); + break; + case GTASA: + SA_CRunningScript__CollectParameters(thiz, count); + break; + case GTALCS: + LCS_CRunningScript__CollectParameters(thiz, cast(thiz + 0x18), count, ScriptParams); + break; + case GTAVCS: + LCS_CRunningScript__CollectParameters(thiz, cast(thiz + 0x10), count, ScriptParams); + break; + } + } + + // @CRunningScript::UpdateCompareFlag + typedef void (*fn_CRunningScript__UpdateCompareFlag)(ptr thiz, bool flag); + fn_CRunningScript__UpdateCompareFlag CRunningScript__UpdateCompareFlag; + + // inidcates when @CTheScripts::Load executing + bool bCTheScripts__Load = false; + + // @CTheScripts::Load SA + // starts custom scripts after game loads saved scripts, custom scripts start in CTheScripts::Init must be disabled + typedef void (*fn_SA_CTheScripts__Load)(); + fn_SA_CTheScripts__Load _SA_CTheScripts__Load, SA_CTheScripts__Load_; + void SA_CTheScripts__Load() + { + utils::log("CTheScripts::Load()"); + bCTheScripts__Load = true; + SA_CTheScripts__Load_(); + bCTheScripts__Load = false; + preload_scripts(); + launch_scripts(); + } + + // @CTheScripts::Load VC3LCS + typedef void (*fn_VC3_CTheScripts__Load)(uint32_t, uint32_t); + fn_VC3_CTheScripts__Load _VC3_CTheScripts__Load, VC3_CTheScripts__Load_; + void VC3_CTheScripts__Load(uint32_t a, uint32_t b) + { + utils::log("CTheScripts::Load()"); + bCTheScripts__Load = true; + VC3_CTheScripts__Load_(a, b); + bCTheScripts__Load = false; + preload_scripts(); + launch_scripts(); + } + + // @CTheScripts::Init + // starts custom scripts after game loads scm + typedef void (*fn_CTheScripts__Init)(); + fn_CTheScripts__Init _CTheScripts__Init, CTheScripts__Init_; + void CTheScripts__Init() + { + utils::log("CTheScripts::Init()"); + CTheScripts__Init_(); + if (!bCTheScripts__Load) + { + preload_scripts(); + launch_scripts(); + } + } + + // @CTheScripts::Init LCS + typedef int32_t (*fn_LCS_CTheScripts__Init)(bool b); + fn_LCS_CTheScripts__Init _LCS_CTheScripts__Init, LCS_CTheScripts__Init_; + int32_t LCS_CTheScripts__Init(bool b) + { + /*typedef int32_t (*fn_LCS_CFileMgr__LoadFile)(const char *fname, uint8_t *buf, int32_t size, const char *mode); + fn_LCS_CFileMgr__LoadFile LoadFile = getsym("_ZN8CFileMgr8LoadFileEPKcPhiS1_"); + uint8_t *buf = (uint8_t *)malloc(1024 * 1024 * 4); + int32_t size = LoadFile("data/main.scm", buf, 1024 * 1024 * 4, "rb"); + if (size > 0) + { + FILE *f = fopen("/sdcard/main.scm", "wb"); + fwrite(buf, 1, size, f); + fclose(f); + }*/ + + utils::log("CTheScripts::Init()"); + int32_t res = LCS_CTheScripts__Init_(b); + CTheScripts__ScriptSpace = *CTheScripts__ScriptSpace_LCS; // dynamic allocation + preload_scripts(); + launch_scripts(); + return res; + } + + // @CTheScripts::Save SA + // removes custom scripts from active scripts list, calls save and adds custom scripts to active list + typedef void (*fn_SA_CTheScripts__Save)(); + fn_SA_CTheScripts__Save _SA_CTheScripts__Save, SA_CTheScripts__Save_; + void SA_CTheScripts__Save() + { + utils::log("CTheScripts::Save()"); + save_scripts(); + } + + // @CTheScripts::Save VC3LCS + typedef void (*fn_VC3LCS_CTheScripts__Save)(uint32_t, uint32_t); + fn_VC3LCS_CTheScripts__Save _VC3LCS_CTheScripts__Save, VC3LCS_CTheScripts__Save_; + void VC3LCS_CTheScripts__Save(uint32_t a, uint32_t b) + { + utils::log("CTheScripts::Save()"); + save_scripts(a, b); + } + + // @CRunningScript::ProcessOneCommand + // executes one opcode, returns true when thread switch should occure + typedef bool (*fn_CRunningScript__ProcessOneCommand)(ptr thiz); + fn_CRunningScript__ProcessOneCommand _CRunningScript__ProcessOneCommand, CRunningScript__ProcessOneCommand_; + bool CRunningScript__ProcessOneCommand(ptr thiz) + { + process_opcodes(thiz); + return true; + } + + // @CText::Get + // gets gxt entry by name + ptr CTextHandle = NULL; + typedef uint16_t *(*fn_CText__Get)(ptr thiz, LPCSTR name); + fn_CText__Get _CText__Get, CText__Get_; + uint16_t *CText__Get(ptr thiz, LPCSTR name) + { + //utils::log("CText::Get(): %s", name); + CTextHandle = thiz; + + if (CTextHandle && !psplang::is_init()) + psplang::init(); + + uint16_t *e = text::get_gxt_entry(name); + return e ? e : CText__Get_(thiz, name); + } + + uint16_t *GetGxtEntry(LPCSTR name, bool useCustom) + { + if (!CTextHandle || !name || !name[0] || (game == GTA3 && strlen(name) > 7)) + return NULL; + uint16_t *e = useCustom ? text::get_gxt_entry(name) : NULL; + return e ? e : CText__Get_(CTextHandle, name); + } + + // sceCtrlReadBufferPositive + typedef void (*fn_CPad__UpdatePads)(); + fn_CPad__UpdatePads _CPad__UpdatePads, CPad__UpdatePads_; + void CPad__UpdatePads() + { + //utils::log("CPad::UpdatePads"); + CPad__UpdatePads_(); + + touch::psp_input_event(); + ui::handle_psp_controls(); + } + + // legacy external storage path + std::string get_storage_dir() + { + char dir[64]; + sprintf(dir, "ms0:/PSP/PLUGINS/cleo/%s/", select("", "", "", "lcs", "vcs")); + return dir; + } + + uint32_t VC3LCS_CRunningScript__ProcessOneCommand_call_1, + VC3LCS_CRunningScript__ProcessOneCommand_call_2, + SA_CRunningScript__ProcessOneCommand_call, + SA_CRunningScript__ProcessOneCommand_call_end, + VC3SA_NvEventQueueActivity__init_ptr; + + bool init_pattern() + { + #define PATTERN_NOT_FOUND { utils::log("required pattern %d not found!", __LINE__); return false; } + #define FIND_PATTERN(...) if (!__FindPatternAddress(addr, __VA_ARGS__)) PATTERN_NOT_FOUND; + + uint32_t addr; + + // CRunningScript__ProcessOneCommand + switch (game) + { + case GTALCS: + FIND_PATTERN("2D 20 00 02 00 00 00 00 00 00 00 00 ?? ?? ?? ?? 00 00 00 00"); + VC3LCS_CRunningScript__ProcessOneCommand_call_1 = addr - 4; + + break; + case GTAVCS: + FIND_PATTERN("2B 18 64 00 ?? ?? ?? ?? 0B 02 02 92"); + VC3LCS_CRunningScript__ProcessOneCommand_call_1 = addr + 12; + break; + } + + return true; + } + + void init_fixes() + { + + } + + bool init_code() + { + if (!init_pattern()) + return false; + + if (game == GTALCS) + { + uint32_t addr; + + #define READ_ADDR(addr1, addr2) ((static_cast(*cast(addr1)) << 16) | *cast(addr2)) + #define READ_ADDR_INDIRECT(addr1, addr2) (READ_ADDR(addr1, addr2) - 0x10000) + #define READ_ADDR_GP(addr) (libres::getGpValue() + *cast(addr)) + #define READ_REL3_ADDR(offset) memutils::mem_read_mips_jmp(cast(addr) + offset) + + // ScriptParams + FIND_PATTERN("?? ?? 02 3C 2D 80 C0 00 ?? ?? 51 24"); + ScriptParams = cast(READ_ADDR_INDIRECT(addr, addr + 8)); + utils::log("ScriptParams: 0x%08X", ScriptParams); + + // _ZN6CTimer22m_snTimeInMillisecondsE + FIND_PATTERN("FF 3F 43 30 ?? ?? ?? ?? 01 00 42 30 00 60 83 44"); + CTimer__m_snTimeInMilliseconds = cast(READ_ADDR_INDIRECT(addr - 8, addr - 4)); + utils::log("CTimer__m_snTimeInMilliseconds: 0x%08X", CTimer__m_snTimeInMilliseconds); + + // _ZN14CRunningScript17ProcessOneCommandEv + FIND_PATTERN("18 00 C4 8C 21 10 44 00"); + _CRunningScript__ProcessOneCommand = cast(addr - 16); // + + // _ZN11CTheScripts11ScriptSpaceE + CTheScripts__ScriptSpace_LCS = cast(READ_ADDR_GP(addr - 4)); + utils::log("CTheScripts__ScriptSpace_LCS: 0x%08X", CTheScripts__ScriptSpace_LCS); + + // _ZN11CTheScripts14pActiveScriptsE + FIND_PATTERN("14 00 B4 27 18 00 B6 27 1C 00 B7 27 21 88 02 02 00 00 00 00"); + CTheScripts__pActiveScripts = cast(READ_ADDR_INDIRECT(addr - 8, addr - 4)); + utils::log("CTheScripts__pActiveScripts: 0x%08X", CTheScripts__pActiveScripts); + + // _ZN11CTheScripts14StartNewScriptEi + FIND_PATTERN("08 00 B1 FF 2D 88 80 00 10 00 BF FF ?? ?? ?? ?? 2D 20 00 02"); + CTheScripts__StartNewScript = cast(addr - 24); + utils::log("CTheScripts__StartNewScript: 0x%08X", CTheScripts__StartNewScript); + + // _ZN5CText3GetEPKc + FIND_PATTERN("2D 30 A0 03 18 00 B1 FF 2D 80 80 00 2D 88 A0 00 20 00 BF FF ?? ?? ?? ?? 00 00 A0 A3 00 00 A3 93 ?? ?? ?? ?? 2D 20 40 00"); + _CText__Get = cast(addr - 8); + utils::log("_CText__Get: 0x%08X", _CText__Get); + + // _ZN14CRunningScript26GetPointerToScriptVariableEPjh + FIND_PATTERN("?? ?? ?? ?? 00 00 BF FF ?? ?? ?? ?? FF 00 C6 30"); + VC3LCS_CRunningScript__GetPointerToScriptVariable = cast(addr); + utils::log("VC3LCS_CRunningScript__GetPointerToScriptVariable: 0x%08X", VC3LCS_CRunningScript__GetPointerToScriptVariable); + + // _ZN11CTheScripts14SaveAllScriptsEPhPj + FIND_PATTERN("28 00 B5 FF 2D 48 A0 00"); + _VC3LCS_CTheScripts__Save = cast(addr - 12); + utils::log("_VC3LCS_CTheScripts__Save: 0x%08X", _VC3LCS_CTheScripts__Save); + + // _ZN14CRunningScript17CollectParametersEPjiPi + FIND_PATTERN("08 00 B1 FF 2D 80 A0 00 10 00 B2 FF 2D 88 E0 00"); + LCS_CRunningScript__CollectParameters = cast(addr - 8); + utils::log("LCS_CRunningScript__CollectParameters: 0x%08X", LCS_CRunningScript__CollectParameters); + + // _ZN11CTheScripts4InitEb + FIND_PATTERN("2D F0 80 00 30 00 B0 FF"); + _LCS_CTheScripts__Init = cast(addr - 8); // + utils::log("_LCS_CTheScripts__Init: 0x%08X", _LCS_CTheScripts__Init); + + // sceCtrlReadBufferPositive + FIND_PATTERN("?? ?? ?? ?? 2D 20 00 00 2D 28 00 00 ?? ?? ?? ?? 2D 20 40 00 ?? ?? ?? ?? 01 00 04 24"); + _CPad__UpdatePads = cast(addr - 8); + touch::CPad__GetPad = cast(READ_REL3_ADDR(0)); + utils::log("_CPad__UpdatePads: 0x%08X", _CPad__UpdatePads); + + armhook::hook_mips_func(_VC3LCS_CTheScripts__Save, 8, VC3LCS_CTheScripts__Save, &VC3LCS_CTheScripts__Save_); + CRunningScript__ProcessOneCommand_ = _CRunningScript__ProcessOneCommand; + armhook::replace_mips_call(VC3LCS_CRunningScript__ProcessOneCommand_call_1, CRunningScript__ProcessOneCommand); + armhook::hook_mips_func(_LCS_CTheScripts__Init, 8, LCS_CTheScripts__Init, &LCS_CTheScripts__Init_); + armhook::hook_mips_func(_CText__Get, 8, CText__Get, &CText__Get_); + armhook::hook_mips_func(_CPad__UpdatePads, 8, CPad__UpdatePads, &CPad__UpdatePads_); + } + else if (game == GTAVCS) + { + uint32_t addr; + + // ScriptParams + FIND_PATTERN("?? ?? 11 3C 10 00 12 26 18 00 BF FF ?? ?? ?? ?? ?? ?? ?? ?? 2D 28 40 02 ?? ?? ?? ?? 2D 20 00 02 2D 28 40 02"); + ScriptParams = cast(READ_ADDR_INDIRECT(addr, addr + 12)); + utils::log("ScriptParams: 0x%08X", ScriptParams); + + // _ZN6CTimer22m_snTimeInMillisecondsE + FIND_PATTERN("4D 00 03 3C ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 23 10 43 00"); + CTimer__m_snTimeInMilliseconds = cast(READ_ADDR_INDIRECT(addr, addr + 4)); + utils::log("CTimer__m_snTimeInMilliseconds: 0x%08X", CTimer__m_snTimeInMilliseconds); + + // _ZN11CTheScripts11ScriptSpaceE + FIND_PATTERN("48 00 03 3C 00 00 25 8E"); + CTheScripts__ScriptSpace_LCS = cast(READ_ADDR(addr, addr + 8)); + utils::log("CTheScripts__ScriptSpace_LCS: 0x%08X", CTheScripts__ScriptSpace_LCS); + + // _ZN11CTheScripts14pActiveScriptsE + FIND_PATTERN("4D 00 05 3C 2D 20 00 02 ?? ?? ?? ?? ?? ?? ?? ?? 01 00 03 24 2D 10 00 02"); + CTheScripts__pActiveScripts = cast(READ_ADDR_INDIRECT(addr, addr + 12)); + utils::log("CTheScripts__pActiveScripts: 0x%08X", CTheScripts__pActiveScripts); + + // _ZN11CTheScripts14StartNewScriptEi + FIND_PATTERN("00 00 B0 FF ?? ?? ?? ?? 08 00 B1 FF ?? ?? ?? ?? 2D 88 80 00 10 00 BF FF ?? ?? ?? ?? 2D 20 00 02"); + CTheScripts__StartNewScript = cast(addr - 8); + + // _ZN14CRunningScript17ProcessOneCommandEv + FIND_PATTERN("10 00 BF FF 48 00 02 3C"); + _CRunningScript__ProcessOneCommand = cast(addr - 8); // + + // _ZN5CText3GetEPKc + FIND_PATTERN("2D 30 A0 03 18 00 B1 FF 2D 80 80 00 2D 88 A0 00 20 00 BF FF ?? ?? ?? ?? 00 00 A0 A3 00 00 A3 93 ?? ?? ?? ?? 2D 20 40 00"); + _CText__Get = cast(addr - 8); + utils::log("_CText__Get: 0x%08X", _CText__Get); + + // GetPointerToScriptVariable + FIND_PATTERN("00 00 E6 8C 2D 60 40 00"); + VC3LCS_CRunningScript__GetPointerToScriptVariable = cast(addr - 8); + + // _ZN11CTheScripts14SaveAllScriptsEPhPj + FIND_PATTERN("4D 00 03 3C 28 00 B5 FF 2D 48 80 00"); + _VC3LCS_CTheScripts__Save = cast(addr - 12); + + // _ZN14CRunningScript17CollectParametersEPjiPi + FIND_PATTERN("10 00 B2 FF 2D 88 A0 00 18 00 B3 FF 2D 90 E0 00 20 00 B4 FF ?? ?? ?? ?? 00 00 B0 FF 2D A0 80 00"); + LCS_CRunningScript__CollectParameters = cast(addr - 8); + + // _ZN11CTheScripts4InitEb + FIND_PATTERN("30 00 B0 FF 4D 00 05 3C"); + _LCS_CTheScripts__Init = cast(addr - 8); // + + // sceCtrlReadBufferPositive + FIND_PATTERN("?? ?? ?? ?? 2D 20 00 00 2D 28 00 00 ?? ?? ?? ?? 2D 20 40 00 ?? ?? ?? ?? 01 00 04 24"); + touch::CPad__GetPad = cast(READ_REL3_ADDR(0)); + _CPad__UpdatePads = cast(addr - 8); + + armhook::hook_mips_func(_VC3LCS_CTheScripts__Save, 8, VC3LCS_CTheScripts__Save, &VC3LCS_CTheScripts__Save_); + CRunningScript__ProcessOneCommand_ = _CRunningScript__ProcessOneCommand; + armhook::replace_mips_call(VC3LCS_CRunningScript__ProcessOneCommand_call_1, CRunningScript__ProcessOneCommand); + armhook::hook_mips_func(_LCS_CTheScripts__Init, 8, LCS_CTheScripts__Init, &LCS_CTheScripts__Init_); + armhook::hook_mips_func(_CText__Get, 8, CText__Get, &CText__Get_); + armhook::hook_mips_func(_CPad__UpdatePads, 8, CPad__UpdatePads, &CPad__UpdatePads_); + } + + return true; + } + + // calls all initializers + void initialize() + { + mutex::init(); + + utils::log(VERSION_STR); + utils::log("initialize start"); + + // resolve main game lib + if (libres::init(game, image_base)) + { + utils::log("game's (index %d) main library resolved (image base 0x%08X)", game, image_base); + } else + { + utils::log("initialize failed - can't resolve game's main library"); + return; + } + + game_ver = game == GTALCS ? VER_GTALCS_PSP_1_05_OR_HIGHER : VER_GTAVCS_PSP_1_02_OR_HIGHER; + + // print game version + utils::log("game ver: %s", str_game_version[game_ver]); + + // init armhook + armhook::init(); + + // init code + if (init_code()) + { + utils::log("code initialized"); + } else + { + utils::log("initialize failed - can't initialize code"); + return; + } + + // init ui code + ui::init(); + utils::log("ui code initialized"); + + // init text + text::init(); + + utils::log("initialize success"); + } + + // script code buf + // copies script to the buf and adds it to the list + void preload_script(std::string fname, const uint8_t *code, uint32_t code_size, bool is_invokable, uint32_t invokable_id) + { + // copy script to the script code buf + if (game == GTALCS || game == GTAVCS) // has to be fixed in SB + { + if (code_size >= 8) + { + uint64_t pad; + memcpy(&pad, code, sizeof(pad)); + if (!pad) + { + code += 8; + code_size -= 8; + } + } + } + uint8_t *script_code; + script_code = new uint8_t[code_size + 8]; + memcpy(script_code, code, code_size); + + // fill script desc and add it to the scripts arr + t_script *script = new t_script(); + script->invokable_id = is_invokable ? invokable_id : -1; + script->name = fname; + script->code = script_code; + script->code_size = code_size; + script->offset = game == GTASA ? cast(script->code) : cast(script->code) - cast(CTheScripts__ScriptSpace); + scripts.push_back(script); + + // set invokable script name for menu + if (is_invokable) + text::set_gxt_invokable_script_name(invokable_id, fname); + } + + // preloads scripts at start + void preload_scripts() + { + // clear scripts + for (int i = 0; i < scripts.size(); i++) + { + delete[] scripts[i]->code; + delete scripts[i]; + } + scripts.clear(); + // init gxt entries + text::init(); + // delete menu ui + ui::delete_menu(); + // init script menu for sa + switch (game) + { + case GTALCS: + preload_script("menu", lcs_menu_script, sizeof(lcs_menu_script), false, 0); + break; + case GTAVCS: + preload_script("menu", vcs_menu_script, sizeof(vcs_menu_script), false, 0); + break; + } + // check all found files + uint32_t invokable_count = 0; + + uintptr_t script_offset = (uintptr_t)(&CLEOScripts); + do + { + std::string fname = (const char*)script_offset; + size_t script_name_len = fname.size() + 1; + script_offset += script_name_len; + uint32_t script_size = *(uint32_t*)(script_offset); + script_offset += sizeof(uint32_t); + + if (!script_size) + break; + + if (script_offset + script_size > ((uintptr_t)(&CLEOScripts) + sizeof(CLEOScripts))) + break; + + std::string root = strutils::get_parent_path(fname, true); + fname = strutils::get_filename(fname); + + if ((game == GTALCS && root == "lcs") || (game == GTAVCS && root == "vcs") || root == "") + { + std::string ext = strutils::get_ext(fname); + bool is_invokable = ext == "csi"; + if (ext == "csa" || is_invokable) + { + // preload script + uint32_t code_size = script_size; + uint8_t* code = (uint8_t*)script_offset; + if (!code || !code_size) + { + utils::log("can't read script '%s'", fname.c_str()); + continue; + } + preload_script(fname, code, code_size, is_invokable, invokable_count); + if (is_invokable) + invokable_count++; + utils::log("script '%s' with size %d preloaded", fname.c_str(), code_size); + } + else if (ext == "fxt") + { + // load fxt + uint32_t size = script_size; + LPSTR text_raw = cast(script_offset); + if (!text_raw || !size) + { + utils::log("can't read fxt '%s'", fname.c_str()); + continue; + } + LPSTR text = cast(malloc(size + 1)); + memcpy(text, text_raw, size); + text[size] = 0; + text::load_gxt_entries_from_text(text, size); + free(text); + free(text_raw); + utils::log("fxt '%s' loaded", fname.c_str()); + } + } + script_offset += script_size; + + } while (true); + + utils::log("total preloaded scripts %d", scripts.size()); + } + + // launches custom scripts + void launch_scripts() + { + utils::log("launch_scripts start"); + mutex_vars.clear(); + for (int32_t i = 0; i < scripts.size(); i++) + { + t_script *script = scripts[i]; + // only normal custom scripts start here + if (script->invokable_id == -1) + { + utils::log("starting script '%s'", script->name.c_str()); + script->handle = CTheScripts__StartNewScript(script->offset); + script->wait_passed = false; + } else // invokable scripts start by special opcode and don't require wait + { + script->handle = NULL; + script->wait_passed = true; + } + } + utils::log("launch_scripts success"); + } + + void save_scripts(uint32_t a, uint32_t b) + { + utils::log("save_scripts start"); + + struct t_linked_script + { + t_linked_script *next; + t_linked_script *prev; + char *get_name() + { + return cast(cast(this) + select(8, 8, 8, 16, 0x20F)); + } + }; + + ptr game_active_scripts = CTheScripts__pActiveScripts; + + t_linked_script *curActiveScript, *firstActiveScript = NULL; + + curActiveScript = *cast(game_active_scripts); + while (curActiveScript) + { + bool orig = get_script_using_handle(cast(curActiveScript)) == NULL; + if (orig) + { + if (!firstActiveScript) + { + firstActiveScript = curActiveScript; + *cast(game_active_scripts) = firstActiveScript; + } + } else + { + utils::log("skipping '%.8s'", curActiveScript->get_name()); + if (curActiveScript->prev) + curActiveScript->prev->next = curActiveScript->next; + if (curActiveScript->next) + curActiveScript->next->prev = curActiveScript->prev; + } + curActiveScript = curActiveScript->next; + } + + if (game == GTASA) + SA_CTheScripts__Save_(); + else + VC3LCS_CTheScripts__Save_(a, b); + + // find last active + curActiveScript = *cast(game_active_scripts); + while (curActiveScript->next) + curActiveScript = curActiveScript->next; + + for (int32_t i = 0; i < scripts.size(); i++) + { + if (!scripts[i]->handle) + continue; + curActiveScript->next = cast(scripts[i]->handle); + curActiveScript->next->prev = curActiveScript; + curActiveScript->next->next = NULL; + curActiveScript = curActiveScript->next; + } + + utils::log("save_scripts success"); + } + + ptr get_real_code_ptr(uint32_t ip) + { + if (game != GTASA) ip += cast(CTheScripts__ScriptSpace); + return cast(ip); + } + + uint32_t get_ip_using_real_code_ptr(ptr realPtr) + { + if (game != GTASA) realPtr -= cast(CTheScripts__ScriptSpace); + return cast(realPtr); + } + + ptr get_real_label_ptr(ptr handle, uint32_t offset) + { + if (t_script *script = get_script_using_handle(handle)) + { + int32_t offset_signed = *cast(&offset); + offset = (offset_signed >= 0) ? (cast(script->code) + offset_signed) : (cast(script->code) - offset_signed); + return cast(offset); + } + return NULL; + } + + bool read_str_8byte(ptr handle, std::string &str) + { + uint32_t *p_ip = cast(handle + select(0x10, 0x10, 0x14, 0x18, 0x10)); + ptr code = get_real_code_ptr(*p_ip); + str.clear(); + if (game != GTAVCS) + { + str.append(cast(code)); + *p_ip += 8; + } else + { + if (*code != 0x0A) + return false; + str.append(cast(code + 1)); + *p_ip += str.size() + 2; + } + return true; + } + + bool read_str_long(ptr handle, std::string &str) + { + uint32_t *p_ip = cast(handle + select(0x10, 0x10, 0x14, 0x18, 0x10)); + ptr code = get_real_code_ptr(*p_ip); + if (*code != select(0x0E, 0x0E, 0x0E, 0x6B, 0x6B)) + return false; + str.clear(); + str.append(cast(code + 2), *(code + 1)); + *p_ip += *(code + 1) + 2; + return true; + } + + // implements custom opcodes + bool custom_opcode(t_script& script, uint16_t op); + // processes opcodes until opcode after which CRunningScript switch occures + void process_opcodes(ptr handle) + { + // check if this thread handle is from our script + bool handle_found = false; + bool wait_passed = false; + uint8_t *code = NULL; + std::string name; + t_script *script = get_script_using_handle(handle); + if (script) + { + code = script->code; + name = script->name; + wait_passed = script->wait_passed; + handle_found = true; + } + + //utils::log("%u handle_found %X %d", utils::get_tick_count(), handle, handle_found); + + // after the start custom scripts must be idle for a few seconds + if (handle_found && !wait_passed) + { + uint32_t wait_time = (game == GTASA) ? 3000 : 2000; + setfield(handle, select(0x7C, 0x7C, 0xEC, 0x210, 0x200), *CTimer__m_snTimeInMilliseconds + wait_time); + script->wait_passed = true; + return; + } + + // opcodes to replace + uint16_t OP_J = 2, + OP_JT = 0x4C, + OP_JF = 0x4D, + OP_CALL = 0x50, + OP_RET = 0x51, + OP_ENDTHREAD = 0x4E, + OP_ENDCUSTOMTHREAD = 0x05DC; + + if (game == GTAVCS) + { + OP_JT = 0x21; + OP_JF = 0x22; + OP_CALL = 0x25; + OP_RET = 0x26; + OP_ENDTHREAD = 0x23; + } + + bool result; + do + { + ptr ip = getfield(handle, select(0x10, 0x10, 0x14, 0x18, 0x10)); // ip, for SA is absolute ptr + //utils::log("%s %08X %08X %08X", __FUNCTION__, handle, ip + cast(CTheScripts__ScriptSpace), CTheScripts__ScriptSpace); + if (game != GTASA) ip += cast(CTheScripts__ScriptSpace); + + bool cond = getfield(handle, select(0x78, 0x79, 0xE5, 0x20D, 0x209)) != 0; // thread if cond + + uint16_t op = *cast(ip); + + if (handle_found) + { + //utils::log("%s %X %X op %04X", cast(handle + select(8, 8, 8, 16, 0x20F)), handle, ip - code, op); + + if (op == OP_J || op == OP_JT || op == OP_JF || op == OP_CALL) + { + // move to opcode param + ip += 2; + // check param type + if (!(*ip == 1 || ((game == GTALCS || game == GTAVCS) && *ip == 6))) + { + utils::log("wrong param type in '%s' at %d, terminating", name.c_str(), ip - code); + exit(1); + } + ip++; + // read offset as int + int32_t offset_signed = *cast(ip); + ip += 4; + // calc offset from ScriptSpace + uint32_t offset = (offset_signed >= 0) ? (cast(code) + offset_signed) : (cast(code) - offset_signed); + // OP_CALL saves thread ip on call stack + if (op == OP_CALL) + { + //utils::log("OP_CALL"); + uint16_t si = getfield(handle, select(0x2C, 0x2C, 0x38, 0x5C, 0x204)); + setfield(handle, select(0x2C, 0x2C, 0x38, 0x5C, 0x204), si + 1); + ptr vc3_ip = ip - cast(CTheScripts__ScriptSpace); + //utils::log("OP_CALL ret ip %X %X si %d", vc3_ip, ip, si); + setfield(handle, select(0x14, 0x14, 0x18, 0x1C, 0x14) + si * sizeof(uint32_t), select(vc3_ip, vc3_ip, ip, vc3_ip, vc3_ip)); + } + // set new ip + if ((op == OP_J) || (op == OP_CALL) || (op == OP_JT && cond) || (op == OP_JF && !cond)) + ip = cast(offset); + // set thread ip + if (game != GTASA) ip -= cast(CTheScripts__ScriptSpace); + setfield(handle, select(0x10, 0x10, 0x14, 0x18, 0x10), ip); + result = false; + } else + if (op == OP_RET) // lcs uses it for ret from funcs as well, so let's replace it + { + uint16_t si = getfield(handle, select(0x2C, 0x2C, 0x38, 0x5C, 0x204)) - 1; + setfield(handle, select(0x10, 0x10, 0x14, 0x18, 0x10), + getfield(handle, select(0x14, 0x14, 0x18, 0x1C, 0x14) + si * sizeof(uint32_t))); + setfield(handle, select(0x2C, 0x2C, 0x38, 0x5C, 0x204), si); + //utils::log("OP_RET ret ip %X si %d", getfield(handle, select(0x10, 0x10, 0x14, 0x18)), si); + result = false; + } else + if (op == OP_ENDTHREAD || op == OP_ENDCUSTOMTHREAD) + { + // replacement for thread end opcodes + utils::log("terminating script '%s'", name.c_str()); + setfield(handle, select(0x7C, 0x7C, 0xEC, 0x210, 0x200), 0xFFFFFFFF); // wait time + result = true; + } else + { + ptr *p_ip = cast(handle + select(0x10, 0x10, 0x14, 0x18, 0x10)); + *p_ip += 2; + if (custom_opcode(*script, op) /*|| plugins::handle_opcode(script->handle, cast(p_ip), op)*/) + { + result = false; + } else + { + *p_ip -= 2; + result = CRunningScript__ProcessOneCommand_(handle); + } + } + } else + result = CRunningScript__ProcessOneCommand_(handle); + + } while (!result); + } + + bool custom_opcode(t_script &script, uint16_t op) + { + const uint16_t OP_GET_LABEL_ADDR = 0x0DD0, + OP_GET_FUNC_ADDR_CSTR = 0x0DD1, + OP_CONTEXT_CALL = 0x0DD2, + OP_CONTEXT_SET_REG = 0x0DD3, + OP_CONTEXT_GET_REG = 0x0DD4, + OP_GET_PLATFORM = 0x0DD5, + OP_GET_GAME_VER = 0x0DD6, + OP_GET_IMAGE_BASE = 0x0DD7, + OP_READ_MEM = 0x0DD8, + OP_WRITE_MEM = 0x0DD9, + OP_SEARCH_MEM = 0x0DDA, + OP_GET_GAME_VER_EX = 0x0DDB, + OP_SET_MUTEX_VAR = 0x0DDC, + OP_GET_MUTEX_VAR = 0x0DDD, + OP_CALL_FUNCTION = 0x0DDE, + OP_GET_TOUCH_POINT_STATE = 0x0DE0, + OP_GET_TOUCH_SLIDE_STATE = 0x0DE1, + OP_GET_MENU_BUTTON_STATE = 0x0DE2, + OP_GET_MENU_BUTTON_PRESSED = 0x0DE3, + OP_GET_PSP_CONTROL_STATE = 0x0DE4, + OP_GET_PSP_CONTROL_PRESSED = 0x0DE5, + OP_INVOKABLE_SCRIPT_STATS = 0x0DEE, + OP_START_INVOKABLE_SCRIPT = 0x0DEF, + OP_SHOW_MENU_OPEN_ARROW = 0x0DF0, + OP_HIDE_MENU_OPEN_ARROW = 0x0DF1, + OP_CREATE_MENU = 0x0DF2, + OP_DELETE_MENU = 0x0DF3, + OP_GET_MENU_TOUCHED_ITEM_INDEX = 0x0DF4, + OP_SET_MENU_ACTIVE_ITEM_INDEX = 0x0DF5, + OP_GET_MENU_ACTIVE_ITEM_INDEX = 0x0DF6; + + switch (op) + { + case OP_GET_LABEL_ADDR: + { + //utils::log("OP_GET_LABEL_ADDR"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t offset = ScriptParams[0]; + int32_t offset_signed = *cast(&offset); + offset = (offset_signed >= 0) ? (cast(script.code) + offset_signed) : (cast(script.code) - offset_signed); + *v = offset; + return true; + } + case OP_GET_FUNC_ADDR_CSTR: + { + //utils::log("OP_GET_FUNC_ADDR_CSTR"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + *v = 0; + //utils::log("func '%s' addr is 0x%X", func_name, *v); + return true; + } + case OP_CONTEXT_CALL: + { + //utils::log("OP_CONTEXT_CALL"); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t func = ScriptParams[0]; + //utils::log("call func 0x%x", func); + asm_call(script.context, func); + return true; + } + case OP_CONTEXT_SET_REG: + { + //utils::log("OP_CONTEXT_SET_REG"); + CRunningScript__CollectParameters(script.handle, 2); + uint32_t reg = ScriptParams[0]; + uint32_t val = ScriptParams[1]; + script.context[reg] = val; + return true; + } + case OP_CONTEXT_GET_REG: + { + //utils::log("OP_CONTEXT_GET_REG"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t reg = ScriptParams[0]; + *v = script.context[reg]; + return true; + } + case OP_GET_PLATFORM: + { + //utils::log("OP_GET_PLATFORM"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + *v = 2; + return true; + } + case OP_GET_GAME_VER: + { + //utils::log("OP_GET_GAME_VER"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + *v = game_ver; + return true; + } + case OP_GET_IMAGE_BASE: + { + //utils::log("OP_GET_IMAGE_BASE"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + *v = image_base; + return true; + } + case OP_READ_MEM: + { + //utils::log("OP_READ_MEM"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 3); + uint32_t addr = ScriptParams[0]; + uint32_t size = ScriptParams[1]; + uint32_t correct_ib = ScriptParams[2]; + if (correct_ib) + addr += image_base; + *v = 0; + memcpy(v, cast(addr), size); + return true; + } + case OP_WRITE_MEM: + { + //utils::log("OP_WRITE_MEM"); + CRunningScript__CollectParameters(script.handle, 5); + uint32_t addr = ScriptParams[0]; + uint32_t val = ScriptParams[1]; + uint32_t size = ScriptParams[2]; + uint32_t correct_ib = ScriptParams[3]; + uint32_t protect = ScriptParams[4]; + if (correct_ib) + addr += image_base; + memutils::mem_write_arr(cast(addr), cast(&val), size, protect); + return true; + } + case OP_SEARCH_MEM: + { + //utils::log("OP_SEARCH_MEM"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 2); + LPSTR pattern = cast(ScriptParams[0]); + uint32_t index = ScriptParams[1]; + if (!__FindPatternAddressCompact(*v, pattern, index)) + *v = 0; + //utils::log("pattern '%s' index %d addr is 0x%X", pattern, index, *v); + return true; + } + case OP_GET_GAME_VER_EX: + { + //utils::log("OP_GET_GAME_VER_EX"); + uint32_t *id = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + uint32_t *ver = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + uint32_t *ver_code = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + *id = 0; + sscanf(package_name.c_str() + 4, "%d", id); + *ver = strutils::str_hash(package_version_name); + *ver_code = package_version_code; + return true; + } + case OP_SET_MUTEX_VAR: + { + //utils::log("OP_SET_MUTEX_VAR"); + CRunningScript__CollectParameters(script.handle, 2); + uint32_t id = ScriptParams[0]; + uint32_t v = ScriptParams[1]; + if (mutex_vars.size()) + for (int32_t i = 0; i < mutex_vars.size(); i++) + if (mutex_vars.at(i).id == id) + { + mutex_vars.at(i).value = v; + return true; + } + t_mutex_var mv; + mv.id = id; + mv.value = v; + mutex_vars.push_back(mv); + return true; + } + case OP_GET_MUTEX_VAR: + { + //utils::log("OP_GET_MUTEX_VAR"); + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t id = ScriptParams[0]; + *v = 0; + if (mutex_vars.size()) + for (int32_t i = 0; i < mutex_vars.size(); i++) + if (mutex_vars.at(i).id == id) + { + *v = mutex_vars.at(i).value; + break; + } + return true; + } + case OP_CALL_FUNCTION: + { + //utils::log("OP_CALL_FUNCTION"); + CRunningScript__CollectParameters(script.handle, 2); + uint32_t addr = ScriptParams[0]; + uint32_t add_ib = ScriptParams[1]; + if (add_ib) addr += image_base; + + int params_i[8]; + memset(params_i, 0, sizeof(params_i)); + + float params_f[8]; + memset(params_f, 0, sizeof(params_f)); + + bool is_res_float = false; + + uint32_t params_a[32]; // any + memset(params_a, 0, sizeof(params_a)); + + int params_i_count = 0; + int params_f_count = 0; + int params_a_count = 0; + + void *res_ptr = NULL; + + while (true) + { + // see if this is opcode param list end + uint32_t *p_ip = cast(script.handle + select(0x10, 0x10, 0x14, 0x18, 0x10)); + ptr code = get_real_code_ptr(*p_ip); + if (*code == 0) + { + *p_ip += 1; + break; + } + + // read string type + std::string str; + if (!read_str_8byte(script.handle, str)) + { + utils::log("func call param type has to be 8byte string"); + exit(1); + } + str = strutils::str_to_lower(str); + + // param type enum + enum eParamType + { + ptNone, + ptInt, + ptFloat + }; + + // parse param + eParamType pt = ptNone; + uint32_t int_val = 0; + float float_val = 0.0f; + if (str == "i") + { + CRunningScript__CollectParameters(script.handle, 1); + int_val = ScriptParams[0]; + pt = ptInt; + } else + if (str == "f") + { + CRunningScript__CollectParameters(script.handle, 1); + float_val = *cast(&ScriptParams[0]); + pt = ptFloat; + } else + if (str == "ref") + { + int_val = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + pt = ptInt; + } else + if (str == "resi") + { + res_ptr = CRunningScript__GetPointerToScriptVariable(script.handle, 0); + } else + if (str == "resf") + { + res_ptr = CRunningScript__GetPointerToScriptVariable(script.handle, 0); + is_res_float = true; + } else + { + utils::log("func call has unknown param type '%s'", str.c_str()); + exit(1); + } + + // if this is a func param then put it into one of the func param arrays + if (pt != ptNone) + { + // verify func param limit + if (params_i_count + params_f_count + params_a_count == 32) + { + utils::log("func call has more than 32 params"); + exit(1); + } + if (pt == ptInt) + { + if (params_i_count < 8) + params_i[params_i_count++] = int_val; + else + params_a[params_a_count++] = int_val; + } else + { + if (params_f_count < 8) + params_f[params_f_count++] = float_val; + else { + void *float_ptr = &float_val; + params_a[params_a_count++] = *cast(float_ptr); + } + } + } + } + + #define PARAM_TYPES \ + uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, \ + float, float, float, float, float, float, float, float, \ + uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, \ + uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, \ + uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t + + #define PARAM_VALS \ + params_i[0], params_i[1], params_i[2], params_i[3], params_i[4], params_i[5], params_i[6], params_i[7], \ + params_f[0], params_f[1], params_f[2], params_f[3], params_f[4], params_f[5], params_f[6], params_f[7], \ + params_a[0], params_a[1], params_a[2], params_a[3], params_a[4], params_a[5], params_a[6], params_a[7], \ + params_a[8], params_a[9], params_a[10], params_a[11], params_a[12], params_a[13], params_a[14], params_a[15], \ + params_a[16], params_a[17], params_a[18], params_a[19], params_a[20], params_a[21], params_a[22], params_a[23] + + if (!is_res_float) + { + typedef uint32_t (*func_i_t)(PARAM_TYPES); + uint32_t res = cast(addr)(PARAM_VALS); + if (res_ptr) + *cast(res_ptr) = res; + } else + { + typedef float (*func_f_t)(PARAM_TYPES); + float res = cast(addr)(PARAM_VALS); + if (res_ptr) + *cast(res_ptr) = res; + } + + #undef PARAM_VALS + #undef PARAM_TYPES + + return true; + } + case OP_GET_TOUCH_POINT_STATE: + { + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 2); + *v = 0; + return true; + } + case OP_GET_TOUCH_SLIDE_STATE: + { + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 4); + *v = 0; + return true; + } + case OP_GET_MENU_BUTTON_STATE: + { + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + *v = (touch::menu_button_pressed()) ? 1 : 0; + return true; + } + case OP_GET_MENU_BUTTON_PRESSED: + { + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t mintime = ScriptParams[0]; + *v = (touch::menu_button_pressed_timed(mintime)) ? 1 : 0; + return true; + } + case OP_GET_PSP_CONTROL_STATE: + { + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t index = ScriptParams[0]; + *v = (touch::psp_control_pressed((ePspControl)index)) ? 1 : 0; + return true; + } + case OP_GET_PSP_CONTROL_PRESSED: + { + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 2); + uint32_t index = ScriptParams[0]; + uint32_t mintime = ScriptParams[1]; + *v = (touch::psp_control_pressed_timed((ePspControl)index, mintime)) ? 1 : 0; + return true; + } + case OP_INVOKABLE_SCRIPT_STATS: + { + uint32_t inv_count = 0; + if (scripts.size()) + for (int32_t i = 0; i < scripts.size(); i++) + if (scripts[i]->invokable_id != -1) + inv_count++; + uint32_t *p_count = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + uint32_t *p_page_count = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + *p_count = inv_count; + *p_page_count = inv_count / 12 + 1; + return true; + } + case OP_START_INVOKABLE_SCRIPT: + { + uint32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t id = ScriptParams[0]; + if (scripts.size()) + { + for (int32_t i = 0; i < scripts.size(); i++) + { + t_script *script = scripts[i]; + if (script->invokable_id == id) + { + // if script isn't started before + if (script->handle == NULL) + { + utils::log("starting invokable script '%s'", script->name.c_str()); + script->handle = CTheScripts__StartNewScript(script->offset); + *v = 0; + return true; + } + // if script started before and already finished + uint32_t wait_time = getfield(script->handle, select(0x7C, 0x7C, 0xEC, 0x210, 0x200)); + if (wait_time == 0xFFFFFFFF) + { + utils::log("restarting invokable script '%s'", script->name.c_str()); + // set start ip + uint32_t *p_ip = cast(script->handle + select(0x10, 0x10, 0x14, 0x18, 0x10)); + *p_ip = script->offset; + // remove wait + setfield(script->handle, select(0x7C, 0x7C, 0xEC, 0x210, 0x200), 0); + *v = 1; + return true; + } + // script is running + utils::log("invokable script '%s' already running", script->name.c_str()); + *v = 2; + return true; + } + } + } + utils::log("invokable script %d not found", id); + *v = -1; + return true; + } + case OP_SHOW_MENU_OPEN_ARROW: + { + //ui::show_arrow(); + return true; + } + case OP_HIDE_MENU_OPEN_ARROW: + { + //ui::hide_arrow(); + return true; + } + case OP_CREATE_MENU: + { + CRunningScript__CollectParameters(script.handle, 2); + uint32_t addr = ScriptParams[0]; + uint32_t item_count = ScriptParams[1]; + // read flags + uint8_t flags = *cast(addr); + bool bItemsUseGxt = flags & 1; + bool bGxtTrimToFit = (flags & 2) != 0; + uint32_t maxItemStrSize = bGxtTrimToFit ? select(20, 20, 20, 18, 19) : 0; + addr += 4; + // read title + std::string title; + while (*cast(addr)) + { + title += *cast(addr); + addr++; + } + addr++; + title = psplang::localize(title); + // read close button title + std::string titleclose; + while (*cast(addr)) + { + titleclose += *cast(addr); + addr++; + } + addr++; + titleclose = psplang::localize(titleclose); + // read items + std::vector items; + while (*cast(addr) && item_count) + { + // read item + std::string a_item; + wide_string w_item; + while (*cast(addr)) + { + a_item += *cast(addr); + w_item += *cast(addr); + addr++; + } + addr++; + bool bGxtEntryExists = false; + if (bItemsUseGxt) + { + if (uint16_t *wstr = GetGxtEntry(a_item.c_str(), true)) + { + std::string str = strutils::ansi_from_wstr(wstr); + if (str.size()) + { + if (game == GTA3) + { + if (!strstr(str.c_str(), "missing")) + { + bGxtEntryExists = true; + w_item = wstr; + } + } else + { + bGxtEntryExists = true; + w_item = wstr; + } + } + } + } + if (!bGxtEntryExists) + w_item = strutils::wstr_from_ansi(psplang::localize(a_item).c_str()); + if (bGxtEntryExists && maxItemStrSize && w_item.size() > maxItemStrSize) + w_item.erase(maxItemStrSize); + items.push_back(w_item); + item_count--; + } + // create menu + ui::create_menu(title, titleclose, items); + return true; + } + case OP_DELETE_MENU: + { + ui::delete_menu(); + return true; + } + case OP_GET_MENU_TOUCHED_ITEM_INDEX: + { + int32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + CRunningScript__CollectParameters(script.handle, 1); + uint32_t maxtime = ScriptParams[0]; // touch valid time + *v = ui::get_menu_touched_item_index(maxtime); + return true; + } + case OP_SET_MENU_ACTIVE_ITEM_INDEX: + { + CRunningScript__CollectParameters(script.handle, 1); + int32_t page_index = ScriptParams[0]; + ui::set_menu_active_item_index(page_index); + return true; + } + case OP_GET_MENU_ACTIVE_ITEM_INDEX: + { + int32_t *v = cast(CRunningScript__GetPointerToScriptVariable(script.handle, 0)); + *v = ui::get_menu_active_item_index(); + return true; + } + default: + return false; + } + } +} + + diff --git a/source/core.h b/source/core.h new file mode 100644 index 0000000..8f2bf68 --- /dev/null +++ b/source/core.h @@ -0,0 +1,47 @@ +#pragma once + +#define CLEO_ANDROID "CLEO ANDROID" +#define COPYRIGHT "(C) Alexander Blade" +#define WEBPAGE "http://dev-c.com" +#define WWW_WEBPAGE "www.dev-c.com" +#define VERSION_STR CLEO_ANDROID " (" VERSION_DATE ")" +#define VERSION_DATE "09 Oct 2024" +#define VERSION_CODE 202 +#define VERSION_CODE_STR "2.0.2" + +#include "common.h" + +namespace core +{ + enum e_game + { + GTA3, + GTAVC, + GTASA, + GTALCS, + GTAVCS + }; + + e_game GetGame(); + int GetGameVersion(); + + const char *GetPackageName(); + const char *GetPackageVersionStr(); + int GetPackageVersionCode(); + + uint16_t *GetGxtEntry(LPCSTR name, bool useCustom); + + uint32_t *GetScriptParams(); + void CRunningScript__CollectParameters(ptr thiz, uint32_t count); + ptr CRunningScript__GetPointerToScriptVariable(ptr thiz, uint8_t param); + + void initialize(); + + ptr get_real_code_ptr(uint32_t ip); + uint32_t get_ip_using_real_code_ptr(ptr realPtr); + ptr get_real_label_ptr(ptr handle, uint32_t offset); + bool read_str_8byte(ptr handle, std::string &str); + bool read_str_long(ptr handle, std::string &str); + + extern uint32_t* CTimer__m_snTimeInMilliseconds; +} diff --git a/source/core_asm.h b/source/core_asm.h new file mode 100644 index 0000000..0de1996 --- /dev/null +++ b/source/core_asm.h @@ -0,0 +1,125 @@ +#pragma once + +extern "C" __attribute__((visibility("default"))) void asm_call(uint32_t *regs, uint32_t func) +{ + __asm + (" ;\ + addiu $sp, $sp, -256 ;\ + ;\ + sw $ra, 100($sp) ;\ + ;\ + sw $a0, 4($sp) ;\ + sw $a1, 8($sp) ;\ + sw $a2, 12($sp) ;\ + sw $a3, 16($sp) ;\ + sw $t0, 20($sp) ;\ + sw $t1, 24($sp) ;\ + sw $t2, 28($sp) ;\ + sw $t3, 32($sp) ;\ + sw $12, 36($sp) ;\ + sw $13, 40($sp) ;\ + sw $14, 44($sp) ;\ + sw $15, 48($sp) ;\ + sw $t8, 52($sp) ;\ + sw $t9, 56($sp) ;\ + sw $s0, 60($sp) ;\ + sw $s1, 64($sp) ;\ + sw $s2, 68($sp) ;\ + sw $s3, 72($sp) ;\ + sw $s4, 76($sp) ;\ + sw $s5, 80($sp) ;\ + sw $s6, 84($sp) ;\ + sw $s7, 88($sp) ;\ + sw $v0, 92($sp) ;\ + sw $v1, 96($sp) ;\ + ;\ + move $t9, $a0 ;\ + ;\ + lw $a0, 0($t9) ;\ + lw $a1, 4($t9) ;\ + lw $a2, 8($t9) ;\ + lw $a3, 12($t9) ;\ + lw $t0, 16($t9) ;\ + lw $t1, 20($t9) ;\ + lw $t2, 24($t9) ;\ + lw $t3, 28($t9) ;\ + lw $12, 32($t9) ;\ + lw $13, 36($t9) ;\ + lw $14, 40($t9) ;\ + lw $15, 44($t9) ;\ + lw $t8, 48($t9) ;\ + /*lw $t9, 52($t9)*/ ;\ + lw $s0, 56($t9) ;\ + lw $s1, 60($t9) ;\ + lw $s2, 64($t9) ;\ + lw $s3, 68($t9) ;\ + lw $s4, 72($t9) ;\ + lw $s5, 76($t9) ;\ + lw $s6, 80($t9) ;\ + lw $s7, 84($t9) ;\ + lw $v0, 88($t9) ;\ + lw $v1, 92($t9) ;\ + ;\ + lw $t9, 8($sp) ;\ + jalr $t9 ;\ + nop ;\ + ;\ + lw $t9, 4($sp) ;\ + ;\ + sw $a0, 0($t9) ;\ + sw $a1, 4($t9) ;\ + sw $a2, 8($t9) ;\ + sw $a3, 12($t9) ;\ + sw $t0, 16($t9) ;\ + sw $t1, 20($t9) ;\ + sw $t2, 24($t9) ;\ + sw $t3, 28($t9) ;\ + sw $12, 32($t9) ;\ + sw $13, 36($t9) ;\ + sw $14, 40($t9) ;\ + sw $15, 44($t9) ;\ + sw $t8, 48($t9) ;\ + /*sw $t9, 52($t9)*/ ;\ + sw $s0, 56($t9) ;\ + sw $s1, 60($t9) ;\ + sw $s2, 64($t9) ;\ + sw $s3, 68($t9) ;\ + sw $s4, 72($t9) ;\ + sw $s5, 76($t9) ;\ + sw $s6, 80($t9) ;\ + sw $s7, 84($t9) ;\ + sw $v0, 88($t9) ;\ + sw $v1, 92($t9) ;\ + ;\ + lw $a0, 4($sp) ;\ + lw $a1, 8($sp) ;\ + lw $a2, 12($sp) ;\ + lw $a3, 16($sp) ;\ + lw $t0, 20($sp) ;\ + lw $t1, 24($sp) ;\ + lw $t2, 28($sp) ;\ + lw $t3, 32($sp) ;\ + lw $12, 36($sp) ;\ + lw $13, 40($sp) ;\ + lw $14, 44($sp) ;\ + lw $15, 48($sp) ;\ + lw $t8, 52($sp) ;\ + lw $t9, 56($sp) ;\ + lw $s0, 60($sp) ;\ + lw $s1, 64($sp) ;\ + lw $s2, 68($sp) ;\ + lw $s3, 72($sp) ;\ + lw $s4, 76($sp) ;\ + lw $s5, 80($sp) ;\ + lw $s6, 84($sp) ;\ + lw $s7, 88($sp) ;\ + lw $v0, 92($sp) ;\ + lw $v1, 96($sp) ;\ + ;\ + lw $ra, 100($sp) ;\ + addiu $sp, $sp, 256 ;\ + jr $ra ;\ + nop ;\ + "); + +} \ No newline at end of file diff --git a/source/core_menu.h b/source/core_menu.h new file mode 100644 index 0000000..5c0bac3 --- /dev/null +++ b/source/core_menu.h @@ -0,0 +1,151 @@ +#pragma once + +#include + +namespace core +{ + static uint8_t lcs_menu_script[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x03, 0x4D, 0x45, 0x4E, 0x55, 0x00, 0x00, + 0x29, 0x08, 0xD5, 0x0D, 0x0C, 0xDB, 0x00, 0x01, 0x39, 0x00, 0x0C, 0x07, 0x01, 0x4D, 0x00, 0x01, + 0xD3, 0xFF, 0xFF, 0xFF, 0xEA, 0x03, 0x43, 0x4C, 0x44, 0x53, 0x43, 0x00, 0x48, 0x08, 0x02, 0x00, + 0x01, 0xC9, 0xFF, 0xFF, 0xFF, 0xEA, 0x03, 0x43, 0x4C, 0x44, 0x53, 0x43, 0x4C, 0x00, 0x00, 0x01, + 0x00, 0x08, 0xD0, 0x07, 0xF0, 0x0D, 0xEE, 0x0D, 0x18, 0x0C, 0x01, 0x00, 0x01, 0xD5, 0x0D, 0x0C, + 0xDB, 0x00, 0x01, 0x39, 0x00, 0x0C, 0x07, 0x01, 0x4D, 0x00, 0x01, 0x9D, 0xFF, 0xFF, 0xFF, 0x8B, + 0x00, 0x20, 0xCE, 0x19, 0x02, 0x00, 0x01, 0x98, 0xFF, 0xFF, 0xFF, 0x8B, 0x00, 0x20, 0xCE, 0x18, + 0xDD, 0x0D, 0x0C, 0x01, 0xDB, 0x00, 0x01, 0x39, 0x00, 0x0C, 0x07, 0x01, 0x4D, 0x00, 0x01, 0x7E, + 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x01, 0x9B, 0xFE, 0xFF, 0xFF, 0xE1, 0x0D, 0x0D, 0x07, 0x04, 0x07, + 0x06, 0x01, 0x08, 0xDC, 0x05, 0xE2, 0x0D, 0x0E, 0xDB, 0x00, 0x07, 0x02, 0x39, 0x00, 0x0D, 0x01, + 0x39, 0x00, 0x0E, 0x01, 0x4D, 0x00, 0x01, 0x56, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x01, 0x9B, 0xFE, + 0xFF, 0xFF, 0xDC, 0x0D, 0x01, 0x07, 0x01, 0xF1, 0x0D, 0xD0, 0x0D, 0x0C, 0x06, 0x94, 0xFE, 0xFF, + 0xFF, 0xF2, 0x0D, 0x0C, 0x18, 0xF5, 0x0D, 0x17, 0xD5, 0x0D, 0x0C, 0xDB, 0x00, 0x07, 0x02, 0x39, + 0x00, 0x0C, 0x07, 0x01, 0x39, 0x00, 0x18, 0x01, 0x4D, 0x00, 0x01, 0x19, 0xFF, 0xFF, 0xFF, 0xEB, + 0x03, 0x01, 0x00, 0x07, 0x64, 0xEA, 0x03, 0x43, 0x4C, 0x4D, 0x4E, 0x55, 0x4E, 0x00, 0x00, 0xB9, + 0x01, 0x20, 0x01, 0x01, 0x00, 0x07, 0x32, 0x78, 0x03, 0x01, 0x00, 0x01, 0xF4, 0x0D, 0x16, 0x07, + 0x64, 0xDB, 0x00, 0x07, 0x02, 0x29, 0x00, 0x16, 0x01, 0x2D, 0x80, 0x16, 0x18, 0x4D, 0x00, 0x01, + 0xCA, 0xFE, 0xFF, 0xFF, 0xEB, 0x03, 0x01, 0x00, 0x07, 0x64, 0xEF, 0x0D, 0x0C, 0x16, 0xDB, 0x00, + 0x01, 0x39, 0x00, 0x0C, 0x07, 0x02, 0x4D, 0x00, 0x01, 0xD1, 0xFE, 0xFF, 0xFF, 0xEA, 0x03, 0x43, + 0x4C, 0x4D, 0x4E, 0x55, 0x31, 0x00, 0x00, 0x02, 0x00, 0x01, 0xA9, 0xFE, 0xFF, 0xFF, 0xDB, 0x00, + 0x01, 0x39, 0x00, 0x16, 0x07, 0xFE, 0x4D, 0x00, 0x01, 0xB0, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x07, + 0x64, 0x02, 0x00, 0x01, 0xA9, 0xFE, 0xFF, 0xFF, 0x02, 0x00, 0x01, 0x0F, 0xFF, 0xFF, 0xFF, 0xF6, + 0x0D, 0x17, 0xF3, 0x0D, 0xB9, 0x01, 0x20, 0x07, 0x01, 0xDC, 0x0D, 0x01, 0x01, 0x02, 0x00, 0x01, + 0xBE, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x43, 0x4C, 0x45, 0x4F, 0x20, 0x4D, 0x45, 0x4E, + 0x55, 0x00, 0x43, 0x4C, 0x4F, 0x53, 0x45, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x30, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, + 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x33, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x31, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x35, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x31, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x31, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x30, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x32, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x32, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x34, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x32, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, + 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x39, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x33, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x31, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x33, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x33, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x36, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x38, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x33, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x30, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x34, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, + 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x35, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x34, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x37, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x34, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x35, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x32, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x34, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x35, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x36, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x35, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, + 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x31, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x36, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x33, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x36, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x36, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x38, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x30, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x37, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x32, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x37, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, + 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x37, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x37, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x39, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x38, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x38, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x34, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x36, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x38, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x38, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x38, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, + 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x33, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x39, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x35, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x39, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x39, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, + 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, + 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, + 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, + 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x37, 0x00, 0x00 + }; + + static uint8_t vcs_menu_script[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x02, 0x0A, 0x4D, 0x45, 0x4E, 0x55, 0x00, + 0x67, 0x02, 0x0A, 0x43, 0x4C, 0x44, 0x53, 0x43, 0x00, 0x01, 0x00, 0x08, 0xD0, 0x07, 0xF0, 0x0D, + 0xEE, 0x0D, 0x19, 0x0D, 0x01, 0x00, 0x01, 0xD5, 0x0D, 0x0D, 0x78, 0x00, 0x01, 0x1B, 0x00, 0x0D, + 0x07, 0x01, 0x22, 0x00, 0x01, 0xC8, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x01, 0xC3, 0xFF, 0xFF, 0xFF, + 0x04, 0x00, 0x21, 0xD0, 0x0E, 0xDD, 0x0D, 0x0D, 0x01, 0x78, 0x00, 0x01, 0x1B, 0x00, 0x0D, 0x07, + 0x01, 0x22, 0x00, 0x01, 0xA9, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0x01, 0xED, 0xFE, 0xFF, 0xFF, 0xE1, + 0x0D, 0x0E, 0x07, 0x04, 0x07, 0x06, 0x01, 0x08, 0xDC, 0x05, 0xE2, 0x0D, 0x0F, 0x78, 0x00, 0x07, + 0x02, 0x1B, 0x00, 0x0E, 0x01, 0x1B, 0x00, 0x0F, 0x01, 0x22, 0x00, 0x01, 0x81, 0xFF, 0xFF, 0xFF, + 0x02, 0x00, 0x01, 0xED, 0xFE, 0xFF, 0xFF, 0xDC, 0x0D, 0x01, 0x07, 0x01, 0xF1, 0x0D, 0xD0, 0x0D, + 0x0D, 0x06, 0xE6, 0xFE, 0xFF, 0xFF, 0xF2, 0x0D, 0x0D, 0x19, 0xF5, 0x0D, 0x18, 0x07, 0x01, 0x21, + 0x01, 0x01, 0x00, 0x07, 0x32, 0x21, 0x02, 0x01, 0x00, 0x01, 0xF4, 0x0D, 0x17, 0x07, 0x64, 0x78, + 0x00, 0x07, 0x02, 0x15, 0x00, 0x17, 0x01, 0x17, 0x80, 0x17, 0x19, 0x22, 0x00, 0x01, 0x1C, 0xFF, + 0xFF, 0xFF, 0x68, 0x02, 0x01, 0x00, 0x07, 0x64, 0xEF, 0x0D, 0x0D, 0x17, 0x78, 0x00, 0x01, 0x1B, + 0x00, 0x0D, 0x07, 0x02, 0x22, 0x00, 0x01, 0x23, 0xFF, 0xFF, 0xFF, 0x67, 0x02, 0x0A, 0x43, 0x4C, + 0x4D, 0x4E, 0x55, 0x31, 0x00, 0x02, 0x00, 0x01, 0xFB, 0xFE, 0xFF, 0xFF, 0x78, 0x00, 0x01, 0x1B, + 0x00, 0x17, 0x07, 0xFE, 0x22, 0x00, 0x01, 0x02, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x07, 0x64, 0x02, + 0x00, 0x01, 0xFB, 0xFE, 0xFF, 0xFF, 0x02, 0x00, 0x01, 0x61, 0xFF, 0xFF, 0xFF, 0xF6, 0x0D, 0x18, + 0xF3, 0x0D, 0x07, 0x01, 0x21, 0x07, 0x01, 0xDC, 0x0D, 0x01, 0x01, 0x02, 0x00, 0x01, 0xE4, 0xFF, + 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x43, 0x4C, 0x45, 0x4F, 0x20, 0x4D, 0x45, 0x4E, 0x55, 0x00, + 0x43, 0x4C, 0x4F, 0x53, 0x45, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x31, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x31, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x33, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x31, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x31, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x38, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x30, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x32, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x32, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x32, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, + 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x37, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x32, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x32, 0x39, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x33, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x33, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x34, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x36, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x33, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x33, 0x38, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x33, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, + 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x33, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x34, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x35, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x34, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x34, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x34, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x30, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x32, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x35, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x34, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x35, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, + 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x35, 0x39, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x36, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x31, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x36, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x36, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x36, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x36, 0x38, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x36, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x30, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x37, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, + 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x35, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x37, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x37, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x37, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x37, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x38, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x32, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x34, 0x00, 0x43, + 0x53, 0x49, 0x5F, 0x38, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x36, 0x00, 0x43, 0x53, 0x49, + 0x5F, 0x38, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, 0x38, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x38, + 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x30, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x31, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x39, 0x32, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x33, 0x00, 0x43, 0x53, + 0x49, 0x5F, 0x39, 0x34, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, + 0x39, 0x36, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x37, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x38, + 0x00, 0x43, 0x53, 0x49, 0x5F, 0x39, 0x39, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x30, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x31, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x32, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x33, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x34, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x35, 0x00, 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x36, 0x00, + 0x43, 0x53, 0x49, 0x5F, 0x31, 0x30, 0x37, 0x00, 0x00 + }; +} \ No newline at end of file diff --git a/source/libres.cpp b/source/libres.cpp new file mode 100644 index 0000000..0afdb63 --- /dev/null +++ b/source/libres.cpp @@ -0,0 +1,37 @@ +#include "libres.h" +#include "utils.h" +#include "pattern.h" + +using namespace core; + +namespace libres +{ + uint32_t getGpValue() + { + unsigned int gp; + asm( + "move %0, $gp\n" + : "=r"(gp) + ); + return gp; + } + + bool init(e_game& game, int32_t& image_base) + { + uint32_t addr; + if (__FindPatternAddress(addr, "52 00 02 3C ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 00 00 00 ?? ?? ?? ?? 00 00 00 00 ?? ?? ?? ?? 48 00 10 3C")) + { + game = GTAVCS; + image_base = 0; + return true; + } + else if (__FindPatternAddress(addr, "2D 20 40 00 63 00 04 3C")) + { + game = GTALCS; + image_base = 0; + return true; + } + + return false; + } +} diff --git a/source/libres.h b/source/libres.h new file mode 100644 index 0000000..ddc7342 --- /dev/null +++ b/source/libres.h @@ -0,0 +1,10 @@ +#pragma once + +#include "common.h" +#include "core.h" + +namespace libres +{ + uint32_t getGpValue(); + bool init(core::e_game &game, int32_t &image_base); +} diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..9882d21 --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,44 @@ +#include "core.h" +#include "utils.h" + +#include "../includes/pcsx2/pcsx2f_api.h" +#include "../includes/pcsx2/nanoprintf.h" +#include "../includes/pcsx2/log.h" +#include "../includes/pcsx2/memalloc.h" + +int CompatibleCRCList[] = { + (int)0x4F32A11F, (int)0xB3AD1EA4, // vcs + (int)0x7EA439F5, (int)0xD693D4CF, // lcs +}; + +char OSDText[OSDStringNum][OSDStringSize] = { {1} }; + +#define STR(x) STR2(x) +#define STR2(x) #x + +extern "C" const char VERSION[] = "[|VERSION]" VERSION_DATE ";" STR(VERSION_CODE) ";" VERSION_CODE_STR "[/VERSION|]"; + +#ifdef __cplusplus +void* operator new(size_t size) +{ + void* p = AllocMemBlock(size); + return p; +} + +void operator delete(void* p) +{ + FreeMemBlock(p); +} +#endif + +extern "C" void init() +{ + logger.SetBuffer(OSDText, sizeof(OSDText) / sizeof(OSDText[0]), sizeof(OSDText[0])); + + core::initialize(); +} + +int main() +{ + return 0; +} diff --git a/source/makefile b/source/makefile new file mode 100644 index 0000000..4b6c5fe --- /dev/null +++ b/source/makefile @@ -0,0 +1,22 @@ +EE_BIN = ../data/PLUGINS/cleo.elf +EE_OBJS = main.o ../includes/pcsx2/memalloc.o ../includes/pcsx2/log.o utils.o text.o libres.o armhook.o touch.o ui.o pattern.o core.o mutex.o strutils.o memutils.o psplang.o + +CFLAGS = -O0 -Os -G0 -Wall -fshort-wchar -fno-pic -mno-check-zero-division -fpack-struct=16 +CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti +ASFLAGS = $(CFLAGS) + +BASE_ADDRESS = 0x05000000 +EE_LINKFILE = linkfile +EE_LIBS += -l:libstdc++.a -l:libm.a +EE_LDFLAGS = -Wl,--entry=init -Wl,--no-relax -Wl,-Map,../data/PLUGINS/cleo.map -Wl,'--defsym=BASE_ADDRESS=$(BASE_ADDRESS)' + +all: clean main-build + +main-build: $(EE_BIN) + +clean: + rm -f $(EE_OBJS) $(EE_BIN) + +PS2SDK = ../external/ps2sdk/ps2sdk +include $(PS2SDK)/samples/Makefile.pref +include $(PS2SDK)/samples/Makefile.eeglobal diff --git a/source/memutils.cpp b/source/memutils.cpp new file mode 100644 index 0000000..de46f65 --- /dev/null +++ b/source/memutils.cpp @@ -0,0 +1,27 @@ +#include "memutils.h" + +namespace memutils +{ + void mem_write_arr(uint8_t *addr, uint8_t *arr, uint32_t size, bool protect) + { + for (int i = 0; i < size; i++) + addr[i] = arr[i]; + } + + ptr mem_read_mips_jmp(uint8_t *addr) + { + return cast((*cast(addr) & 0x03FFFFFF) << 2); + } + + void mem_write_mips_jmp(uint8_t *addrFrom, uint8_t *addrTo, bool withNop) + { + uint64_t code = 0x08000000 | ((cast(addrTo) >> 2) & 0x03FFFFFF); + mem_write_arr(addrFrom, cast(&code), withNop ? 8 : 4); + } + + void mem_write_mips_call(uint8_t *addrFrom, uint8_t *addrTo, bool withNop) + { + uint64_t code = 0x0C000000 | ((cast(addrTo) >> 2) & 0x03FFFFFF); + mem_write_arr(addrFrom, cast(&code), withNop ? 8 : 4); + } +} \ No newline at end of file diff --git a/source/memutils.h b/source/memutils.h new file mode 100644 index 0000000..1b94173 --- /dev/null +++ b/source/memutils.h @@ -0,0 +1,15 @@ +#pragma once + +#include "common.h" + +namespace memutils +{ + void mem_write_arr(uint8_t *addr, uint8_t *arr, uint32_t size, bool protect = true); + + // read addr from mips jmp/call + ptr mem_read_mips_jmp(uint8_t *addr); + // addrFrom must divide by 4 without remainder + void mem_write_mips_jmp(uint8_t *addrFrom, uint8_t *addrTo, bool withNop); + // addrFrom must divide by 4 without remainder + void mem_write_mips_call(uint8_t *addrFrom, uint8_t *addrTo, bool withNop); +} \ No newline at end of file diff --git a/source/mutex.cpp b/source/mutex.cpp new file mode 100644 index 0000000..f10b397 --- /dev/null +++ b/source/mutex.cpp @@ -0,0 +1,27 @@ +#include "mutex.h" + +namespace mutex +{ + //SceUID mutex[msize]; + + void init() + { + //memset(mutex, 0, sizeof(mutex)); + //for (int i = 0; i < msize; i++) + //{ + // char name[32]; + // sprintf(name, "CleoMutex_%d", i); + // mutex[i] = sceKernelCreateSema(name, 0, 1, 1, NULL); + //} + } + + void enter(eMutex m) + { + //sceKernelWaitSema(mutex[m], 1, 0); + } + + void leave(eMutex m) + { + //sceKernelSignalSema(mutex[m], 1); + } +} \ No newline at end of file diff --git a/source/mutex.h b/source/mutex.h new file mode 100644 index 0000000..f84b20a --- /dev/null +++ b/source/mutex.h @@ -0,0 +1,26 @@ +#pragma once + +#include "common.h" + +namespace mutex +{ + enum eMutex + { + mlog, + mplugin, + + msize + }; + + void init(); + void enter(eMutex m); + void leave(eMutex m); +} + +#define CS_SCOPE(m) \ + struct cs_t \ + { \ + cs_t() { mutex::enter(m); } \ + ~cs_t() { mutex::leave(m); } \ + } __cs; + diff --git a/source/pattern.cpp b/source/pattern.cpp new file mode 100644 index 0000000..a8ea1a0 --- /dev/null +++ b/source/pattern.cpp @@ -0,0 +1,8 @@ +#define FIND_PATTERN_ADDR_COMPACT + +#include "pattern.h" + +bool __FindPatternAddressCompact(void *&result, const char *lpszPattern, int index) +{ + return __FindPatternAddress(result, lpszPattern, index); +} \ No newline at end of file diff --git a/source/pattern.h b/source/pattern.h new file mode 100644 index 0000000..a174399 --- /dev/null +++ b/source/pattern.h @@ -0,0 +1,167 @@ +#pragma once + +#include "common.h" +#include "libres.h" + +template +#ifndef __INTELLISENSE__ +__attribute__((__always_inline__)) +#endif +inline bool __FindPatternAddress(T &result, const char *lpszPattern, int index = 0) +{ + __STATIC_ASSERT(sizeof(T) == sizeof(uint32_t)); + + struct pattern_byte_t + { + uint8_t index; + uint8_t value; + }; + + pattern_byte_t pattern_bytes[64]; + int pattern_byte_count = 0; + uint8_t byte_index = 0; + + int str_current_char_index = 0; + +#define ACH \ + if (!lpszPattern || !lpszPattern[str_current_char_index]) \ + break; \ + else \ + while (true) \ + { \ + char c1 = lpszPattern[str_current_char_index]; \ + \ + if (c1 == ' ') \ + { \ + str_current_char_index++; \ + break; \ + } \ + \ + char c2 = lpszPattern[str_current_char_index + 1]; \ + \ + if (!c2 || ((c1 == '?' || c2 == '?') && c1 != c2)) \ + return false; \ + \ + if (c1 == '?') \ + { \ + byte_index++; \ + str_current_char_index += 2; \ + break; \ + } \ + \ + /* to upper */ \ + if (c1 >= 'a' && c1 <= 'f') c1 -= 'a' - 'A'; \ + if (c2 >= 'a' && c2 <= 'f') c2 -= 'a' - 'A'; \ + \ + bool c1_in_bounds = (c1 >= '0' && c1 <= '9') || (c1 >= 'A' && c1 <= 'F'); \ + bool c2_in_bounds = (c2 >= '0' && c2 <= '9') || (c2 >= 'A' && c2 <= 'F'); \ + \ + if (!c1_in_bounds || !c2_in_bounds) \ + return false; \ + \ + uint8_t c1_value = c1 >= '0' && c1 <= '9' ? c1 - '0' : c1 - 'A' + 0xA; \ + uint8_t c2_value = c2 >= '0' && c2 <= '9' ? c2 - '0' : c2 - 'A' + 0xA; \ + \ + uint8_t byte_value = (c1_value << 4) | c2_value; \ + \ + if (pattern_byte_count == sizeof(pattern_bytes) / sizeof(pattern_bytes[0])) \ + return false; \ + \ + pattern_bytes[pattern_byte_count].index = byte_index; \ + pattern_bytes[pattern_byte_count].value = byte_value; \ + \ + pattern_byte_count++; \ + \ + byte_index++; \ + str_current_char_index += 2; \ + \ + break; \ + } + + while (true) + { +#ifndef FIND_PATTERN_ADDR_COMPACT + + // pattern string is larger than the pattern ! + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + ACH; ACH; ACH; ACH; ACH; ACH; ACH; ACH; + +#endif + + while (true) + ACH; + + break; + } + +#undef ACH + + if (!pattern_byte_count) + return false; + + void *imageBase = (void*)0x100000; + uint8_t* addrStart = (uint8_t*)imageBase; + uint8_t* addrMax = (uint8_t*)0xFFFFFF; + for (uint8_t* addr = addrStart; addr < addrMax; addr++) + { + bool mismatch = false; + +#define CMPB { \ + if (i >= pattern_byte_count) \ + break; \ + if (addr[pattern_bytes[i].index] != pattern_bytes[i].value) \ + { \ + mismatch = true; \ + break; \ + } \ + i++; \ + } + + int i = 0; + while (true) + { +#ifndef FIND_PATTERN_ADDR_COMPACT + + CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; + CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; + CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; + CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; CMPB; + +#endif + + while (true) + CMPB; + + break; + } + +#undef CMPB + + if (!mismatch) + { + if (!index) + { + result = (T)addr; + return true; + } + index--; + } + } // single section + + return false; +} + +bool __FindPatternAddressCompact(void *&result, const char *lpszPattern, int index = 0); + +template +inline bool __FindPatternAddressCompact(T &result, const char *lpszPattern, int index = 0) +{ + return __FindPatternAddressCompact(cast(result), lpszPattern, index); +} \ No newline at end of file diff --git a/source/psplang.cpp b/source/psplang.cpp new file mode 100644 index 0000000..3a99c5d --- /dev/null +++ b/source/psplang.cpp @@ -0,0 +1,80 @@ +#include "psplang.h" +#include "core.h" +#include "strutils.h" + +namespace psplang +{ + bool bInit = false; + + bool is_init() + { + return bInit; + } + + bool bVcsRus1 = false; + + bool is_vcs_rus1() + { + return bVcsRus1; + } + + bool bLcsRus1 = false; + + bool is_lcs_rus1() + { + return bLcsRus1; + } + + void init() + { + if (bInit) + return; + + uint16_t *lang_loading_str = core::GetGxtEntry("LOADING", false); + + if (!lang_loading_str) + return; + + switch (core::GetGame()) + { + case core::GTALCS: + { + if (*lang_loading_str == 0x0033) + bLcsRus1 = true; + break; + } + case core::GTAVCS: + { + if (*lang_loading_str == 0x0033) + bVcsRus1 = true; + break; + } + } + + bInit = true; + } + + std::string localize(std::string str) + { + if (!bInit) + return str; + + if (bVcsRus1 || bLcsRus1) + { + for (int i = 0; i < str.size(); i++) + { + char c = str[i]; + if (c >= 'A' && c <= 'Z') + c += 0x20; + str[i] = c; + } + + str = strutils::str_replace(str, "cleo android", "Cleo Android"); + str = strutils::str_replace(str, "(c) alexander blade", "(C) Alexander Blade"); + if (bLcsRus1) + str = strutils::str_replace(str, "(C)", " (C)"); + } + + return str; + } +} \ No newline at end of file diff --git a/source/psplang.h b/source/psplang.h new file mode 100644 index 0000000..323b90b --- /dev/null +++ b/source/psplang.h @@ -0,0 +1,10 @@ +#include "common.h" + +namespace psplang +{ + bool is_init(); + bool is_vcs_rus1(); + bool is_lcs_rus1(); + void init(); + std::string localize(std::string str); +} \ No newline at end of file diff --git a/source/strutils.cpp b/source/strutils.cpp new file mode 100644 index 0000000..b10aa4c --- /dev/null +++ b/source/strutils.cpp @@ -0,0 +1,138 @@ +#include "strutils.h" + +namespace strutils +{ + std::string ansi_from_wstr(uint16_t *w) + { + std::string res; + while (*w) + { + res += static_cast(*w); + w++; + } + return res; + } + + wide_string wstr_from_ansi(const char *a) + { + wide_string res; + while (*a) + { + res += static_cast(*a); + a++; + } + return res; + } + + void wstr_from_ansi(uint16_t *w, const char *a) + { + int i = 0; + while (a[i]) + { + w[i] = a[i]; + i++; + } + w[i] = 0; + } + + std::string str_to_lower(std::string str) + { + for (int i = 0; i < (int)str.size(); i++) + str[i] = tolower(str[i]); + return str; + } + + uint32_t str_hash(std::string astr) + { + const char *str = astr.c_str(); + uint32_t len = strlen(str); + if (!len) return 0; + uint32_t hash, i; + for (hash = i = 0; i < len; ++i) + { + hash += str[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + + std::string str_replace(std::string subject, const std::string& search, const std::string& replace) + { + size_t pos = 0; + while ((pos = subject.find(search, pos)) != std::string::npos) + { + subject.replace(pos, search.length(), replace); + pos += replace.length(); + } + return subject; + } + + std::string replace_slashes(std::string str) + { + std::string res; + char prev_char = 0; + for (int i = 0; i < (int)str.size(); i++) + { + char c = str[i]; + if (c == '\\') c = '/'; + if (c == '/' && prev_char == '/') + continue; + res += (prev_char = c); + } + return res; + } + + std::string path_normalize(std::string path, eTrailingSlashPolicy tsp) + { + if (path.size()) + { + path = replace_slashes(path); + if (path.size()) + { + bool has_trailing_slash = path[path.size() - 1] == '/'; + switch (tsp) + { + case tspMake: + if (!has_trailing_slash) + path += '/'; + break; + case tspClear: + if (has_trailing_slash) + path = path.substr(0, path.size() - 1); + break; + } + } + } + return path; + } + + std::string get_ext(std::string path, bool lower) + { + size_t pos = path.find_last_of("."); + return pos != std::string::npos && pos < path.size() - 1 ? + lower ? str_to_lower(path.substr(pos + 1)) : path.substr(pos + 1) : + ""; + } + + std::string get_parent_path(std::string path, bool lower) + { + path = path_normalize(path); + size_t pos = path.find_last_of("/"); + return pos != std::string::npos && pos < path.size() - 1 ? + lower ? str_to_lower(path.substr(0, pos)) : path.substr(0, pos) : + ""; + } + + std::string get_filename(std::string path, bool lower) + { + path = path_normalize(path); + size_t pos = path.find_last_of("/"); + return pos != std::string::npos && pos < path.size() - 1 ? + lower ? str_to_lower(path.substr(pos + 1)) : path.substr(pos + 1) : + ""; + } +} diff --git a/source/strutils.h b/source/strutils.h new file mode 100644 index 0000000..09e4439 --- /dev/null +++ b/source/strutils.h @@ -0,0 +1,30 @@ +#pragma once + +#include "common.h" + +namespace strutils +{ + std::string ansi_from_wstr(uint16_t *w); + wide_string wstr_from_ansi(const char *a); + void wstr_from_ansi(uint16_t *w, const char *a); + + std::string str_to_lower(std::string str); + + uint32_t str_hash(std::string astr); + + std::string str_replace(std::string subject, const std::string& search, const std::string& replace); + std::string replace_slashes(std::string str); + + enum eTrailingSlashPolicy + { + tspDontCare, + tspMake, + tspClear + }; + + std::string path_normalize(std::string path, eTrailingSlashPolicy tsp = tspDontCare); + + std::string get_ext(std::string path, bool lower = true); + std::string get_parent_path(std::string path, bool lower = true); + std::string get_filename(std::string path, bool lower = true); +} \ No newline at end of file diff --git a/source/text.cpp b/source/text.cpp new file mode 100644 index 0000000..e665b27 --- /dev/null +++ b/source/text.cpp @@ -0,0 +1,130 @@ +#include "text.h" +#include "core.h" +#include "utils.h" +#include "strutils.h" +#include "psplang.h" + +using namespace strutils; + +namespace text +{ + const char CSI_ENTRY[] = "CSI_%d"; + + std::map gxt_entries; + + void init() + { + for (std::map::iterator i = gxt_entries.begin(); i != gxt_entries.end(); i++) + delete[] i->second; + gxt_entries.clear(); + // startup string replacement shown in GTA3 and GTAVC + add_gxt_entry("SPLASH", VERSION_STR); + // script menu + //add_gxt_entry("CLDSC", VERSION_STR "~n~" COPYRIGHT "~n~" WEBPAGE); // SA, LCS, VCS PSP + //add_gxt_entry("CLDSCL", VERSION_STR " " COPYRIGHT " " WWW_WEBPAGE); // LCS PSP + add_gxt_entry("CLMNU", "cleo menu"); + add_gxt_entry("CLMNUD", "Touch screen up or down to select a script, center to start it"); + add_gxt_entry("CLMNUN", "You have no scripts for menu, are you sure the game has STORAGE access permission?"); + add_gxt_entry("CLMNU0", "No such script !"); + add_gxt_entry("CLMNU1", "Script is already running !"); + add_gxt_entry("CLMNU2", " Touch screen left or right to select script, center to start, any other screen part to dismiss"); + add_gxt_entry("CLMNUL", "LEFT"); + add_gxt_entry("CLMNUT", "UP"); + add_gxt_entry("CLMNUB", "DOWN"); + add_gxt_entry("CLMNUR", "RIGHT"); + add_gxt_entry("CLMNUS", "DISMISS"); + for (int32_t i = 0; i < 108; i++) + { + char str[16]; + sprintf(str, CSI_ENTRY, i); + add_gxt_entry(str, "-"); + } + } + + void add_gxt_entry(std::string name, std::string str) + { + str = psplang::localize(str); + uint16_t *w = new uint16_t[str.length() + 1]; + uint32_t hash = str_hash(name); + wstr_from_ansi(w, str.c_str()); + gxt_entries[hash] = w; + } + + void set_gxt_invokable_script_name(uint32_t num, std::string str) + { + if (gxt_entries.size()) + { + char name[64]; + sprintf(name, CSI_ENTRY, num); + uint32_t hash = str_hash(name); + if (gxt_entries.find(hash) != gxt_entries.end()) + { + str = str_to_lower(str); + str = str_replace(str, "gta3", ""); + str = str_replace(str, "gtavcs", ""); + str = str_replace(str, "gtavc", ""); + str = str_replace(str, "gtasa", ""); + str = str_replace(str, "gtalcs", ""); + str = str_replace(str, "csi", ""); + str = str_replace(str, ".", ""); + str = str_replace(str, "_", " "); + str = psplang::localize(str); + uint16_t *w = new uint16_t[str.length() + 1]; + wstr_from_ansi(w, str.c_str()); + delete[] gxt_entries[hash]; + gxt_entries[hash] = w; + } + } + } + + uint16_t *get_gxt_entry(std::string name) + { + uint32_t hash = str_hash(name); + if (gxt_entries.find(hash) != gxt_entries.end()) + return gxt_entries[hash]; + return NULL; + } + + void load_gxt_entries_from_text(LPSTR text, uint32_t size) + { + // read all lines + std::string line; + int32_t i = 0; + while (i < size) + { + if (text[i] == '\r' || text[i] == '\n' || i == size - 1) + { + if (i == size - 1) + line += text[i]; + if (line.size() > 3) + { + char *str = (char *)line.c_str(); + if (str[0] >= 'A' && str[0] <= 'z') + { + char *space = strstr(str, "\x20"); + if (space != NULL && str < space) + { + std::string e_name; + while (str < space) + { + e_name += *str; + str++; + } + str++; + if (line.size() > e_name.size() + 1) + { + //utils::log("'%s' '%s'", e_name.c_str(), str); + add_gxt_entry(e_name, std::string(str)); + } + } + } + line.clear(); + } + } else + { + line += text[i]; + } + i++; + } + } +} diff --git a/source/text.h b/source/text.h new file mode 100644 index 0000000..b04cbd5 --- /dev/null +++ b/source/text.h @@ -0,0 +1,12 @@ +#pragma once + +#include "common.h" + +namespace text +{ + void init(); + void add_gxt_entry(std::string name, std::string str); + void set_gxt_invokable_script_name(uint32_t num, std::string str); + uint16_t *get_gxt_entry(std::string name); + void load_gxt_entries_from_text(LPSTR text, uint32_t size); +} diff --git a/source/touch.cpp b/source/touch.cpp new file mode 100644 index 0000000..9bbe8c0 --- /dev/null +++ b/source/touch.cpp @@ -0,0 +1,84 @@ +#include "touch.h" +#include "utils.h" +#include "ui.h" + +namespace touch +{ + fn_CPad__GetPad CPad__GetPad; + + bool menu_button_state = false; + uint32_t menu_button_event_time = 0; + + void menu_button_event(bool state) + { + if (menu_button_state != state) + menu_button_event_time = utils::get_tick_count(); + menu_button_state = state; + } + + bool menu_button_pressed() + { + return menu_button_state; + } + + bool menu_button_pressed_timed(uint32_t mintime) + { + return menu_button_state && menu_button_event_time + mintime <= utils::get_tick_count(); + } + + bool ctrl_state[CTRL_SIZE]; + uint32_t ctrl_disabled[CTRL_SIZE]; + uint32_t ctrl_time[CTRL_SIZE]; + + void control_event(ePspControl control, bool state) + { + if (ctrl_state[control] != state) + ctrl_time[control] = utils::get_tick_count(); + if (!state) + ctrl_disabled[control] = 0; + ctrl_state[control] = state; + } + + void psp_input_event() + { + CPad* Pad = touch::CPad__GetPad(0); + + menu_button_event((Pad->NewState.START) != 0); + ////if (menu_button_state && !menu_button_pressed_timed(2000)) + //// pad_data->Buttons &= ~(uint32_t)PSP_CTRL_START; + // + + control_event(CTRL_SELECT, (Pad->NewState.SELECT) != 0); + control_event(CTRL_UP, (Pad->NewState.DPADUP) != 0); + control_event(CTRL_RIGHT, (Pad->NewState.DPADRIGHT) != 0); + control_event(CTRL_DOWN, (Pad->NewState.DPADDOWN) != 0); + control_event(CTRL_LEFT, (Pad->NewState.DPADLEFT) != 0); + control_event(CTRL_LTRIGGER, (Pad->NewState.LEFTSHOULDER1) != 0); + control_event(CTRL_RTRIGGER, (Pad->NewState.RIGHTSHOULDER2) != 0); + control_event(CTRL_TRIANGLE, (Pad->NewState.TRIANGLE) != 0); + control_event(CTRL_CIRCLE, (Pad->NewState.CIRCLE) != 0); + control_event(CTRL_CROSS, (Pad->NewState.CROSS) != 0); + control_event(CTRL_SQUARE, (Pad->NewState.SQUARE) != 0); + //control_event(CTRL_HOLD, (Pad->NewState.HOLD) != 0); + + control_event(CTRL_STICK_UP, Pad->NewState.LEFTSTICKY < -10); + control_event(CTRL_STICK_DOWN, Pad->NewState.LEFTSTICKY > 10); + control_event(CTRL_STICK_RIGHT, Pad->NewState.LEFTSTICKX > 10); + control_event(CTRL_STICK_LEFT, Pad->NewState.LEFTSTICKX < -10); + } + + bool psp_control_pressed(ePspControl control) + { + return control < CTRL_SIZE && ctrl_state[control] && ctrl_disabled[control] <= utils::get_tick_count(); + } + + bool psp_control_pressed_timed(ePspControl control, uint32_t mintime) + { + return control < CTRL_SIZE && ctrl_state[control] && ctrl_disabled[control] <= utils::get_tick_count() && ctrl_time[control] + mintime <= utils::get_tick_count(); + } + + void psp_control_disable(ePspControl control, uint32_t mintime) + { + ctrl_disabled[control] = utils::get_tick_count() + mintime; + } +} diff --git a/source/touch.h b/source/touch.h new file mode 100644 index 0000000..ed55120 --- /dev/null +++ b/source/touch.h @@ -0,0 +1,90 @@ +#pragma once + +#include "common.h" + +struct CControllerState +{ + short LEFTSTICKX; + short LEFTSTICKY; + short RIGHTSTICKX; + short RIGHTSTICKY; + + short LEFTSHOULDER1; + short LEFTSHOULDER2; + short RIGHTSHOULDER1; + short RIGHTSHOULDER2; + + short DPADUP; + short DPADDOWN; + short DPADLEFT; + short DPADRIGHT; + + short unk1; + short unk2; + short unk3; + short unk4; + + short START; + short SELECT; + + short SQUARE; + short TRIANGLE; + short CROSS; + short CIRCLE; + + short LEFTSHOCK; + short RIGHTSHOCK; +}; + +struct CPad +{ + char unk[2]; + + struct CControllerState NewState; + struct CControllerState OldState; + + char pad[48]; + + short Mode; //146 + short ShakeDur; + short DisablePlayerControls; + //... +}; + +enum ePspControl +{ + CTRL_SELECT, + CTRL_UP, + CTRL_RIGHT, + CTRL_DOWN, + CTRL_LEFT, + CTRL_LTRIGGER, + CTRL_RTRIGGER, + CTRL_TRIANGLE, + CTRL_CIRCLE, + CTRL_CROSS, + CTRL_SQUARE, + CTRL_HOLD, + + CTRL_STICK_UP, + CTRL_STICK_DOWN, + CTRL_STICK_RIGHT, + CTRL_STICK_LEFT, + + CTRL_SIZE +}; + +namespace touch +{ + typedef CPad* (*fn_CPad__GetPad)(int a1); + extern fn_CPad__GetPad CPad__GetPad; + + void psp_input_event(); + bool psp_control_pressed(ePspControl control); + bool psp_control_pressed_timed(ePspControl control, uint32_t mintime); + void psp_control_disable(ePspControl control, uint32_t mintime); + + void menu_button_event(bool state); + bool menu_button_pressed(); + bool menu_button_pressed_timed(uint32_t mintime); +} diff --git a/source/ui.cpp b/source/ui.cpp new file mode 100644 index 0000000..e2fff9e --- /dev/null +++ b/source/ui.cpp @@ -0,0 +1,802 @@ +#include "ui.h" +#include "armhook.h" +#include "utils.h" +#include "pattern.h" +#include "touch.h" +#include "strutils.h" +#include "memutils.h" +#include "psplang.h" + +namespace ui +{ + typedef void (*fn_Draw_Poly)(float, float, float, float, float, float, float, float, uint8_t *); + fn_Draw_Poly draw_poly; + + enum eAlign + { + eAlignCenter, + eAlignLeft, + eAlignRight + }; + + enum eStyle + { + eStyleSimple, + eStyleClassic, + eStyleSpecial + }; + + typedef void (*fn_Print_String)(uint16_t *str, float x, float y, eAlign align, float scalex, float scaley, uint8_t *rgba, eStyle style); + fn_Print_String print_string; + + void on_draw(); + + uint8_t menu_arrow_color[4]; + uint8_t menu_arrow_selected_alpha; + uint8_t menu_active_item_font_color[4]; + uint8_t menu_selected_item_font_color[4]; + + namespace lcs + { + // @CSprite2d::Draw2DPolygon + typedef void (*fn_CSprite2d__Draw2DPolygon)(float, float, float, float, float, float, float, float, uint8_t *); + fn_CSprite2d__Draw2DPolygon CSprite2d__Draw2DPolygon; + + // @CHud::Draw + typedef void (*fn_CHud__Draw)(); + fn_CHud__Draw _CHud__Draw, CHud__Draw_; + void CHud__Draw() + { + CHud__Draw_(); + on_draw(); + } + + // @CFont::Details + #pragma pack(push, 1) + struct _CFont__Details + { + uint32_t m_dwColor; // 0x00 + float m_fScaleX; // 0x04 + float m_fScaleY; // 0x08 + float m_fSlant; // 0x0C + float m_fSlantRefPointX; // 0x10 + float m_fSlantRefPointY; // 0x14 + bool m_bLeftJustify; // 0x18 + bool m_bCentre; // 0x19 + bool m_bRightJustify; // 0x1A + bool m_bBackground; // 0x1B + bool m_bBackGroundOnlyText; // 0x1C + bool m_bProp; // 0x1D + bool m_bUnk0; // 0x1E + bool m_bFlash; // 0x1F + uint32_t m_dwUnk1; // 0x20 + float m_fAlphaFade; // 0x24 + uint32_t m_dwBackgroundColor; // 0x28 + float m_fWrapx; // 0x2C + float m_fCentreSize; // 0x30 + float m_fRightJustifyWrap; // 0x34 + uint16_t m_wFontStyle; // 0x38 + bool m_bFontStyle_unk; // 0x3A + bool m_bUnk2; // 0x3B + uint32_t m_dwUnk3; // 0x3C + uint16_t m_wDropShadowPosition; // 0x40 + uint16_t m_wUnkPad; // 0x42 + uint32_t m_dwDropColor; // 0x44 + uint32_t m_dwUnk4[4]; // 0x48 + uint32_t m_dwOutlineColor; // 0x58 + uint32_t m_bOutlineOn; // 0x5C + uint32_t m_dwNewLineAdd; // 0x60 + } *CFont__Details; + #pragma pack(pop) + STRUCT_SIZE(_CFont__Details, 0x64); + + // @CFont::SetFontStyle + typedef void (*fn_CFont__SetFontStyle)(uint16_t); + fn_CFont__SetFontStyle CFont__SetFontStyle; + + // @CFont::PrintString + typedef void (*fn_CFont__PrintString)(float, float, uint16_t *, float *); + fn_CFont__PrintString CFont__PrintString; + + float trans_x(float x) { return x * 480.0; } + float trans_y(float y) { return y * 272.0; } + + void _draw_poly(float topleftx, float toplefty, float toprightx, float toprighty, + float bottomleftx, float bottomlefty, float bottomrightx, float bottomrighty, uint8_t *rgba) + { + //CSprite2d__Draw2DPolygon(trans_x(topleftx), trans_y(toplefty), trans_x(toprightx), trans_y(toprighty), + // trans_x(bottomleftx), trans_y(bottomlefty), trans_x(bottomrightx), trans_y(bottomrighty), rgba); + + int a0 = (int)rgba; + float f12 = trans_x(topleftx); + float f13 = trans_y(toplefty); + float f14 = trans_x(toprightx); + float f15 = trans_y(toprighty); + float f16 = trans_x(bottomleftx); + float f17 = trans_y(bottomlefty); + float f18 = trans_x(bottomrightx); + float f19 = trans_y(bottomrighty); + + asm volatile ("lw $v0, %[x]" ::[x] "m" (a0)); + asm volatile ("move $a0, $v0"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f12)); + asm volatile ("mtc1 $v0, $f12"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f13)); + asm volatile ("mtc1 $v0, $f13"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f14)); + asm volatile ("mtc1 $v0, $f14"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f15)); + asm volatile ("mtc1 $v0, $f15"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f16)); + asm volatile ("mtc1 $v0, $f16"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f17)); + asm volatile ("mtc1 $v0, $f17"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f18)); + asm volatile ("mtc1 $v0, $f18"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f19)); + asm volatile ("mtc1 $v0, $f19"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (CSprite2d__Draw2DPolygon)); + asm volatile ("jalr $v0"); + } + + void _print_string(uint16_t *str, float x, float y, eAlign align, float scalex, float scaley, uint8_t *rgba, eStyle style) + { + // save current font settings + _CFont__Details *fd = CFont__Details; + _CFont__Details fdbcp = *fd; + + // set font align + fd->m_bCentre = false; + fd->m_bLeftJustify = false; + fd->m_bRightJustify = false; + switch (align) + { + case eAlignCenter: + fd->m_bCentre = true; + break; + case eAlignLeft: + fd->m_bLeftJustify = true; + break; + case eAlignRight: + fd->m_bRightJustify = true; + break; + } + // style + switch (style) + { + case eStyleClassic: + CFont__SetFontStyle(2); + scalex *= 0.8f; + scaley *= 0.72f; + break; + case eStyleSimple: + case eStyleSpecial: + CFont__SetFontStyle(2); + scalex *= 0.75f; + scaley *= 0.65f; + break; + } + + // scale + fd->m_fScaleX = scalex; + fd->m_fScaleY = scaley; + // color + fd->m_dwColor = *cast(rgba); + // common + fd->m_bBackground = false; + fd->m_bFlash = false; + fd->m_bProp = true; + fd->m_wDropShadowPosition = 1; + fd->m_dwDropColor = 0xFF000000; + + if (psplang::is_lcs_rus1()) + { + CFont__SetFontStyle(1); + scalex *= 0.92; + scaley *= 0.83; + if (style == eStyleClassic) + y -= 0.01; + fd->m_fScaleX = scalex; + fd->m_fScaleY = scaley; + fd->m_wDropShadowPosition = 0; + fd->m_dwDropColor = 0xFF000000; + } + + // print string + //CFont__PrintString(trans_x(x), trans_y(y), str, NULL); + + int a0 = (int)str; + int a1 = (int)NULL; + float f12 = trans_x(x); + float f13 = trans_y(y); + + asm volatile ("lw $v0, %[x]" ::[x] "m" (a0)); + asm volatile ("move $a0, $v0"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (a1)); + asm volatile ("move $a1, $v0"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f12)); + asm volatile ("mtc1 $v0, $f12"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f13)); + asm volatile ("mtc1 $v0, $f13"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (CFont__PrintString)); + asm volatile ("jalr $v0"); + + // restore font settings + *fd = fdbcp; + } + + void init() + { + uint32_t addr; + + #define PATTERN_NOT_FOUND { utils::log("required ui pattern %d not found!", __LINE__); exit(1); } + #define FIND_PATTERN(...) if (!__FindPatternAddress(addr, __VA_ARGS__)) PATTERN_NOT_FOUND; + + #define READ_ADDR(addr1, addr2) ((static_cast(*cast(addr1)) << 16) | *cast(addr2)) + #define READ_ADDR_INDIRECT(addr1, addr2) (READ_ADDR(addr1, addr2) - 0x10000) + + // @CSprite2d::Draw2DPolygon + FIND_PATTERN("82 94 01 46 C2 9C 00 46"); + CSprite2d__Draw2DPolygon = cast(addr - 16); + utils::log("CSprite2d__Draw2DPolygon: 0x%08X", CSprite2d__Draw2DPolygon); + + // @CHud::Draw + FIND_PATTERN("01 00 06 24 D8 04 B1 FF"); + _CHud__Draw = cast(addr - 12); + armhook::hook_mips_func(_CHud__Draw, 8, CHud__Draw, &CHud__Draw_); + utils::log("_CHud__Draw: 0x%08X", _CHud__Draw); + + // @CFont::Details + FIND_PATTERN("00 00 A5 93 01 00 A2 93"); + CFont__Details = cast<_CFont__Details*>(READ_ADDR_INDIRECT(addr - 24, addr - 16)); + utils::log("CFont__Details: 0x%08X", CFont__Details); + + // @CFont::SetFontStyle + FIND_PATTERN("00 24 04 00 02 00 02 24"); + CFont__SetFontStyle = cast(addr); + + // @CFont::PrintString + FIND_PATTERN("50 00 B6 FF 2D 88 80 00"); + CFont__PrintString = cast(addr - 8); + + draw_poly = _draw_poly; + print_string = _print_string; + + uint8_t color[4] = {40, 235, 24, 255}; + memcpy(menu_selected_item_font_color, color, sizeof(color)); + uint8_t color_a[4] = {225, 216, 161, 255}; + memcpy(menu_active_item_font_color, color_a, sizeof(color_a)); + uint8_t arrow_color[4] = { 55, 127, 175, 110 }; + memcpy(menu_arrow_color, arrow_color, sizeof(arrow_color)); + menu_arrow_selected_alpha = 220; + } + } + + namespace vcs + { + // @CSprite2d::Draw2DPolygon + typedef void (*fn_CSprite2d__Draw2DPolygon)(float, float, float, float, float, float, float, float, uint8_t *); + fn_CSprite2d__Draw2DPolygon CSprite2d__Draw2DPolygon; + + // @CFont::SetFontStyle + typedef void (*fn_CFont__SetFontStyle)(uint32_t); + fn_CFont__SetFontStyle CFont__SetFontStyle; + + // @CFont::Unknown_1 + typedef void (*fn_CFont__SetFontPreset1)(); + fn_CFont__SetFontPreset1 CFont__SetFontPreset1; + + // @CFont::SetUnkFloat + typedef void (*fn_CFont__SetFontScale)(float); + fn_CFont__SetFontScale CFont__SetFontScale; + + // @CFont::SetFontAlign + enum eFontAlign + { + faNone = 0, + faLeft = 1, + faCenter = 2, + faRight = 4 + }; + typedef void (*fn_CFont__SetFontAlign)(eFontAlign); + fn_CFont__SetFontAlign CFont__SetFontAlign; + + // @CFont::SetColor + typedef void (*fn_CFont__SetColor)(uint8_t *rgba); + fn_CFont__SetColor CFont__SetColor; + + // @CFont::PrintString + typedef void (*fn_CFont__PrintString)(uint16_t *, int, int); + fn_CFont__PrintString CFont__PrintString; + + // @CHud::Draw + typedef void (*fn_CHud__Draw)(void *); + fn_CHud__Draw _CHud__Draw, CHud__Draw_; + void CHud__Draw(void *thiz) + { + CHud__Draw_(thiz); + on_draw(); + } + + float trans_x(float x) { return x * 480.0; } + float trans_y(float y) { return y * 272.0; } + + void _draw_poly(float topleftx, float toplefty, float toprightx, float toprighty, + float bottomleftx, float bottomlefty, float bottomrightx, float bottomrighty, uint8_t *rgba) + { + //CSprite2d__Draw2DPolygon(trans_x(topleftx), trans_y(toplefty), trans_x(toprightx), trans_y(toprighty), + // trans_x(bottomleftx), trans_y(bottomlefty), trans_x(bottomrightx), trans_y(bottomrighty), rgba); + + int a0 = (int)rgba; + float f12 = trans_x(topleftx); + float f13 = trans_y(toplefty); + float f14 = trans_x(toprightx); + float f15 = trans_y(toprighty); + float f16 = trans_x(bottomleftx); + float f17 = trans_y(bottomlefty); + float f18 = trans_x(bottomrightx); + float f19 = trans_y(bottomrighty); + + asm volatile ("lw $v0, %[x]" ::[x] "m" (a0)); + asm volatile ("move $a0, $v0"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f12)); + asm volatile ("mtc1 $v0, $f12"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f13)); + asm volatile ("mtc1 $v0, $f13"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f14)); + asm volatile ("mtc1 $v0, $f14"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f15)); + asm volatile ("mtc1 $v0, $f15"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f16)); + asm volatile ("mtc1 $v0, $f16"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f17)); + asm volatile ("mtc1 $v0, $f17"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f18)); + asm volatile ("mtc1 $v0, $f18"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (f19)); + asm volatile ("mtc1 $v0, $f19"); + asm volatile ("lw $v0, %[x]" ::[x] "m" (CSprite2d__Draw2DPolygon)); + asm volatile ("jalr $v0"); + } + + void _print_string(uint16_t *str, float x, float y, eAlign align, float scalex, float scaley, uint8_t *rgba, eStyle style) + { + + eFontAlign font_align = faNone; + switch (align) + { + case eAlignCenter: + font_align = faCenter; + break; + case eAlignLeft: + font_align = faLeft; + break; + case eAlignRight: + font_align = faRight; + break; + } + + int font_style = 0; + switch (style) + { + case eStyleClassic: + font_style = 2; + scaley *= 0.72f * 0.4f; + break; + case eStyleSimple: + if (scalex == 0.4f && scaley == 0.8f) // page index + { + font_style = 2; + font_align = faNone; + x -= 0.02; + scaley *= 0.67f * 0.4f; + } else + { + font_style = 2; + scaley *= 0.67f * 0.3f; + } + break; + case eStyleSpecial: + font_style = 0; + break; + } + + if (psplang::is_vcs_rus1()) + { + font_style = 1; + if (style == eStyleClassic) scaley *= 1.8; else + if (style == eStyleSimple) scaley *= 1.6; + } + + CFont__SetFontStyle(font_style); + CFont__SetFontPreset1(); + CFont__SetFontScale(scaley); + CFont__SetFontAlign(font_align); + CFont__SetColor(rgba); + + CFont__PrintString(str, (int)trans_x(x), (int)trans_y(y)); + } + + void init() + { + uint32_t addr; + + // @CSprite2d::Draw2DPolygon + FIND_PATTERN("49 00 03 3C 49 00 02 3C ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 00 B0 FF C2 9C 00 46"); + CSprite2d__Draw2DPolygon = cast(addr - 0); + utils::log("CSprite2d__Draw2DPolygon: 0x%08X", CSprite2d__Draw2DPolygon); + + // @CHud::Draw + FIND_PATTERN("28 00 B1 FF 2D 80 80 00 30 00 B2 FF 38 00 B3 FF 40 00 B4 FF 48 00 B5 FF"); // reads gp related addr + _CHud__Draw = cast(addr - 8); + armhook::hook_mips_func(_CHud__Draw, 8, CHud__Draw, &CHud__Draw_); + + // @CFont::PrintString + FIND_PATTERN("18 00 B3 FF 2D 90 80 00 20 00 B4 FF 2D 98 A0 00 00 00 B0 FF"); + CFont__PrintString = cast(addr - 8); + + #define READ_REL3_ADDR(offset) memutils::mem_read_mips_jmp(cast(addr) + offset) + + FIND_PATTERN("2D 80 A0 00 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 02 00 04 24"); + + // @CFont::SetFontStyle + CFont__SetFontStyle = cast(READ_REL3_ADDR(12)); + + // @CFont::SetFontPreset1 + CFont__SetFontPreset1 = cast(READ_REL3_ADDR(20)); + + // @CFont::SetFontAlign + CFont__SetFontAlign = cast(READ_REL3_ADDR(28)); + + FIND_PATTERN("02 00 04 24 0C 2C A8 92"); + + // @CFont::SetColor + CFont__SetColor = cast(READ_REL3_ADDR(28)); + + FIND_PATTERN("01 00 04 24 ?? ?? ?? ?? 00 00 00 00 19 3F 01 3C 9A 99 21 34 00 60 81 44 ?? ?? ?? ?? 00 00 00 00 50 00 A2 8E"); + + // @CFont::SetFontScale + CFont__SetFontScale = cast(READ_REL3_ADDR(24)); + + draw_poly = _draw_poly; + print_string = _print_string; + + uint8_t color[4] = { 40, 235, 24, 255 }; + memcpy(menu_selected_item_font_color, color, sizeof(color)); + uint8_t color_a[4] = { 225, 216, 161, 255 }; + memcpy(menu_active_item_font_color, color_a, sizeof(color_a)); + uint8_t arrow_color[4] = { 55, 127, 175, 110 }; + memcpy(menu_arrow_color, arrow_color, sizeof(arrow_color)); + menu_arrow_selected_alpha = 220; + } + } + + // arrow + + static uint16_t unistr[512]; + uint16_t *ansi_to_unicode(LPCSTR str, bool localize = false) + { + std::string localized; + if (localize) + { + localized = psplang::localize(str); + str = localized.c_str(); + } + strutils::wstr_from_ansi(unistr, str); + return unistr; + } + + // draws arrow, length is 0..1 + void draw_arrow(float length) + { + uint8_t arrow_color[4] = { 255, 10, 10, 190 }; + float width = 0.08, top = 0.15, left = 0.5 - width / 2, h = length / 1.75; + draw_poly(left, top, left + width, top, left, top + h, left + width, top + h, arrow_color); + float aextent = 0.027, awidth = 0.12; + draw_poly(left - aextent, top + h, left + width + aextent, top + h, 0.5, top + h + awidth, 0.5, top + h + awidth, arrow_color); + uint8_t font_color[4] = { 252, 250, 250, 255 }; + + print_string(ansi_to_unicode("OPEN CLEO MENU", true), 0.5, top - 0.05, eAlignCenter, 0.85, 2.1, font_color, eStyleClassic); + print_string(ansi_to_unicode("PSP: PRESS START", true), 0.5, top + 0.1, eAlignCenter, 0.85, 2.1, font_color, eStyleClassic); + print_string(ansi_to_unicode("TO OPEN GAME MENU HOLD START", true), 0.5, top + 0.19, eAlignCenter, 0.5, 1.3, font_color, eStyleClassic); + } + + uint32_t arrow_draw_until_time; // time in ticks til arrow must be drawn + uint32_t arrow_frames; // indicates arrow current draw stage + + // shows arrow, called from scripts + void show_arrow() + { + static bool bShowArrow = true; // arrow shows only at start + if (bShowArrow) + { + bShowArrow = false; + const uint32_t arrow_draw_time = 17000; + arrow_draw_until_time = utils::get_tick_count() + arrow_draw_time; + arrow_frames = 0; + } + } + + // hides arrow, called from scripts + void hide_arrow() + { + arrow_draw_until_time = 0; + arrow_frames = 0; + } + + // menu + + std::string menu_name; // title + std::string menu_close_name; // close button title + std::vector menu_items; // item names + int32_t menu_active_page = -1; // active page index, -1 when menu is not active + int32_t menu_active_item = -1; // active item index in menu_items + int32_t menu_page_count; // total page count, set on create + int32_t menu_selected_item; // selected item index in menu_items, updated on touch handle + uint32_t menu_selected_item_time; // time in ticks when menu_selected_item was selected + const int32_t menu_items_per_page = 8; // max items per page + int32_t menu_arrow_left_highlight_frames_left; // frames to draw left arrow highlighted + int32_t menu_arrow_right_highlight_frames_left; // frames to draw right arrow highlighted + int32_t menu_item_highlight_frames_left; // frames to draw selected item highlighted + const int32_t menu_highlight_frames = 3; // frame count for highlight to be active + // updated on draw + float menu_close_points[4]; // close button rect coords [x1,y1,x2,y2] + float menu_arrow_left_points[8]; // left arrow coords for checking touches [x1,y1,x2,y2] * 4 + float menu_arrow_right_points[8]; // right arrow coords for checking touches [x1,y1,x2,y2] * 4 + float menu_item_points[menu_items_per_page * 4]; // item coords for checking touches [x1,y1,x2,y2] * menu_items_per_page + uint32_t menu_item_points_current; // current page item count + uint32_t hud_draw_time; + + // creates menu, called from scripts + void create_menu(std::string name, std::string closename, std::vector &items) + { + menu_name = name; + menu_close_name = closename; + menu_items = items; + menu_active_page = 0; + menu_active_item = 0; + menu_selected_item = -1; + menu_selected_item_time = 0; + menu_arrow_left_highlight_frames_left = 0; + menu_arrow_right_highlight_frames_left = 0; + menu_item_highlight_frames_left = 0; + menu_page_count = menu_items.size() / menu_items_per_page; + if (menu_items.size() % menu_items_per_page) + menu_page_count++; + } + + // get menu touched item index + // resets touch state + // returns -1 if no menu touch occured, -2 for close button, 0 and above for item indexes + // maxtime - touch valid time + int32_t get_menu_touched_item_index(uint32_t maxtime) + { + if (utils::get_tick_count() < menu_selected_item_time + maxtime) + { + // utils::log("menu_selected_item %d", menu_selected_item); + menu_selected_item_time = 0; // reset touch state + return menu_selected_item; + } + return -1; + } + + // deletes menu, called from scripts or on restart + void delete_menu() + { + menu_active_page = -1; + menu_active_item = -1; + menu_selected_item = -1; + menu_selected_item_time = 0; + menu_page_count = 0; + } + + void set_menu_active_item_index(int32_t index) + { + if (index >= 0 && index < menu_items.size()) + { + menu_active_item = index; + menu_active_page = menu_active_item / menu_items_per_page; + } + } + + int32_t get_menu_active_item_index() + { + return menu_active_item; + } + + void handle_psp_controls() + { + if (menu_active_page == -1 || menu_active_item == -1 || hud_draw_time + 150 < utils::get_tick_count()) return; + + bool up = menu_page_count > 0 ? touch::psp_control_pressed(CTRL_UP) || touch::psp_control_pressed(CTRL_STICK_UP) : false; + bool down = menu_page_count > 0 ? touch::psp_control_pressed(CTRL_DOWN) || touch::psp_control_pressed(CTRL_STICK_DOWN) : false; + bool cross = menu_page_count > 0 ? touch::psp_control_pressed(CTRL_CROSS) : false; + bool right = menu_page_count > 1 ? touch::psp_control_pressed(CTRL_RIGHT) || touch::psp_control_pressed(CTRL_STICK_RIGHT) : false; + bool left = menu_page_count > 1 ? touch::psp_control_pressed(CTRL_LEFT) || touch::psp_control_pressed(CTRL_STICK_LEFT) : false; + bool circle = touch::psp_control_pressed(CTRL_CIRCLE) || touch::menu_button_pressed_timed(1900); + + int32_t start_item = menu_active_page * menu_items_per_page; + int32_t menu_items_this_page = (start_item + menu_items_per_page > menu_items.size()) ? menu_items.size() - start_item : menu_items_per_page; + + if (up) + { + touch::psp_control_disable(CTRL_UP, 150); + touch::psp_control_disable(CTRL_STICK_UP, 150); + menu_active_item--; + if (menu_active_item < start_item) + menu_active_item = start_item + menu_items_this_page - 1; + } else + if (down) + { + touch::psp_control_disable(CTRL_DOWN, 150); + touch::psp_control_disable(CTRL_STICK_DOWN, 150); + menu_active_item++; + if (menu_active_item == start_item + menu_items_this_page) + menu_active_item = start_item; + } else + if (right || left) + { + if (right) + { + touch::psp_control_disable(CTRL_RIGHT, 200); + touch::psp_control_disable(CTRL_STICK_RIGHT, 200); + menu_arrow_right_highlight_frames_left = menu_highlight_frames; + menu_active_page++; + if (menu_active_page == menu_page_count) + menu_active_page = 0; + } else + { + touch::psp_control_disable(CTRL_LEFT, 200); + touch::psp_control_disable(CTRL_STICK_LEFT, 200); + menu_arrow_left_highlight_frames_left = menu_highlight_frames; + menu_active_page--; + if (menu_active_page == -1) + menu_active_page = menu_page_count - 1; + } + int32_t current_page_item = menu_active_item % menu_items_per_page; + start_item = menu_active_page * menu_items_per_page; + menu_active_item = start_item + current_page_item; + if (menu_active_item >= menu_items.size()) + menu_active_item = menu_items.size() - 1; + } else + if (cross) + { + touch::psp_control_disable(CTRL_CROSS, 200); + menu_selected_item = menu_active_item; + menu_selected_item_time = utils::get_tick_count(); + menu_item_highlight_frames_left = menu_highlight_frames; + } else + if (circle) + { + touch::psp_control_disable(CTRL_CIRCLE, 200); + menu_selected_item = -2; + menu_selected_item_time = utils::get_tick_count(); + } + } + + // menu must be active in order this to be called + void draw_menu() + { + // draw rect + float width = 0.3, height = 0.8, top = 0.12, left = 0.5 - width / 2; + uint8_t rect_color[4] = { 55, 127, 175, 150 }; + draw_poly(left, top, left + width, top, left, top + height, left + width, top + height, rect_color); + // draw title + uint8_t font_color[4] = { 252, 250, 250, 255 }; + print_string(ansi_to_unicode(menu_name.c_str()), 0.5, top - 0.03, eAlignCenter, 0.8, 1.8, font_color, eStyleClassic); + + // draw page num if needed + if (menu_page_count > 1) + { + char str[32]; + sprintf(str, "%d of %d", menu_active_page + 1, menu_page_count); + print_string(ansi_to_unicode(str), 0.61, top + 0.04, eAlignCenter, 0.4f, 0.8f, font_color, eStyleSimple); + } + // draw items + if (menu_active_page < 0 || menu_active_page > menu_page_count - 1) + menu_active_page = 0; + int32_t start_item = menu_active_page * menu_items_per_page; + int32_t menu_items_this_page = (start_item + menu_items_per_page > menu_items.size()) ? menu_items.size() - start_item : menu_items_per_page; + top = 0.2; + menu_item_points_current = menu_items_this_page; + for (int32_t i = 0; i < menu_items_this_page; i++) + { + uint8_t font_color[4] = { 252, 250, 250, 255 }; + if (menu_selected_item == start_item + i && menu_item_highlight_frames_left) + { + memcpy(font_color, menu_selected_item_font_color, sizeof(font_color)); + menu_item_highlight_frames_left--; + } + + if (menu_active_item == start_item + i && !menu_item_highlight_frames_left) // menu_selected_item != menu_active_item + memcpy(font_color, menu_active_item_font_color, sizeof(font_color)); + + print_string((uint16_t *)menu_items[start_item + i].c_str(), 0.5, top, eAlignCenter, 0.5, 1.5, font_color, eStyleSimple); + top += 0.01; + menu_item_points[i * 4] = 0.5 - width / 2.5; + menu_item_points[i * 4 + 1] = top; + top += 0.07; + menu_item_points[i * 4 + 2] = 0.5 + width / 2.5; + menu_item_points[i * 4 + 3] = top; + top += 0.01; + } + // draw arrows if needed + if (menu_page_count > 1) + { + // left arrow + float awidth = 0.08, aheight = 0.2, aextent = 0.015, right = left + width; + uint8_t arrow_left_color[4]; + memcpy(arrow_left_color, menu_arrow_color, sizeof(menu_arrow_color)); + if (menu_arrow_left_highlight_frames_left) + { + arrow_left_color[3] = menu_arrow_selected_alpha; + menu_arrow_left_highlight_frames_left--; + } + menu_arrow_left_points[0] = left - awidth - aextent; + menu_arrow_left_points[1] = 0.5; + menu_arrow_left_points[2] = left - aextent; + menu_arrow_left_points[3] = 0.5 - aheight / 2; + menu_arrow_left_points[4] = left - aextent; + menu_arrow_left_points[5] = 0.5 + aheight / 2; + menu_arrow_left_points[6] = left - awidth - aextent; + menu_arrow_left_points[7] = 0.5; + draw_poly(menu_arrow_left_points[0], menu_arrow_left_points[1], menu_arrow_left_points[2], menu_arrow_left_points[3], + menu_arrow_left_points[6], menu_arrow_left_points[7], + menu_arrow_left_points[4], menu_arrow_left_points[5], + arrow_left_color); + // right arrow + uint8_t arrow_right_color[4]; + memcpy(arrow_right_color, menu_arrow_color, sizeof(menu_arrow_color)); + if (menu_arrow_right_highlight_frames_left) + { + arrow_right_color[3] = menu_arrow_selected_alpha; + menu_arrow_right_highlight_frames_left--; + } + menu_arrow_right_points[0] = right + awidth + aextent; + menu_arrow_right_points[1] = 0.5; + menu_arrow_right_points[2] = right + aextent; + menu_arrow_right_points[3] = 0.5 - aheight / 2; + menu_arrow_right_points[4] = right + aextent; + menu_arrow_right_points[5] = 0.5 + aheight / 2; + menu_arrow_right_points[6] = right + awidth + aextent; + menu_arrow_right_points[7] = 0.5; + draw_poly(menu_arrow_right_points[0], menu_arrow_right_points[1], menu_arrow_right_points[2], menu_arrow_right_points[3], + menu_arrow_right_points[6], menu_arrow_right_points[7], + menu_arrow_right_points[4], menu_arrow_right_points[5], + arrow_right_color); + } + } + + // common + void on_draw() + { + // arrow + uint32_t time = utils::get_tick_count(); + hud_draw_time = time; + if (time < arrow_draw_until_time || arrow_frames > 0) + { + const int32_t arrow_frames_max = 40, arrow_freeze_frames_max = 20; + + draw_arrow(arrow_frames < arrow_frames_max ? (float)arrow_frames / (float)arrow_frames_max : 1.0); + + if (arrow_frames++ == arrow_frames_max + arrow_freeze_frames_max) + arrow_frames = 0; + } + // menu + if (menu_active_page != -1) + draw_menu(); + } + + void init() + { + switch (core::GetGame()) + { + case core::GTALCS: + lcs::init(); + break; + case core::GTAVCS: + vcs::init(); + break; + } + } +} diff --git a/source/ui.h b/source/ui.h new file mode 100644 index 0000000..fa78a9a --- /dev/null +++ b/source/ui.h @@ -0,0 +1,18 @@ +#pragma once + +#include "common.h" +#include "core.h" + +namespace ui +{ + void init(); + + void show_arrow(); + void hide_arrow(); + void create_menu(std::string name, std::string closename, std::vector &items); + int32_t get_menu_touched_item_index(uint32_t maxtime); + void delete_menu(); + void set_menu_active_item_index(int32_t index); + int32_t get_menu_active_item_index(); + void handle_psp_controls(); +} diff --git a/source/utils.cpp b/source/utils.cpp new file mode 100644 index 0000000..a0e7464 --- /dev/null +++ b/source/utils.cpp @@ -0,0 +1,73 @@ +#include "utils.h" +#include "mutex.h" +#include "core.h" + +#include "../includes/pcsx2/log.h" + +namespace utils +{ + uint32_t get_tick_count() + { + return *core::CTimer__m_snTimeInMilliseconds; + } + + //FILE *log_file = NULL; + + void log(const char *fmt, ...) + { + //uncomment for log + //CS_SCOPE(mutex::mlog); + // + //char str[256]; + //va_list lst; + //va_start(lst, fmt); + //vsprintf(str, fmt, lst); + //va_end(lst); + // + //logger.WriteF("%s", str); + } + + bool string_compare(const std::string &left, const std::string &right) + { + for (std::string::const_iterator lit = left.begin(), rit = right.begin(); lit != left.end() && rit != right.end(); ++lit, ++rit) + if (tolower(*lit) < tolower(*rit)) + return true; + else if (tolower(*lit) > tolower(*rit)) + return false; + if (left.size() < right.size()) + return true; + return false; + } + + bool list_files_in_dir(std::string dir, std::vector &files) + { + //int fd = sceIoDopen(dir.c_str()); + //if (fd < 0) return false; + //SceIoDirent dirp; + //memset(&dirp, 0, sizeof(dirp)); + //while (sceIoDread(fd, &dirp) > 0) + // if((dirp.d_stat.st_attr & FIO_SO_IFDIR) == 0) + // files.push_back(std::string(dirp.d_name)); + //sceIoDclose(fd); + // + //if (files.size()) + // sort(files.begin(), files.end(), string_compare); + //return true; + } + + uint8_t *load_binary_file(std::string filename, uint32_t &size) + { + //FILE *file = fopen(filename.c_str(), "r"); + //if (!file) return NULL; + //fseek(file, 0, SEEK_END); + //size = ftell(file); + //fseek(file, 0, SEEK_SET); + //uint8_t *buf = cast(malloc(size)); + //fread(buf, 1, size, file); + //fclose(file); + //return buf; + } + +} + + diff --git a/source/utils.h b/source/utils.h new file mode 100644 index 0000000..42b487b --- /dev/null +++ b/source/utils.h @@ -0,0 +1,15 @@ +#pragma once + +#include "common.h" + +namespace utils +{ + uint32_t get_tick_count(); + + void log(const char *fmt, ...); + + bool list_files_in_dir(std::string dir, std::vector &files); + uint8_t *load_binary_file(std::string filename, uint32_t &size); +} + +