1
0
Fork 0

Merge branch 'master' into timetable-clear-all-fix

pull/13978/head
DrewJenn 2025-06-18 09:59:33 -05:00 committed by GitHub
commit 7212fece8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
826 changed files with 24312 additions and 20856 deletions

View File

@ -50,7 +50,6 @@ Describe here
Some things are not automated, and forgotten often. This list is a reminder for the reviewers. Some things are not automated, and forgotten often. This list is a reminder for the reviewers.
* The bug fix is important enough to be backported? (label: 'backport requested') * The bug fix is important enough to be backported? (label: 'backport requested')
* This PR touches english.txt or translations? Check the [guidelines](https://github.com/OpenTTD/OpenTTD/blob/master/docs/eints.md) * This PR touches english.txt or translations? Check the [guidelines](https://github.com/OpenTTD/OpenTTD/blob/master/docs/eints.md)
* This PR affects the save game format? (label 'savegame upgrade')
* This PR affects the GS/AI API? (label 'needs review: Script API') * This PR affects the GS/AI API? (label 'needs review: Script API')
* ai_changelog.hpp, game_changelog.hpp need updating. * ai_changelog.hpp, game_changelog.hpp need updating.
* The compatibility wrappers (compat_*.nut) need updating. * The compatibility wrappers (compat_*.nut) need updating.

View File

@ -158,7 +158,7 @@ def scan_source_files(path, strings_found):
# Most files we can just open, but some use magic, that requires the # Most files we can just open, but some use magic, that requires the
# G++ preprocessor before we can make sense out of it. # G++ preprocessor before we can make sense out of it.
if new_path == "src/table/cargo_const.h": if new_path == "src/table/cargo_const.h":
p = subprocess.run(["g++", "-E", new_path], stdout=subprocess.PIPE) p = subprocess.run(["g++", "-E", "-DCHECK_UNUSED_STRINGS", new_path], stdout=subprocess.PIPE)
output = p.stdout.decode() output = p.stdout.decode()
else: else:
with open(new_path) as fp: with open(new_path) as fp:

View File

@ -37,12 +37,10 @@ Both 'stable' and 'nightly' versions are available for download:
OpenTTD is also available for free on [Steam](https://store.steampowered.com/app/1536610/OpenTTD/), [GOG.com](https://www.gog.com/game/openttd), and the [Microsoft Store](https://www.microsoft.com/p/openttd-official/9ncjg5rvrr1c). On some platforms OpenTTD will be available via your OS package manager or a similar service. OpenTTD is also available for free on [Steam](https://store.steampowered.com/app/1536610/OpenTTD/), [GOG.com](https://www.gog.com/game/openttd), and the [Microsoft Store](https://www.microsoft.com/p/openttd-official/9ncjg5rvrr1c). On some platforms OpenTTD will be available via your OS package manager or a similar service.
## 1.2) OpenTTD gameplay manual ## 1.2) OpenTTD gameplay manual
OpenTTD has a [community-maintained wiki](https://wiki.openttd.org/), including a gameplay manual and tips. OpenTTD has a [community-maintained wiki](https://wiki.openttd.org/), including a gameplay manual and tips.
## 1.3) Supported platforms ## 1.3) Supported platforms
OpenTTD has been ported to several platforms and operating systems. OpenTTD has been ported to several platforms and operating systems.
@ -56,6 +54,7 @@ The currently supported platforms are:
Other platforms may also work (in particular various BSD systems), but we don't actively test or maintain these. Other platforms may also work (in particular various BSD systems), but we don't actively test or maintain these.
### 1.3.1) Legacy support ### 1.3.1) Legacy support
Platforms, languages and compilers change. Platforms, languages and compilers change.
We'll keep support going on old platforms as long as someone is interested in supporting them, except where it means the project can't move forward to keep up with language and compiler features. We'll keep support going on old platforms as long as someone is interested in supporting them, except where it means the project can't move forward to keep up with language and compiler features.
@ -72,7 +71,6 @@ For some platforms these will be downloaded during the installation process if r
For some platforms, you will need to refer to [the installation guide](https://wiki.openttd.org/en/Manual/Installation). For some platforms, you will need to refer to [the installation guide](https://wiki.openttd.org/en/Manual/Installation).
### 1.4.1) Free graphics and sound files ### 1.4.1) Free graphics and sound files
The free data files, split into OpenGFX for graphics, OpenSFX for sounds and The free data files, split into OpenGFX for graphics, OpenSFX for sounds and
@ -85,7 +83,6 @@ OpenMSX for music can be found at:
Please follow the readme of these packages about the installation procedure. Please follow the readme of these packages about the installation procedure.
The Windows installer can optionally download and install these packages. The Windows installer can optionally download and install these packages.
### 1.4.2) Original Transport Tycoon Deluxe graphics and sound files ### 1.4.2) Original Transport Tycoon Deluxe graphics and sound files
If you want to play with the original Transport Tycoon Deluxe data files you have to copy the data files from the CD-ROM into the baseset/ directory. If you want to play with the original Transport Tycoon Deluxe data files you have to copy the data files from the CD-ROM into the baseset/ directory.
@ -100,7 +97,6 @@ You need to copy the following files:
- trgir.grf or TRGI.GRF - trgir.grf or TRGI.GRF
- trgtr.grf or TRGT.GRF - trgtr.grf or TRGT.GRF
### 1.4.3) Original Transport Tycoon Deluxe music ### 1.4.3) Original Transport Tycoon Deluxe music
If you want the Transport Tycoon Deluxe music, copy the appropriate files from the original game into the baseset folder. If you want the Transport Tycoon Deluxe music, copy the appropriate files from the original game into the baseset folder.
@ -108,7 +104,6 @@ If you want the Transport Tycoon Deluxe music, copy the appropriate files from t
- TTD for DOS: The GM.CAT file - TTD for DOS: The GM.CAT file
- Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT - Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT
## 1.5) Add-on content / mods ## 1.5) Add-on content / mods
OpenTTD features multiple types of add-on content, which modify gameplay in different ways. OpenTTD features multiple types of add-on content, which modify gameplay in different ways.
@ -117,7 +112,6 @@ Most types of add-on content can be downloaded within OpenTTD via the 'Check Onl
Add-on content can also be installed manually, but that's more complicated; the [OpenTTD wiki](https://wiki.openttd.org/) may offer help with that, or the [OpenTTD directory structure guide](./docs/directory_structure.md). Add-on content can also be installed manually, but that's more complicated; the [OpenTTD wiki](https://wiki.openttd.org/) may offer help with that, or the [OpenTTD directory structure guide](./docs/directory_structure.md).
### 1.5.1) Social Integration ### 1.5.1) Social Integration
OpenTTD has the ability to load plugins to integrate with Social Platforms like Steam, Discord, etc. OpenTTD has the ability to load plugins to integrate with Social Platforms like Steam, Discord, etc.
@ -126,7 +120,6 @@ To enable such integration, the plugin for the specific platform has to be downl
See [OpenTTD's website](https://www.openttd.org), under Downloads, for what plugins are available. See [OpenTTD's website](https://www.openttd.org), under Downloads, for what plugins are available.
### 1.6) OpenTTD directories ### 1.6) OpenTTD directories
OpenTTD uses its own directory structure to store game data, add-on content etc. OpenTTD uses its own directory structure to store game data, add-on content etc.
@ -137,7 +130,6 @@ For more information, see the [directory structure guide](./docs/directory_struc
If you want to compile OpenTTD from source, instructions can be found in [COMPILING.md](./COMPILING.md). If you want to compile OpenTTD from source, instructions can be found in [COMPILING.md](./COMPILING.md).
## 2.0) Contact and Community ## 2.0) Contact and Community
'Official' channels 'Official' channels
@ -160,12 +152,10 @@ You can play OpenTTD with others, either cooperatively or competitively.
See the [multiplayer documentation](./docs/multiplayer.md) for more details. See the [multiplayer documentation](./docs/multiplayer.md) for more details.
### 2.2) Contributing to OpenTTD ### 2.2) Contributing to OpenTTD
We welcome contributors to OpenTTD. More information for contributors can be found in [CONTRIBUTING.md](./CONTRIBUTING.md) We welcome contributors to OpenTTD. More information for contributors can be found in [CONTRIBUTING.md](./CONTRIBUTING.md)
### 2.3) Reporting bugs ### 2.3) Reporting bugs
Good bug reports are very helpful. We have a [guide to reporting bugs](./CONTRIBUTING.md#bug-reports) to help with this. Good bug reports are very helpful. We have a [guide to reporting bugs](./CONTRIBUTING.md#bug-reports) to help with this.
@ -173,12 +163,10 @@ Good bug reports are very helpful. We have a [guide to reporting bugs](./CONTRI
Desyncs in multiplayer are complex to debug and report (some software development skils are required). Desyncs in multiplayer are complex to debug and report (some software development skils are required).
Instructions can be found in [debugging and reporting desyncs](./docs/debugging_desyncs.md). Instructions can be found in [debugging and reporting desyncs](./docs/debugging_desyncs.md).
### 2.4) Translating ### 2.4) Translating
OpenTTD is translated into many languages. Translations are added and updated via the [online translation tool](https://translator.openttd.org). OpenTTD is translated into many languages. Translations are added and updated via the [online translation tool](https://translator.openttd.org).
## 3.0) Licensing ## 3.0) Licensing
OpenTTD is licensed under the GNU General Public License version 2.0. OpenTTD is licensed under the GNU General Public License version 2.0.
@ -215,6 +203,6 @@ See `src/3rdparty/openttd_social_integration_api/LICENSE` for the complete licen
The atomic datatype support detection in `cmake/3rdparty/llvm/CheckAtomic.cmake` is licensed under the Apache 2.0 license. The atomic datatype support detection in `cmake/3rdparty/llvm/CheckAtomic.cmake` is licensed under the Apache 2.0 license.
See `cmake/3rdparty/llvm/LICENSE.txt` for the complete license text. See `cmake/3rdparty/llvm/LICENSE.txt` for the complete license text.
## 4.0 Credits ## 4.0) Credits
See [CREDITS.md](./CREDITS.md) See [CREDITS.md](./CREDITS.md)

View File

@ -9,6 +9,9 @@
GSBridge.GetBridgeID <- GSBridge.GetBridgeType; GSBridge.GetBridgeID <- GSBridge.GetBridgeType;
/* Emulate old GSText parameter padding behaviour */
GSText.SCRIPT_TEXT_MAX_PARAMETERS <- 20;
class GSCompat14 { class GSCompat14 {
function Text(text) function Text(text)
{ {

View File

@ -1,5 +1,108 @@
## 15.x ## 15.x
### 15.0-beta2 (2025-04-13)
- Feature: Snow-covered rocks are now visible (#13627)
- Feature: Generate more rocks on steep slopes during map generation or heightmap import (#13462)
- Feature: Prevent towns from upgrading individually-placed houses (#13270)
- Feature: [Win32] Touchpad two-finger map scrolling (#13172)
- Feature: NewGRF Badges (#13073)
- Add: [NewGRF] Add road-/tram-/rail-type variable 0x45 to get mutual road-/tram-/rail-type on same tile (#13934)
- Add: [Script] Newer Cargo Classes (#13779)
- Add: Show hyperlink destination tooltips in text window (#13742)
- Add: [Script] Saving/loading ScriptList (#13556)
- Add: Press Ctrl to build diagonal canals in game mode (#13432)
- Add: Sandbox settings to Sandbox Options window (#13268)
- Add: Setting to allow placing houses manually in-game (#13266)
- Add: [Script] Event for when a company's president name changes (#13208)
- Add: Ability to toggle visibility of station signs by facility (#13207)
- Add: [Script] ScriptEventCompanyRename (#12878)
- Add: Ukrainian Hryvnia currency (#12877)
- Add: Convert 32bpp-only sprites to 8bpp when needed (#11602)
- Change: [Script] Start GS (but don't run it) when generating world in scenario editor (#13961)
- Change: [Script] Add vehicle owner to crash event (#13878)
- Change: Make tree placement at world generation look more organic (#13515)
- Change: [MacOS] Put the icon in a rounded rectangle (#13446)
- Change: [Script] GetWaypointID to return the StationID of any waypoint (#13407)
- Change: Draw company manager face jacket after collar (#13390)
- Change: Don't distinguish between bus and truck stops when removing them (#13384)
- Change: [Script] Rename BridgeID to BridgeType in the script API (#13352)
- Change: Add fonts document to help window (#13305)
- Change: Log changes to sandbox settings (#13267)
- Change: When player joins network company, use its name instead of number in chat (#13263)
- Change: [Win32] Draw window title bar according to current Windows light/dark theme (#13196)
- Change: Restore wider spacers in main toolbars (#12039)
- Fix: NewGRF Global variables 0D, 0E and 1E refer to wrong GRFFile (#13986)
- Fix #13980: Allow diagonal selection for road convert (#13983)
- Fix: Validate raw strings from game-scripts, and strip invalid and control characters (#13976)
- Fix: Capitalise "Disabled" for the "maximum non-sticky open windows" setting (#13975)
- Fix: Frame widget with label had incorrect spacing (#13967)
- Fix: StringFilter included quotes in the search and failed (#13965)
- Fix #13955: Make graphs respect RTL (#13957)
- Fix: Numbers were left-aligned for RTL languages in several windows (#13959)
- Fix: MayHaveRoad claimed rail station tiles had road, so the custom stationspec index would be read as roadtype (#13949)
- Fix: [Script] Prevent cloning of API instances (#13947)
- Fix: Reference to the correct section of the README, if a graphics or a sound set is incomplete (#13946)
- Fix: Draw the bevel around the music track name as inset (#13935)
- Fix #13923: Padding in music GUI was asymmetric, so it looked different for LTR and RTL languages (#13933)
- Fix #13928: BuildOilRig did not properly set airport rotation (#13929)
- Fix: SDL2 application name hint was not effective (#13926)
- Fix #13921: [Win32] Don't try close an already closed event handle during destruction (#13924)
- Fix #13921: Don't reject MIDI files with a valid file magic value (#13924)
- Fix #13912: Multitile buildings break apart in house picker (#13914)
- Fix #13908: Require double click on order to change stop location (#13913)
- Fix #13910: Invalidate content of house picker window if language is changed (#13911)
- Fix: [Script] Reset instance when changing running scripts in scenario editor (#13906)
- Fix: [Script] Only run the gamescript GameLoop() in-game (#13896)
- Fix #13893: Reversed all x-axis labels for company related and industry production graphs in wallclock mode (#13894)
- Fix #13842: Close industry production graph if industry is removed (#13890)
- Fix #11528: Starting autorail dragging from existing track tiles resulted in adding non-continuous tracks (#13885)
- Fix: Autoreplace rail/road list only listed buildable types (#13887)
- Fix: [NewGRF] Display an error, if NewGRF reference out-of-bounds string parameters in gender/plural choices (#13881)
- Fix #13849: Settings in old saves could be overridden by defaults (#13874)
- Fix #13562: Removed cost estimation message from money cheat (#13857)
- Fix: [NewGRF] Plurals and genders did not work in strings with cases or substrings (#13853, #13852)
- Fix: [NewGRF] String parameter stack and case selection were not processed for control code 0x81 (#13851)
- Fix #13839: Incorrect colour of first company legend in smallmap window (#13841)
- Fix: i circumflex width in TrueType small font (#13836)
- Fix: Don't show owner of non-existent road (#13824)
- Fix: Error message window timeout doesn't match setting (#13812)
- Fix #13795: Crash in vehicle list of 32-bit platforms (#13796)
- Fix: [Script] Company rename event sometimes had the wrong name (#13794)
- Fix: Improve manager face randomisation (#13776)
- Fix #13740: [Script] Handle implicit orders for jump orders (#13753)
- Fix #13749: Default service intervals were not updated when changing timekeeping unit (#13751)
- Fix #13725: Use proper query strings for changing timetable values (#13737)
- Fix #11226: Don't draw story page elements that won't be visible (#13736)
- Fix: More AI than max_no_competitors could start with competitors_interval=0 (#13670)
- Fix: League table window ignored the minimal size in its widget description (#13629)
- Fix: Incorrect snow density when making rocks snowy (#13626)
- Fix: NewGRF vehicles display loading sprites when not actually loading or unloading (#13554)
- Fix #12925: Prevent cost estimates for settings changes (#13550)
- Fix: [Script] Report errors happening during 'Load()' (#13537)
- Fix: [Script] Improve type checking of parameters (#13522)
- Fix: [Script] Don't set CommandCallback for asynchronous commands (#13501)
- Fix: Missing error messages with sell- and autoreplace-all commands (#13469)
- Fix: Too many trees when generating trees at same height (#13460)
- Fix #12912: Company inaugurated year in wallclock mode was not saved (#13448)
- Fix: [Script] Wrong return value for failed preconditions Vehicle::CloneVehicle (#13445)
- Fix #13140: Scale initial industry production estimate by cargo scale (#13427)
- Fix #13384: Crash when remove bus/truck stop tool used on road waypoints (#13391)
- Fix #12987: Historical houses now always spawn completed (#13332)
- Fix: [Win32] Font detection didn't work for locales not supporting code pages (#13306)
- Fix: Restore ability to disable service interval (#13281)
- Fix: Hide company settings from console commands (#13269)
- Fix: Disable service interval widgets for non-owned vehicles (#13260)
- Fix #13225: Cargo payment graph key toggled wrong data sets (#13226)
- Fix: Rail station tile flags were not set early enough (#13203)
- Fix #13199: -f command line parameter does not need a value (#13200)
- Fix: Missing water region invalidation after flooding a half tile with rail in the highest corner (#13047)
- Fix: Strip control codes before sorting NewGRF names (#13034)
- Fix #12968: Added back ability to create unremovable houses (#12989)
- Remove: Drop support for UCS2/UTF-16 encoded scripts (#13992)
- Remove: Support for SDL1.2 (#13298)
### 15.0-beta1 (2024-12-24) ### 15.0-beta1 (2024-12-24)
- Feature: Town, industry and vehicle window zoom with mouse wheel (#12810, #12809, #12797) - Feature: Town, industry and vehicle window zoom with mouse wheel (#12810, #12809, #12797)

View File

@ -17,41 +17,48 @@ if(NOT APILC)
endif() endif()
macro(dump_fileheader) macro(dump_fileheader)
get_filename_component(SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE}" NAME) get_filename_component(SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE}" NAME_WE)
string(APPEND SQUIRREL_EXPORT "\n#include \"../${SCRIPT_API_FILE_NAME}\"") string(APPEND SQUIRREL_EXPORT "\n#include \"../${SCRIPT_API_FILE_NAME}.hpp\"")
if(NOT "${APIUC}" STREQUAL "Template") if(NOT "${APIUC}" STREQUAL "Template")
string(REPLACE "script_" "template_" SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE_NAME}") string(REPLACE "script_" "template_" SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE_NAME}")
string(APPEND SQUIRREL_EXPORT "\n#include \"../template/${SCRIPT_API_FILE_NAME}.sq\"") string(APPEND SQUIRREL_EXPORT "\n#include \"../template/${SCRIPT_API_FILE_NAME}.sq.hpp\"")
endif()
endmacro()
macro(open_namespace)
if(NOT NAMESPACE_OPENED)
string(APPEND SQUIRREL_EXPORT "\nnamespace SQConvert {")
set(NAMESPACE_OPENED TRUE)
endif() endif()
endmacro() endmacro()
macro(dump_class_templates NAME) macro(dump_class_templates NAME)
string(REGEX REPLACE "^Script" "" REALNAME ${NAME}) string(REGEX REPLACE "^Script" "" REALNAME ${NAME})
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} *> { static inline ${NAME} *Get(HSQUIRRELVM vm, int index) { return static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Param<${NAME} *> { static inline ${NAME} *Get(HSQUIRRELVM vm, int index) { return static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<${NAME} &> { static inline ${NAME} &Get(HSQUIRRELVM vm, int index) { return *static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Param<${NAME} &> { static inline ${NAME} &Get(HSQUIRRELVM vm, int index) { return *static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<const ${NAME} *> { static inline const ${NAME} *Get(HSQUIRRELVM vm, int index) { return static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Param<const ${NAME} *> { static inline const ${NAME} *Get(HSQUIRRELVM vm, int index) { return static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<const ${NAME} &> { static inline const ${NAME} &Get(HSQUIRRELVM vm, int index) { return *static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };") string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Param<const ${NAME} &> { static inline const ${NAME} &Get(HSQUIRRELVM vm, int index) { return *static_cast<${NAME} *>(Squirrel::GetRealInstance(vm, index, \"${REALNAME}\")); } };")
if("${NAME}" STREQUAL "ScriptEvent") if("${NAME}" STREQUAL "ScriptEvent")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };") string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };")
elseif("${NAME}" STREQUAL "ScriptText") elseif("${NAME}" STREQUAL "ScriptText")
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<Text *> {") string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Param<Text *> {")
string(APPEND SQUIRREL_EXPORT "\n static inline Text *Get(HSQUIRRELVM vm, int index) {") string(APPEND SQUIRREL_EXPORT "\n\t\tstatic inline Text *Get(HSQUIRRELVM vm, int index) {")
string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_INSTANCE) {") string(APPEND SQUIRREL_EXPORT "\n\t\t\tif (sq_gettype(vm, index) == OT_INSTANCE) {")
string(APPEND SQUIRREL_EXPORT "\n return Param<ScriptText *>::Get(vm, index);") string(APPEND SQUIRREL_EXPORT "\n\t\t\t\treturn Param<ScriptText *>::Get(vm, index);")
string(APPEND SQUIRREL_EXPORT "\n }") string(APPEND SQUIRREL_EXPORT "\n\t\t\t}")
string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_STRING) {") string(APPEND SQUIRREL_EXPORT "\n\t\t\tif (sq_gettype(vm, index) == OT_STRING) {")
string(APPEND SQUIRREL_EXPORT "\n return new RawText(Param<const std::string &>::Get(vm, index));") string(APPEND SQUIRREL_EXPORT "\n\t\t\t\treturn new RawText(Param<const std::string &>::Get(vm, index));")
string(APPEND SQUIRREL_EXPORT "\n }") string(APPEND SQUIRREL_EXPORT "\n\t\t\t}")
string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_NULL) {") string(APPEND SQUIRREL_EXPORT "\n\t\t\tif (sq_gettype(vm, index) == OT_NULL) {")
string(APPEND SQUIRREL_EXPORT "\n return nullptr;") string(APPEND SQUIRREL_EXPORT "\n\t\t\t\treturn nullptr;")
string(APPEND SQUIRREL_EXPORT "\n }") string(APPEND SQUIRREL_EXPORT "\n\t\t\t}")
string(APPEND SQUIRREL_EXPORT "\n throw sq_throwerror(vm, fmt::format(\"parameter {} has an invalid type ; expected: 'Text'\", index - 1));") string(APPEND SQUIRREL_EXPORT "\n\t\t\tthrow sq_throwerror(vm, fmt::format(\"parameter {} has an invalid type ; expected: 'Text'\", index - 1));")
string(APPEND SQUIRREL_EXPORT "\n }") string(APPEND SQUIRREL_EXPORT "\n\t\t}")
string(APPEND SQUIRREL_EXPORT "\n };") string(APPEND SQUIRREL_EXPORT "\n\t};")
else() else()
string(APPEND SQUIRREL_EXPORT "\n template <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };") string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Return<${NAME} *> { static inline int Set(HSQUIRRELVM vm, ${NAME} *res) { if (res == nullptr) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, \"${REALNAME}\", res, nullptr, DefSQDestructorCallback<${NAME}>, true); return 1; } };")
endif() endif()
endmacro() endmacro()
@ -66,7 +73,6 @@ macro(reset_reader)
unset(STATIC_METHODS) unset(STATIC_METHODS)
unset(CLS) unset(CLS)
unset(START_SQUIRREL_DEFINE_ON_NEXT_LINE) unset(START_SQUIRREL_DEFINE_ON_NEXT_LINE)
set(CLS_LEVEL 0)
unset(CLS_IN_API) unset(CLS_IN_API)
endmacro() endmacro()
@ -75,6 +81,9 @@ reset_reader()
file(STRINGS "${SCRIPT_API_FILE}" SOURCE_LINES) file(STRINGS "${SCRIPT_API_FILE}" SOURCE_LINES)
set(NUM_LINE 0) set(NUM_LINE 0)
set(CLS_LEVEL 0)
set(BRACE_LEVEL 0)
macro(doxygen_check) macro(doxygen_check)
if(NOT "${DOXYGEN_SKIP}" STREQUAL "") if(NOT "${DOXYGEN_SKIP}" STREQUAL "")
message(FATAL_ERROR "${SCRIPT_API_FILE}:${NUM_LINE}: a DOXYGEN_API block was not properly closed") message(FATAL_ERROR "${SCRIPT_API_FILE}:${NUM_LINE}: a DOXYGEN_API block was not properly closed")
@ -110,7 +119,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
continue() continue()
endif() endif()
if("${LINE}" MATCHES "^([ ]*)\\* @api (.*)$") if("${LINE}" MATCHES "^([\t ]*)\\* @api (.*)$")
set(LINE ${CMAKE_MATCH_2}) set(LINE ${CMAKE_MATCH_2})
# By default, classes are not selected # By default, classes are not selected
if(NOT CLS_LEVEL) if(NOT CLS_LEVEL)
@ -148,22 +157,28 @@ foreach(LINE IN LISTS SOURCE_LINES)
continue() continue()
endif() endif()
# Count braces to skip function bodies
string(REGEX REPLACE "[^{]" "" OPENING_BRACES "${LINE}")
string(LENGTH "${OPENING_BRACES}" OPENING_BRACES)
string(REGEX REPLACE "[^}]" "" CLOSING_BRACES "${LINE}")
string(LENGTH "${CLOSING_BRACES}" CLOSING_BRACES)
math(EXPR BRACE_LEVEL "${BRACE_LEVEL} + ${OPENING_BRACES} - ${CLOSING_BRACES}")
# Ignore forward declarations of classes # Ignore forward declarations of classes
if("${LINE}" MATCHES "^( *)class(.*);") if("${LINE}" MATCHES "^(\t*)class(.*);")
continue() continue()
endif() endif()
# We only want to have public functions exported for now # We only want to have public functions exported for now
if("${LINE}" MATCHES "^( *)class (.*) (: public|: protected|: private|:) ([^ ]*)") if("${LINE}" MATCHES "^(\t*)class (.*) (: public|: protected|: private|:) ([^ ]*)")
if(NOT CLS_LEVEL) if(NOT CLS_LEVEL)
if(NOT DEFINED API_SELECTED) if(NOT DEFINED API_SELECTED)
message(WARNING "Class '${CMAKE_MATCH_2}' has no @api. It won't be published to any API.") message(WARNING "${SCRIPT_API_FILE}:${NUM_LINE}: Class '${CMAKE_MATCH_2}' has no @api. It won't be published to any API.")
set(API_SELECTED FALSE) set(API_SELECTED FALSE)
endif() endif()
unset(IS_PUBLIC) unset(IS_PUBLIC)
unset(CLS_PARAM_0) unset(CLS_PARAMS)
set(CLS_PARAM_1 1) set(CLS_TYPES "x")
set(CLS_PARAM_2 "x")
set(CLS_IN_API ${API_SELECTED}) set(CLS_IN_API ${API_SELECTED})
unset(API_SELECTED) unset(API_SELECTED)
set(CLS "${CMAKE_MATCH_2}") set(CLS "${CMAKE_MATCH_2}")
@ -181,19 +196,19 @@ foreach(LINE IN LISTS SOURCE_LINES)
math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1") math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1")
continue() continue()
endif() endif()
if("${LINE}" MATCHES "^( *)public") if("${LINE}" MATCHES "^(\t*)public")
if(CLS_LEVEL EQUAL 1) if(CLS_LEVEL EQUAL 1)
set(IS_PUBLIC TRUE) set(IS_PUBLIC TRUE)
endif() endif()
continue() continue()
endif() endif()
if("${LINE}" MATCHES "^( *)protected") if("${LINE}" MATCHES "^(\t*)protected")
if(CLS_LEVEL EQUAL 1) if(CLS_LEVEL EQUAL 1)
unset(IS_PUBLIC) unset(IS_PUBLIC)
endif() endif()
continue() continue()
endif() endif()
if("${LINE}" MATCHES "^( *)private") if("${LINE}" MATCHES "^(\t*)private")
if(CLS_LEVEL EQUAL 1) if(CLS_LEVEL EQUAL 1)
unset(IS_PUBLIC) unset(IS_PUBLIC)
endif() endif()
@ -221,7 +236,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif() endif()
# We need to make specialized conversions for structs # We need to make specialized conversions for structs
if("${LINE}" MATCHES "^( *)struct ([^ ]*)") if("${LINE}" MATCHES "^(\t*)struct ([^ ]*)")
math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1") math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1")
# Check if we want to publish this struct # Check if we want to publish this struct
@ -243,7 +258,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif() endif()
# We need to make specialized conversions for enums # We need to make specialized conversions for enums
if("${LINE}" MATCHES "^( *)enum ([^ ]*)") if("${LINE}" MATCHES "^(\t*)enum ([^ ]*)")
math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1") math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1")
# Check if we want to publish this enum # Check if we want to publish this enum
@ -266,7 +281,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif() endif()
# Maybe the end of the class, if so we can start with the Squirrel export pretty soon # Maybe the end of the class, if so we can start with the Squirrel export pretty soon
if("${LINE}" MATCHES "};") if(BRACE_LEVEL LESS CLS_LEVEL)
math(EXPR CLS_LEVEL "${CLS_LEVEL} - 1") math(EXPR CLS_LEVEL "${CLS_LEVEL} - 1")
if(CLS_LEVEL) if(CLS_LEVEL)
unset(IN_ENUM) unset(IN_ENUM)
@ -280,7 +295,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif() endif()
# Empty/white lines. When we may do the Squirrel export, do that export. # Empty/white lines. When we may do the Squirrel export, do that export.
if("${LINE}" MATCHES "^([ ]*)$") if("${LINE}" MATCHES "^([ \t]*)$")
if(NOT START_SQUIRREL_DEFINE_ON_NEXT_LINE) if(NOT START_SQUIRREL_DEFINE_ON_NEXT_LINE)
continue() continue()
endif() endif()
@ -304,33 +319,17 @@ foreach(LINE IN LISTS SOURCE_LINES)
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
if("${APIUC}" STREQUAL "Template") if("${APIUC}" STREQUAL "Template")
# First check whether we have enums to print
if(DEFINED ENUMS)
if(NOT NAMESPACE_OPENED)
string(APPEND SQUIRREL_EXPORT "\nnamespace SQConvert {")
set(NAMESPACE_OPENED TRUE)
endif()
endif()
# Then check whether we have structs/classes to print # Then check whether we have structs/classes to print
if(DEFINED STRUCTS) if(DEFINED STRUCTS)
if(NOT NAMESPACE_OPENED) open_namespace()
string(APPEND SQUIRREL_EXPORT "\nnamespace SQConvert {") string(APPEND SQUIRREL_EXPORT "\n\t/* Allow inner classes/structs to be used as Squirrel parameters */")
set(NAMESPACE_OPENED TRUE)
endif()
string(APPEND SQUIRREL_EXPORT "\n /* Allow inner classes/structs to be used as Squirrel parameters */")
foreach(STRUCT IN LISTS STRUCTS) foreach(STRUCT IN LISTS STRUCTS)
dump_class_templates(${STRUCT}) dump_class_templates(${STRUCT})
endforeach() endforeach()
endif() endif()
if(NOT NAMESPACE_OPENED) open_namespace()
string(APPEND SQUIRREL_EXPORT "\nnamespace SQConvert {") string(APPEND SQUIRREL_EXPORT "\n\t/* Allow ${CLS} to be used as Squirrel parameter */")
set(NAMESPACE_OPENED TRUE)
else()
string(APPEND SQUIRREL_EXPORT "\n")
endif()
string(APPEND SQUIRREL_EXPORT "\n /* Allow ${CLS} to be used as Squirrel parameter */")
dump_class_templates(${CLS}) dump_class_templates(${CLS})
string(APPEND SQUIRREL_EXPORT "\n} // namespace SQConvert") string(APPEND SQUIRREL_EXPORT "\n} // namespace SQConvert")
@ -340,23 +339,23 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif() endif()
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
string(APPEND SQUIRREL_EXPORT "\ntemplate <> SQInteger PushClassName<${CLS}, ScriptType::${APIUC}>(HSQUIRRELVM vm) { sq_pushstring(vm, \"${API_CLS}\", -1); return 1; }") string(APPEND SQUIRREL_EXPORT "\ntemplate <> SQInteger PushClassName<${CLS}, ScriptType::${APIUC}>(HSQUIRRELVM vm) { sq_pushstring(vm, \"${API_CLS}\"); return 1; }")
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
# Then do the registration functions of the class. # Then do the registration functions of the class.
string(APPEND SQUIRREL_EXPORT "\nvoid SQ${API_CLS}_Register(Squirrel *engine)") string(APPEND SQUIRREL_EXPORT "\nvoid SQ${API_CLS}_Register(Squirrel &engine)")
string(APPEND SQUIRREL_EXPORT "\n{") string(APPEND SQUIRREL_EXPORT "\n{")
string(APPEND SQUIRREL_EXPORT "\n DefSQClass<${CLS}, ScriptType::${APIUC}> SQ${API_CLS}(\"${API_CLS}\");") string(APPEND SQUIRREL_EXPORT "\n\tDefSQClass<${CLS}, ScriptType::${APIUC}> SQ${API_CLS}(\"${API_CLS}\");")
if("${SUPER_CLS}" STREQUAL "Text") if("${SUPER_CLS}" STREQUAL "Text")
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.PreRegister(engine);") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.PreRegister(engine);")
else() else()
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.PreRegister(engine, \"${API_SUPER_CLS}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.PreRegister(engine, \"${API_SUPER_CLS}\");")
endif() endif()
if(NOT "${SUPER_CLS}" MATCHES "^ScriptEvent") if((DEFINED CLS_PARAMS OR DEFINED METHODS) AND NOT "${SUPER_CLS}" MATCHES "^ScriptEvent" AND NOT "${CLS}" STREQUAL "ScriptEvent")
if("${CLS_PARAM_2}" STREQUAL "v") if("${CLS_TYPES}" STREQUAL "v")
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.AddSQAdvancedConstructor(engine);") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.AddSQAdvancedConstructor(engine);")
else() else()
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.AddConstructor<void (${CLS}::*)(${CLS_PARAM_0}), ${CLS_PARAM_1}>(engine, \"${CLS_PARAM_2}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.AddConstructor<void (${CLS}::*)(${CLS_PARAMS})>(engine, \"${CLS_TYPES}\");")
endif() endif()
endif() endif()
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
@ -376,7 +375,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
foreach(i RANGE ${LEN}) foreach(i RANGE ${LEN})
string(APPEND SPACES " ") string(APPEND SPACES " ")
endforeach() endforeach()
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.DefSQConst(engine, ${CLS}::${ENUM_VALUE},${SPACES}\"${ENUM_VALUE}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQConst(engine, ${CLS}::${ENUM_VALUE},${SPACES}\"${ENUM_VALUE}\");")
endforeach() endforeach()
if(MLEN) if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
@ -397,7 +396,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
foreach(i RANGE ${LEN}) foreach(i RANGE ${LEN})
string(APPEND SPACES " ") string(APPEND SPACES " ")
endforeach() endforeach()
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.DefSQConst(engine, ${CLS}::${CONST_VALUE},${SPACES}\"${CONST_VALUE}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQConst(engine, ${CLS}::${CONST_VALUE},${SPACES}\"${CONST_VALUE}\");")
endforeach() endforeach()
if(MLEN) if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
@ -423,7 +422,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
foreach(i RANGE ${LEN}) foreach(i RANGE ${LEN})
string(APPEND SPACES " ") string(APPEND SPACES " ")
endforeach() endforeach()
string(APPEND SQUIRREL_EXPORT "\n ScriptError::RegisterErrorMap(${ENUM_STRING},${SPACES}${CLS}::${ENUM_ERROR});") string(APPEND SQUIRREL_EXPORT "\n\tScriptError::RegisterErrorMap(${ENUM_STRING},${SPACES}${CLS}::${ENUM_ERROR});")
endforeach() endforeach()
if(MLEN) if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
@ -444,7 +443,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
foreach(i RANGE ${LEN}) foreach(i RANGE ${LEN})
string(APPEND SPACES " ") string(APPEND SPACES " ")
endforeach() endforeach()
string(APPEND SQUIRREL_EXPORT "\n ScriptError::RegisterErrorMapString(${CLS}::${ENUM_ERROR_TO_STRING},${SPACES}\"${ENUM_ERROR_TO_STRING}\");") string(APPEND SQUIRREL_EXPORT "\n\tScriptError::RegisterErrorMapString(${CLS}::${ENUM_ERROR_TO_STRING},${SPACES}\"${ENUM_ERROR_TO_STRING}\");")
endforeach() endforeach()
if(MLEN) if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
@ -463,8 +462,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
foreach(STATIC_METHOD IN LISTS STATIC_METHODS) foreach(STATIC_METHOD IN LISTS STATIC_METHODS)
string(REPLACE ":" ";" STATIC_METHOD "${STATIC_METHOD}") string(REPLACE ":" ";" STATIC_METHOD "${STATIC_METHOD}")
list(GET STATIC_METHOD 0 FUNCNAME) list(GET STATIC_METHOD 0 FUNCNAME)
list(GET STATIC_METHOD 1 ARGC) list(GET STATIC_METHOD 1 TYPES)
list(GET STATIC_METHOD 2 TYPES)
string(LENGTH "${FUNCNAME}" LEN) string(LENGTH "${FUNCNAME}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}") math(EXPR LEN "${MLEN} - ${LEN}")
if("${TYPES}" STREQUAL "v") if("${TYPES}" STREQUAL "v")
@ -479,9 +477,9 @@ foreach(LINE IN LISTS SOURCE_LINES)
string(APPEND SPACES " ") string(APPEND SPACES " ")
endforeach() endforeach()
if("${TYPES}" STREQUAL "v") if("${TYPES}" STREQUAL "v")
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.DefSQAdvancedStaticMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQAdvancedStaticMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\");")
else() else()
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.DefSQStaticMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\",${SPACES}${ARGC}, \"${TYPES}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQStaticMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\",${SPACES}\"${TYPES}\");")
endif() endif()
endforeach() endforeach()
if(MLEN) if(MLEN)
@ -501,8 +499,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
foreach(METHOD IN LISTS METHODS) foreach(METHOD IN LISTS METHODS)
string(REPLACE ":" ";" METHOD "${METHOD}") string(REPLACE ":" ";" METHOD "${METHOD}")
list(GET METHOD 0 FUNCNAME) list(GET METHOD 0 FUNCNAME)
list(GET METHOD 1 ARGC) list(GET METHOD 1 TYPES)
list(GET METHOD 2 TYPES)
string(LENGTH "${FUNCNAME}" LEN) string(LENGTH "${FUNCNAME}" LEN)
math(EXPR LEN "${MLEN} - ${LEN}") math(EXPR LEN "${MLEN} - ${LEN}")
if("${TYPES}" STREQUAL "v") if("${TYPES}" STREQUAL "v")
@ -517,16 +514,16 @@ foreach(LINE IN LISTS SOURCE_LINES)
string(APPEND SPACES " ") string(APPEND SPACES " ")
endforeach() endforeach()
if("${TYPES}" STREQUAL "v") if("${TYPES}" STREQUAL "v")
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.DefSQAdvancedMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQAdvancedMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\");")
else() else()
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.DefSQMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\",${SPACES}${ARGC}, \"${TYPES}\");") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.DefSQMethod(engine, &${CLS}::${FUNCNAME},${SPACES}\"${FUNCNAME}\",${SPACES}\"${TYPES}\");")
endif() endif()
endforeach() endforeach()
if(MLEN) if(MLEN)
string(APPEND SQUIRREL_EXPORT "\n") string(APPEND SQUIRREL_EXPORT "\n")
endif() endif()
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.PostRegister(engine);") string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.PostRegister(engine);")
string(APPEND SQUIRREL_EXPORT "\n}") string(APPEND SQUIRREL_EXPORT "\n}")
reset_reader() reset_reader()
@ -539,9 +536,13 @@ foreach(LINE IN LISTS SOURCE_LINES)
continue() continue()
endif() endif()
if(NOT BRACE_LEVEL EQUAL CLS_LEVEL)
continue()
endif()
# Add enums # Add enums
if(IN_ENUM) if(IN_ENUM)
string(REGEX MATCH "([^, ]+)" ENUM_VALUE "${LINE}") string(REGEX MATCH "([^,\t ]+)" ENUM_VALUE "${LINE}")
list(APPEND ENUM_VALUES "${ENUM_VALUE}") list(APPEND ENUM_VALUES "${ENUM_VALUE}")
# Check if this a special error enum # Check if this a special error enum
@ -549,12 +550,12 @@ foreach(LINE IN LISTS SOURCE_LINES)
if("${ENUM}" MATCHES ".*::ErrorMessages") if("${ENUM}" MATCHES ".*::ErrorMessages")
# syntax: # syntax:
# enum ErrorMessages { # enum ErrorMessages {
# ERR_SOME_ERROR, // [STR_ITEM1, STR_ITEM2, ...] #\tERR_SOME_ERROR,\t// [STR_ITEM1, STR_ITEM2, ...]
# } # }
# Set the mappings # Set the mappings
if("${LINE}" MATCHES "\\[(.*)\\]") if("${LINE}" MATCHES "\\[(.*)\\]")
string(REGEX REPLACE "[ ]" "" MAPPINGS "${CMAKE_MATCH_1}") string(REGEX REPLACE "[ \t]" "" MAPPINGS "${CMAKE_MATCH_1}")
string(REPLACE "," ";" MAPPINGS "${MAPPINGS}") string(REPLACE "," ";" MAPPINGS "${MAPPINGS}")
foreach(MAPPING IN LISTS MAPPINGS) foreach(MAPPING IN LISTS MAPPINGS)
@ -568,11 +569,11 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif() endif()
# Add a const (non-enum) value # Add a const (non-enum) value
if("${LINE}" MATCHES "^[ ]*static const [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;") if("${LINE}" MATCHES "^[ \t]*static const [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;")
list(APPEND CONST_VALUES "${CMAKE_MATCH_1}") list(APPEND CONST_VALUES "${CMAKE_MATCH_1}")
continue() continue()
endif() endif()
if("${LINE}" MATCHES "^[ ]*static constexpr [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;") if("${LINE}" MATCHES "^[ \t]*static constexpr [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;")
list(APPEND CONST_VALUES "${CMAKE_MATCH_1}") list(APPEND CONST_VALUES "${CMAKE_MATCH_1}")
continue() continue()
endif() endif()
@ -584,7 +585,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
endif() endif()
if("${LINE}" MATCHES "~") if("${LINE}" MATCHES "~")
if(DEFINED API_SELECTED) if(DEFINED API_SELECTED)
message(WARNING "Destructor for '${CLS}' has @api. Tag ignored.") message(WARNING "${SCRIPT_API_FILE}:${NUM_LINE}: Destructor for '${CLS}' has @api. Tag ignored.")
unset(API_SELECTED) unset(API_SELECTED)
endif() endif()
continue() continue()
@ -595,24 +596,26 @@ foreach(LINE IN LISTS SOURCE_LINES)
set(IS_STATIC TRUE) set(IS_STATIC TRUE)
endif() endif()
string(REGEX REPLACE "(virtual|static|const)[ ]+" "" LINE "${LINE}") string(REGEX REPLACE "(virtual|static|const)[ \t]+" "" LINE "${LINE}")
string(REGEX REPLACE "{.*" "" LINE "${LINE}") string(REGEX REPLACE "{.*" "" LINE "${LINE}")
set(PARAM_S "${LINE}") set(PARAM_S "${LINE}")
string(REGEX REPLACE "\\*" "" LINE "${LINE}") string(REGEX REPLACE "\\*" "" LINE "${LINE}")
string(REGEX REPLACE "\\(.*" "" LINE "${LINE}") string(REGEX REPLACE "\\(.*" "" LINE "${LINE}")
string(REGEX REPLACE ".*\\(" "" PARAM_S "${PARAM_S}") # Parameters start at first "(". Further "(" will appear in ctor lists.
string(REGEX MATCH "\\(.*" PARAM_S "${PARAM_S}")
string(REGEX REPLACE "\\).*" "" PARAM_S "${PARAM_S}") string(REGEX REPLACE "\\).*" "" PARAM_S "${PARAM_S}")
string(REGEX REPLACE "^\\(" "" PARAM_S "${PARAM_S}")
string(REGEX MATCH "([^ ]+)( ([^ ]+))?" RESULT "${LINE}") string(REGEX MATCH "([^ \t]+)( ([^ ]+))?" RESULT "${LINE}")
set(FUNCTYPE "${CMAKE_MATCH_1}") set(FUNCTYPE "${CMAKE_MATCH_1}")
set(FUNCNAME "${CMAKE_MATCH_3}") set(FUNCNAME "${CMAKE_MATCH_3}")
if("${FUNCTYPE}" STREQUAL "${CLS}" AND NOT FUNCNAME) if("${FUNCTYPE}" STREQUAL "${CLS}" AND NOT FUNCNAME)
if(DEFINED API_SELECTED) if(DEFINED API_SELECTED)
message(WARNING "Constructor for '${CLS}' has @api. Tag ignored.") message(WARNING "${SCRIPT_API_FILE}:${NUM_LINE}: Constructor for '${CLS}' has @api. Tag ignored.")
unset(API_SELECTED) unset(API_SELECTED)
endif() endif()
set(CLS_PARAM_0 "${PARAM_S}") set(CLS_PARAMS "${PARAM_S}")
if(NOT PARAM_S) if(NOT PARAM_S)
continue() continue()
endif() endif()
@ -627,9 +630,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
set(TYPES "x") set(TYPES "x")
endif() endif()
set(LEN 1)
foreach(PARAM IN LISTS PARAMS) foreach(PARAM IN LISTS PARAMS)
math(EXPR LEN "${LEN} + 1")
string(STRIP "${PARAM}" PARAM) string(STRIP "${PARAM}" PARAM)
if("${PARAM}" MATCHES "\\*|&") if("${PARAM}" MATCHES "\\*|&")
if("${PARAM}" MATCHES "^char") if("${PARAM}" MATCHES "^char")
@ -668,13 +669,12 @@ foreach(LINE IN LISTS SOURCE_LINES)
unset(API_SELECTED) unset(API_SELECTED)
if("${FUNCTYPE}" STREQUAL "${CLS}" AND NOT FUNCNAME) if("${FUNCTYPE}" STREQUAL "${CLS}" AND NOT FUNCNAME)
set(CLS_PARAM_1 ${LEN}) set(CLS_TYPES "${TYPES}")
set(CLS_PARAM_2 "${TYPES}")
elseif("${FUNCNAME}" MATCHES "^_" AND NOT "${TYPES}" STREQUAL "v") elseif("${FUNCNAME}" MATCHES "^_" AND NOT "${TYPES}" STREQUAL "v")
elseif(IS_STATIC) elseif(IS_STATIC)
list(APPEND STATIC_METHODS "${FUNCNAME}:${LEN}:${TYPES}") list(APPEND STATIC_METHODS "${FUNCNAME}:${TYPES}")
else() else()
list(APPEND METHODS "${FUNCNAME}:${LEN}:${TYPES}") list(APPEND METHODS "${FUNCNAME}:${TYPES}")
endif() endif()
continue() continue()
endif() endif()

View File

@ -19,7 +19,7 @@ endif()
file(READ "${API_FILES}" SCRIPT_API_BINARY_FILES) file(READ "${API_FILES}" SCRIPT_API_BINARY_FILES)
foreach(FILE IN LISTS SCRIPT_API_BINARY_FILES) foreach(FILE IN LISTS SCRIPT_API_BINARY_FILES)
file(STRINGS "${FILE}" LINES REGEX "^void SQ${APIUC}.*_Register\\(Squirrel \\*engine\\)$") file(STRINGS "${FILE}" LINES REGEX "^void SQ${APIUC}.*_Register\\(Squirrel &engine\\)$")
if(LINES) if(LINES)
string(REGEX REPLACE ".*api/${APILC}/(.*)" "#include \"\\1\"" FILE "${FILE}") string(REGEX REPLACE ".*api/${APILC}/(.*)" "#include \"\\1\"" FILE "${FILE}")
list(APPEND SQUIRREL_INCLUDES "${FILE}") list(APPEND SQUIRREL_INCLUDES "${FILE}")
@ -28,7 +28,7 @@ foreach(FILE IN LISTS SCRIPT_API_BINARY_FILES)
continue() continue()
endif() endif()
string(REGEX REPLACE "^.*void " " " LINE "${LINE}") string(REGEX REPLACE "^.*void " " " LINE "${LINE}")
string(REGEX REPLACE "Squirrel \\*" "" LINE "${LINE}") string(REGEX REPLACE "Squirrel &" "" LINE "${LINE}")
list(APPEND SQUIRREL_REGISTER "${LINE}") list(APPEND SQUIRREL_REGISTER "${LINE}")
endforeach() endforeach()
endif() endif()

View File

@ -16,8 +16,11 @@
; - `openttd -I <name>` starts OpenTTD with the given set (case sensitive) ; - `openttd -I <name>` starts OpenTTD with the given set (case sensitive)
; - adding `graphicsset = <name>` to the misc section of openttd.cfg makes ; - adding `graphicsset = <name>` to the misc section of openttd.cfg makes
; OpenTTD start with that graphics set by default ; OpenTTD start with that graphics set by default
; - there is a command line tool for all platforms called md5sum that can ; - `grfid -m` can give the GRF file MD5 checksums that you need
; create the MD5 checksum you need. ; - The `--md5` output option for `nmlc` can also give the MD5 if you are
; encoding from an nml source
; - Simple file MD5 checksums, eg. using `md5sum` are not correct for grf
; container versions other than 1
; - all files specified in this file are search relatively to the path where ; - all files specified in this file are search relatively to the path where
; this file is found, i.e. if the graphics files are in a subdir you have ; this file is found, i.e. if the graphics files are in a subdir you have
; to add that subdir to the names in this file to! It will NOT search for ; to add that subdir to the names in this file to! It will NOT search for
@ -44,6 +47,8 @@ description.en_US = howdie
palette = DOS palette = DOS
; preferred blitter, optional; either 8bpp (default) or 32bpp. ; preferred blitter, optional; either 8bpp (default) or 32bpp.
blitter = 8bpp blitter = 8bpp
; url, optional
url = https://github.com/my/baseset
; The files section lists the files that replace sprites. ; The files section lists the files that replace sprites.
; The file names are case sensitive. ; The file names are case sensitive.

View File

@ -2,8 +2,8 @@
## Table of contents ## Table of contents
- 1.0) About - 1.0) [About](#10-about)
- 2.0) Known bugs - 2.0) [Known bugs](#20-known-bugs)
## 1.0) About ## 1.0) About
@ -12,7 +12,7 @@ that are the same as these. If you do, do not act surprised, because
we WILL flame you! we WILL flame you!
The current list of known bugs that we intend to fix can be found in our The current list of known bugs that we intend to fix can be found in our
bug tracking system at https://github.com/OpenTTD/OpenTTD/issues bug tracking system at [https://github.com/OpenTTD/OpenTTD/issues](https://github.com/OpenTTD/OpenTTD/issues)
Also check the closed bugs when searching for your bug in this system as we Also check the closed bugs when searching for your bug in this system as we
might have fixed the bug in the mean time. might have fixed the bug in the mean time.

Binary file not shown.

View File

@ -1 +1 @@
019dba4830a64ee4345d3d647633e1da a4a727b03a7cd07ee0499231f7f233f4

View File

@ -840,3 +840,7 @@
-1 sprites/chars.png 8bpp 630 400 6 21 0 -2 normal -1 sprites/chars.png 8bpp 630 400 6 21 0 -2 normal
-1 sprites/mono.png 8bpp 325 270 7 13 0 0 normal -1 sprites/mono.png 8bpp 325 270 7 13 0 0 normal
-1 sprites/mono.png 8bpp 340 270 7 13 0 0 normal -1 sprites/mono.png 8bpp 340 270 7 13 0 0 normal
// U+E29D: Small left arrow
-1 * 6 12 01 01 01 9D E2
-1 sprites/chars.png 8bpp 10 430 5 5 0 1 normal

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -4,7 +4,7 @@
// See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>. // See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
// //
-1 * 0 0C "OpenTTD GUI graphics" -1 * 0 0C "OpenTTD GUI graphics"
-1 * 3 05 15 \b 191 // OPENTTD_SPRITE_COUNT -1 * 3 05 15 \b 192 // OPENTTD_SPRITE_COUNT
-1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 66 8 64 31 -31 7 normal
-1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 146 8 64 31 -31 7 normal
-1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal -1 sprites/openttdgui.png 8bpp 226 8 64 31 -31 7 normal
@ -196,3 +196,4 @@
-1 sprites/openttdgui.png 8bpp 567 440 12 10 0 0 normal -1 sprites/openttdgui.png 8bpp 567 440 12 10 0 0 normal
-1 sprites/openttdgui.png 8bpp 581 440 10 10 0 0 normal -1 sprites/openttdgui.png 8bpp 581 440 10 10 0 0 normal
-1 sprites/openttdgui.png 8bpp 593 440 10 10 0 0 normal -1 sprites/openttdgui.png 8bpp 593 440 10 10 0 0 normal
-1 sprites/openttdgui.png 8bpp 605 440 8 10 0 0 normal

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@ -24,7 +24,7 @@ if (!$Env:AZURE_CODESIGN_ENDPOINT -or !$Env:AZURE_CODESIGN_ACCOUNT_NAME -or !$En
exit exit
} }
Install-Module -Name AzureCodeSigning -Scope CurrentUser -RequiredVersion 0.3.0 -Force -Repository PSGallery Install-Module -Name TrustedSigning -Scope CurrentUser -RequiredVersion 0.5.3 -Force -Repository PSGallery
$params = @{} $params = @{}
@ -37,4 +37,4 @@ $params["FileDigest"] = "SHA256"
$params["TimestampRfc3161"] = "http://timestamp.acs.microsoft.com" $params["TimestampRfc3161"] = "http://timestamp.acs.microsoft.com"
$params["TimestampDigest"] = "SHA256" $params["TimestampDigest"] = "SHA256"
Invoke-AzureCodeSigning @params Invoke-TrustedSigning @params

View File

@ -17,6 +17,10 @@ function Regression::TestInit()
print(" IsValid(vehicle.plane_speed): " + AIGameSettings.IsValid("vehicle.plane_speed")); print(" IsValid(vehicle.plane_speed): " + AIGameSettings.IsValid("vehicle.plane_speed"));
print(" vehicle.plane_speed: " + AIGameSettings.GetValue("vehicle.plane_speed")); print(" vehicle.plane_speed: " + AIGameSettings.GetValue("vehicle.plane_speed"));
require("require.nut"); require("require.nut");
print(" TestEnum.value1: " + ::TestEnum.value1);
print(" test_constant: " + ::test_constant);
print(" TestEnum.value2: " + TestEnum.value2);
print(" test_constant: " + test_constant);
print(" min(6, 3): " + min(6, 3)); print(" min(6, 3): " + min(6, 3));
print(" min(3, 6): " + min(3, 6)); print(" min(3, 6): " + min(3, 6));
print(" max(6, 3): " + max(6, 3)); print(" max(6, 3): " + max(6, 3));
@ -824,6 +828,13 @@ function Regression::List()
print(" []:"); print(" []:");
print(" 4000 => " + list[4000]); print(" 4000 => " + list[4000]);
print(" clone:");
local list3 = clone list;
print(" Clone ListDump:");
foreach (idx, val in list3) {
print(" " + idx + " => " + val);
}
list.Clear(); list.Clear();
print(" IsEmpty(): " + list.IsEmpty()); print(" IsEmpty(): " + list.IsEmpty());
@ -856,6 +867,12 @@ function Regression::List()
it = list.Next(); it = list.Next();
print(" " + it + " => " + list.GetValue(it)); print(" " + it + " => " + list.GetValue(it));
} }
print(" Clone ListDump:");
foreach (idx, val in list3) {
print(" " + idx + " => " + val);
}
} }
function Regression::Map() function Regression::Map()
@ -1017,6 +1034,28 @@ function Regression::Order()
foreach (idx, val in list) { foreach (idx, val in list) {
print(" " + idx + " => " + val); print(" " + idx + " => " + val);
} }
list = AIVehicleList_Station(3, AIVehicle.VT_ROAD);
print(" Count(): " + list.Count());
list.Valuate(AIVehicle.GetLocation);
print(" Location ListDump:");
for (local i = list.Begin(); !list.IsEnd(); i = list.Next()) {
print(" " + i + " => " + list.GetValue(i));
}
print(" foreach():");
foreach (idx, val in list) {
print(" " + idx + " => " + val);
}
list = AIVehicleList_Station(3, AIVehicle.VT_RAIL);
print(" Count(): " + list.Count());
list.Valuate(AIVehicle.GetLocation);
print(" Location ListDump:");
for (local i = list.Begin(); !list.IsEnd(); i = list.Next()) {
print(" " + i + " => " + list.GetValue(i));
}
print(" foreach():");
foreach (idx, val in list) {
print(" " + idx + " => " + val);
}
} }
function Regression::RailTypeList() function Regression::RailTypeList()
@ -1675,13 +1714,22 @@ function Regression::TownList()
} }
print(" HasStatue(): " + AITown.HasStatue(list.Begin())); print(" HasStatue(): " + AITown.HasStatue(list.Begin()));
print(" GetRoadReworkDuration(): " + AITown.GetRoadReworkDuration(list.Begin()));
print(" GetExclusiveRightsCompany(): " + AITown.GetExclusiveRightsCompany(list.Begin()));
print(" GetExclusiveRightsDuration(): " + AITown.GetExclusiveRightsDuration(list.Begin()));
print(" IsActionAvailable(BUILD_STATUE): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_BUILD_STATUE)); print(" IsActionAvailable(BUILD_STATUE): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_BUILD_STATUE));
print(" PerformTownAction(BUILD_STATUE): " + AITown.PerformTownAction(list.Begin(), AITown.TOWN_ACTION_BUILD_STATUE)); print(" PerformTownAction(BUILD_STATUE): " + AITown.PerformTownAction(list.Begin(), AITown.TOWN_ACTION_BUILD_STATUE));
print(" IsActionAvailable(BUILD_STATUE): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_BUILD_STATUE)); print(" IsActionAvailable(BUILD_STATUE): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_BUILD_STATUE));
print(" HasStatue(): " + AITown.HasStatue(list.Begin())); print(" HasStatue(): " + AITown.HasStatue(list.Begin()));
print(" GetRoadReworkDuration(): " + AITown.GetRoadReworkDuration(list.Begin()));
print(" IsActionAvailable(ROAD_REBUILD): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_ROAD_REBUILD));
print(" PerformTownAction(ROAD_REBUILD): " + AITown.PerformTownAction(list.Begin(), AITown.TOWN_ACTION_ROAD_REBUILD));
print(" IsActionAvailable(ROAD_REBUILD): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_ROAD_REBUILD));
print(" GetRoadReworkDuration(): " + AITown.GetRoadReworkDuration(list.Begin()));
print(" GetExclusiveRightsCompany(): " + AITown.GetExclusiveRightsCompany(list.Begin()));
print(" GetExclusiveRightsDuration(): " + AITown.GetExclusiveRightsDuration(list.Begin()));
print(" IsActionAvailable(BUY_RIGHTS): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_BUY_RIGHTS));
print(" PerformTownAction(BUY_RIGHTS): " + AITown.PerformTownAction(list.Begin(), AITown.TOWN_ACTION_BUY_RIGHTS));
print(" IsActionAvailable(BUY_RIGHTS): " + AITown.IsActionAvailable(list.Begin(), AITown.TOWN_ACTION_BUY_RIGHTS));
print(" GetExclusiveRightsCompany(): " + AITown.GetExclusiveRightsCompany(list.Begin()));
print(" GetExclusiveRightsDuration(): " + AITown.GetExclusiveRightsDuration(list.Begin()));
} }
function Regression::Tunnel() function Regression::Tunnel()
@ -1975,6 +2023,33 @@ function Regression::Math()
print(" 13725 > -2147483648: " + ( 13725 > -2147483648)); print(" 13725 > -2147483648: " + ( 13725 > -2147483648));
} }
function Regression::PriorityQueue()
{
print("");
print("--PriorityQueue--");
local queue = AIPriorityQueue();
print(" IsEmpty(): " + queue.IsEmpty());
print(" Count(): " + queue.Count());
print(" --Insert--")
for (local i = 0; i < 10; i++) {
print(" Insert(" + i + ", " + i + "): " + queue.Insert(i, i));
}
print(" Exists(5): " + queue.Exists(5));
print(" Insert(5, 5): "+ queue.Insert(5, 5));
print(" IsEmpty(): " + queue.IsEmpty());
print(" Count(): " + queue.Count());
local item = queue.Peek();
print(" Peek(): " + item);
print(" Count(): " + queue.Count());
local item2 = queue.Pop();
print(" Pop(): " + item2);
print(" Count(): " + queue.Count());
print(" " + item + " == " + item2 + " : " + (item == item2));
print(" Clear(): " + queue.Clear());
print(" IsEmpty(): " + queue.IsEmpty());
print(" Count(): " + queue.Count());
}
function Regression::Start() function Regression::Start()
{ {
this.TestInit(); this.TestInit();
@ -2049,6 +2124,20 @@ function Regression::Start()
print(" PresidentName: " + c.GetNewName()); print(" PresidentName: " + c.GetNewName());
} break; } break;
case AIEvent.ET_EXCLUSIVE_TRANSPORT_RIGHTS: {
local c = AIEventExclusiveTransportRights.Convert(e);
print(" EventName: ExclusiveTransportRights");
print(" CompanyID: " + c.GetCompanyID());
print(" TownID: " + c.GetTownID());
} break;
case AIEvent.ET_ROAD_RECONSTRUCTION: {
local c = AIEventRoadReconstruction.Convert(e);
print(" EventName: RoadReconstruction");
print(" CompanyID: " + c.GetCompanyID());
print(" TownID: " + c.GetTownID());
} break;
default: default:
print(" Unknown Event"); print(" Unknown Event");
break; break;
@ -2057,12 +2146,18 @@ function Regression::Start()
print(" IsEventWaiting: false"); print(" IsEventWaiting: false");
this.Math(); this.Math();
this.PriorityQueue();
/* Check Valuate() is actually limited, MUST BE THE LAST TEST. */ /* Check Valuate() is actually limited, MUST BE THE LAST TEST. */
print("--Valuate() with excessive CPU usage--") print("--Valuate() with excessive CPU usage--")
local list = AIList(); local list = AIList();
list.AddItem(0, 0); list.AddItem(0, 0);
local Infinite = function(id) { while(true); } local Infinite = function(id) { while(true); }
try {
list = AIIndustryList(Infinite);
} catch (e) {
print("constructor failed with: " + e);
}
list.Valuate(Infinite); list.Valuate(Infinite);
} }

View File

@ -1,2 +1,9 @@
print(" Required this file"); print(" Required this file");
const test_constant = 1;
enum TestEnum {
value0,
value1,
value2
};

View File

@ -8,6 +8,10 @@
IsValid(vehicle.plane_speed): true IsValid(vehicle.plane_speed): true
vehicle.plane_speed: 2 vehicle.plane_speed: 2
Required this file Required this file
TestEnum.value1: 1
test_constant: 1
TestEnum.value2: 2
test_constant: 1
min(6, 3): 3 min(6, 3): 3
min(3, 6): 3 min(3, 6): 3
max(6, 3): 6 max(6, 3): 6
@ -81,7 +85,7 @@
20 20
30 30
40 40
Ops: 8673 Ops: 8649
--Std-- --Std--
abs(-21): 21 abs(-21): 21
@ -571,6 +575,13 @@
4006 => 12 4006 => 12
[]: []:
4000 => 50 4000 => 50
clone:
Clone ListDump:
1005 => 1005
4000 => 50
4001 => 8002
4002 => 8004
4006 => 12
IsEmpty(): true IsEmpty(): true
0 => 5 (true) 0 => 5 (true)
ERROR: Next() is invalid as Begin() is never called ERROR: Next() is invalid as Begin() is never called
@ -580,6 +591,12 @@ ERROR: IsEnd() is invalid as Begin() is never called
2 => 6 (true) 2 => 6 (true)
3 => 6 (true) 3 => 6 (true)
9 => 0 (false) 9 => 0 (false)
Clone ListDump:
1005 => 1005
4000 => 50
4001 => 8002
4002 => 8004
4006 => 12
--Company-- --Company--
SetName(): true SetName(): true
@ -9423,13 +9440,22 @@ ERROR: IsEnd() is invalid as Begin() is never called
23 => 652 23 => 652
25 => 563 25 => 563
HasStatue(): false HasStatue(): false
GetRoadReworkDuration(): 0
GetExclusiveRightsCompany(): -1
GetExclusiveRightsDuration(): 0
IsActionAvailable(BUILD_STATUE): true IsActionAvailable(BUILD_STATUE): true
PerformTownAction(BUILD_STATUE): true PerformTownAction(BUILD_STATUE): true
IsActionAvailable(BUILD_STATUE): false IsActionAvailable(BUILD_STATUE): false
HasStatue(): true HasStatue(): true
GetRoadReworkDuration(): 0
IsActionAvailable(ROAD_REBUILD): true
PerformTownAction(ROAD_REBUILD): true
IsActionAvailable(ROAD_REBUILD): true
GetRoadReworkDuration(): 6
GetExclusiveRightsCompany(): -1
GetExclusiveRightsDuration(): 0
IsActionAvailable(BUY_RIGHTS): true
PerformTownAction(BUY_RIGHTS): true
IsActionAvailable(BUY_RIGHTS): false
GetExclusiveRightsCompany(): 1
GetExclusiveRightsDuration(): 12
--Tunnel-- --Tunnel--
IsTunnelTile(): false IsTunnelTile(): false
@ -9694,6 +9720,14 @@ ERROR: IsEnd() is invalid as Begin() is never called
GetStopLocation(): 1 GetStopLocation(): 1
--VehicleList_Station-- --VehicleList_Station--
Count(): 1
Location ListDump:
20 => 23596
foreach():
20 => 23596
Count(): 0
Location ListDump:
foreach():
Count(): 1 Count(): 1
Location ListDump: Location ListDump:
20 => 23596 20 => 23596
@ -9726,6 +9760,16 @@ ERROR: IsEnd() is invalid as Begin() is never called
EventName: CompanyRenamed EventName: CompanyRenamed
CompanyID: 1 CompanyID: 1
CompanyName: Little Frutford Transport CompanyName: Little Frutford Transport
GetNextEvent: instance
GetEventType: 28
EventName: RoadReconstruction
CompanyID: 1
TownID: 12
GetNextEvent: instance
GetEventType: 27
EventName: ExclusiveTransportRights
CompanyID: 1
TownID: 12
IsEventWaiting: false IsEventWaiting: false
--Math-- --Math--
@ -9760,21 +9804,38 @@ ERROR: IsEnd() is invalid as Begin() is never called
-1 > 2147483647: false -1 > 2147483647: false
-2147483648 > 2147483647: false -2147483648 > 2147483647: false
13725 > -2147483648: true 13725 > -2147483648: true
--PriorityQueue--
IsEmpty(): true
Count(): 0
--Insert--
Insert(0, 0): true
Insert(1, 1): true
Insert(2, 2): true
Insert(3, 3): true
Insert(4, 4): true
Insert(5, 5): true
Insert(6, 6): true
Insert(7, 7): true
Insert(8, 8): true
Insert(9, 9): true
Exists(5): true
Insert(5, 5): true
IsEmpty(): false
Count(): 11
Peek(): 0
Count(): 11
Pop(): 0
Count(): 10
0 == 0 : true
Clear(): (null : 0x00000000)
IsEmpty(): true
Count(): 0
--Valuate() with excessive CPU usage-- --Valuate() with excessive CPU usage--
constructor failed with: excessive CPU usage in list filter function
Your script made an error: excessive CPU usage in valuator function Your script made an error: excessive CPU usage in valuator function
*FUNCTION [unknown()] regression/main.nut line [2065] *FUNCTION [Start()] regression/main.nut line [2161]
*FUNCTION [Valuate()] NATIVE line [-1]
*FUNCTION [Start()] regression/main.nut line [2066]
[id] 0
[this] TABLE
[Infinite] CLOSURE
[list] INSTANCE
[this] INSTANCE
Your script made an error: excessive CPU usage in valuator function
*FUNCTION [Start()] regression/main.nut line [2066]
[Infinite] CLOSURE [Infinite] CLOSURE
[list] INSTANCE [list] INSTANCE

View File

@ -19,6 +19,7 @@
#include <unicode/utypes.h> #include <unicode/utypes.h>
#include <unicode/uobject.h> #include <unicode/uobject.h>
#include <unicode/uscript.h> #include <unicode/uscript.h>
#include <array>
U_NAMESPACE_BEGIN U_NAMESPACE_BEGIN

View File

@ -281,7 +281,7 @@ void Md5::Append(const void *data, const size_t nbytes)
if (offset) { if (offset) {
size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes); size_t copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
memcpy(this->buf + offset, p, copy); std::copy_n(p, copy, this->buf + offset);
if (offset + copy < 64) return; if (offset + copy < 64) return;
@ -294,7 +294,7 @@ void Md5::Append(const void *data, const size_t nbytes)
for (; left >= 64; p += 64, left -= 64) this->Process(p); for (; left >= 64; p += 64, left -= 64) this->Process(p);
/* Process a final partial block. */ /* Process a final partial block. */
if (left) memcpy(this->buf, p, left); if (left) std::copy_n(p, left, this->buf);
} }
void Md5::Finish(MD5Hash &digest) void Md5::Finish(MD5Hash &digest)

View File

@ -1,6 +1,5 @@
add_files( add_files(
sqstdaux.h sqstdaux.h
sqstdmath.h sqstdmath.h
sqstdstring.h
squirrel.h squirrel.h
) )

View File

@ -1,23 +0,0 @@
/* see copyright notice in squirrel.h */
#ifndef _SQSTD_STRING_H_
#define _SQSTD_STRING_H_
typedef unsigned int SQRexBool;
typedef struct SQRex SQRex;
typedef struct {
const SQChar *begin;
SQInteger len;
} SQRexMatch;
SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error);
void sqstd_rex_free(SQRex *exp);
SQBool sqstd_rex_match(SQRex* exp,const SQChar* text);
SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end);
SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end);
SQInteger sqstd_rex_getsubexpcount(SQRex* exp);
SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp);
SQRESULT sqstd_register_stringlib(HSQUIRRELVM v);
#endif /*_SQSTD_STRING_H_*/

View File

@ -68,7 +68,6 @@ struct SQClass;
struct SQInstance; struct SQInstance;
struct SQDelegable; struct SQDelegable;
typedef char SQChar;
#define MAX_CHAR 0xFFFF #define MAX_CHAR 0xFFFF
#define SQUIRREL_VERSION "Squirrel 2.2.5 stable - With custom OpenTTD modifications" #define SQUIRREL_VERSION "Squirrel 2.2.5 stable - With custom OpenTTD modifications"
@ -164,17 +163,17 @@ typedef struct tagSQObject
}SQObject; }SQObject;
typedef struct tagSQStackInfos{ typedef struct tagSQStackInfos{
const SQChar* funcname; std::string_view funcname;
const SQChar* source; std::string_view source;
SQInteger line; SQInteger line = -1;
}SQStackInfos; }SQStackInfos;
typedef struct SQVM* HSQUIRRELVM; typedef struct SQVM* HSQUIRRELVM;
typedef SQObject HSQOBJECT; typedef SQObject HSQOBJECT;
typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM); typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM);
typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size); typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size);
typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,const SQChar * /*desc*/,const SQChar * /*source*/,SQInteger /*line*/,SQInteger /*column*/); typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,std::string_view /*desc*/,std::string_view /*source*/,SQInteger /*line*/,SQInteger /*column*/);
typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,const std::string &); typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,std::string_view);
typedef SQInteger (*SQWRITEFUNC)(SQUserPointer,SQUserPointer,SQInteger); typedef SQInteger (*SQWRITEFUNC)(SQUserPointer,SQUserPointer,SQInteger);
typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger); typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger);
@ -182,16 +181,16 @@ typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger);
typedef char32_t (*SQLEXREADFUNC)(SQUserPointer); typedef char32_t (*SQLEXREADFUNC)(SQUserPointer);
typedef struct tagSQRegFunction{ typedef struct tagSQRegFunction{
const SQChar *name; std::string_view name;
SQFUNCTION f; SQFUNCTION f;
SQInteger nparamscheck; SQInteger nparamscheck;
const SQChar *typemask; std::optional<std::string_view> typemask;
}SQRegFunction; }SQRegFunction;
typedef struct tagSQFunctionInfo { typedef struct tagSQFunctionInfo {
SQUserPointer funcid; SQUserPointer funcid;
const SQChar *name; std::string_view name;
const SQChar *source; std::string_view source;
}SQFunctionInfo; }SQFunctionInfo;
@ -213,8 +212,8 @@ SQInteger sq_getvmstate(HSQUIRRELVM v);
void sq_decreaseops(HSQUIRRELVM v, int amount); void sq_decreaseops(HSQUIRRELVM v, int amount);
/*compiler*/ /*compiler*/
SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror); SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,std::string_view sourcename,SQBool raiseerror);
SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror); SQRESULT sq_compilebuffer(HSQUIRRELVM v,std::string_view buffer,std::string_view sourcename,SQBool raiseerror);
void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable); void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable);
void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable); void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable);
void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f); void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f);
@ -235,10 +234,9 @@ SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size);
void sq_newtable(HSQUIRRELVM v); void sq_newtable(HSQUIRRELVM v);
void sq_newarray(HSQUIRRELVM v,SQInteger size); void sq_newarray(HSQUIRRELVM v,SQInteger size);
void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars); void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars);
SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask); SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,std::optional<std::string_view> typemask);
SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx); SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx);
void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len); void sq_pushstring(HSQUIRRELVM v, std::string_view str);
inline void sq_pushstring(HSQUIRRELVM v, const std::string &str, SQInteger len = -1) { sq_pushstring(v, str.data(), len == -1 ? str.size() : len); }
void sq_pushfloat(HSQUIRRELVM v,SQFloat f); void sq_pushfloat(HSQUIRRELVM v,SQFloat f);
void sq_pushinteger(HSQUIRRELVM v,SQInteger n); void sq_pushinteger(HSQUIRRELVM v,SQInteger n);
void sq_pushbool(HSQUIRRELVM v,SQBool b); void sq_pushbool(HSQUIRRELVM v,SQBool b);
@ -250,7 +248,7 @@ SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx);
SQBool sq_instanceof(HSQUIRRELVM v); SQBool sq_instanceof(HSQUIRRELVM v);
void sq_tostring(HSQUIRRELVM v,SQInteger idx); void sq_tostring(HSQUIRRELVM v,SQInteger idx);
void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b); void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b);
SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c); SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,std::string_view &str);
SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i); SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i);
SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f); SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f);
SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b); SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b);
@ -260,10 +258,10 @@ SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPoint
SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag); SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag);
SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag); SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag);
void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook); void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook);
SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize); std::span<char> sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize);
SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger idx,SQFunctionInfo *fi); SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger idx,SQFunctionInfo *fi);
SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars); SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparams,SQUnsignedInteger *nfreevars);
SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name); SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,std::string_view name);
SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p); SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p);
SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag); SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag);
SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize); SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize);
@ -305,10 +303,9 @@ SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx);
/*calls*/ /*calls*/
SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror, int suspend = -1); SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror, int suspend = -1);
SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror); SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror);
const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx); std::optional<std::string_view> sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx);
const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval); std::optional<std::string_view> sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err, SQInteger len = -1); SQRESULT sq_throwerror(HSQUIRRELVM v,std::string_view err);
inline SQRESULT sq_throwerror(HSQUIRRELVM v, const std::string_view err) { return sq_throwerror(v, err.data(), err.size()); }
void sq_reseterror(HSQUIRRELVM v); void sq_reseterror(HSQUIRRELVM v);
void sq_getlasterror(HSQUIRRELVM v); void sq_getlasterror(HSQUIRRELVM v);
@ -318,7 +315,7 @@ void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj);
void sq_addref(HSQUIRRELVM v,HSQOBJECT *po); void sq_addref(HSQUIRRELVM v,HSQOBJECT *po);
SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po); SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po);
void sq_resetobject(HSQOBJECT *po); void sq_resetobject(HSQOBJECT *po);
const SQChar *sq_objtostring(HSQOBJECT *o); std::optional<std::string_view> sq_objtostring(HSQOBJECT *o);
SQBool sq_objtobool(HSQOBJECT *o); SQBool sq_objtobool(HSQOBJECT *o);
SQInteger sq_objtointeger(HSQOBJECT *o); SQInteger sq_objtointeger(HSQOBJECT *o);
SQFloat sq_objtofloat(HSQOBJECT *o); SQFloat sq_objtofloat(HSQOBJECT *o);
@ -363,7 +360,7 @@ void sq_setdebughook(HSQUIRRELVM v);
/* Limit the total number of ops that can be consumed by an operation */ /* Limit the total number of ops that can be consumed by an operation */
struct SQOpsLimiter { struct SQOpsLimiter {
SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, const char *label); SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, std::string_view label);
~SQOpsLimiter(); ~SQOpsLimiter();
private: private:
HSQUIRRELVM _v; HSQUIRRELVM _v;

View File

@ -16,28 +16,22 @@ void sqstd_printcallstack(HSQUIRRELVM v)
SQInteger i; SQInteger i;
SQBool b; SQBool b;
SQFloat f; SQFloat f;
const SQChar *s;
SQInteger level=1; //1 is to skip this function that is level 0 SQInteger level=1; //1 is to skip this function that is level 0
const SQChar *name=nullptr;
SQInteger seq=0; SQInteger seq=0;
pf(v,"\nCALLSTACK\n"); pf(v,"\nCALLSTACK\n");
while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si)))
{ {
const SQChar *fn="unknown"; std::string_view fn="unknown";
const SQChar *src="unknown"; std::string_view src="unknown";
if(si.funcname)fn=si.funcname; if(!si.funcname.empty())fn=si.funcname;
if(si.source) { if(!si.source.empty()) {
/* We don't want to bother users with absolute paths to all AI files. /* We don't want to bother users with absolute paths to all AI files.
* Since the path only reaches NoAI code in a formatted string we have * Since the path only reaches NoAI code in a formatted string we have
* to strip it here. Let's hope nobody installs openttd in a subdirectory * to strip it here. Let's hope nobody installs openttd in a subdirectory
* of a directory named /ai/. */ * of a directory named /ai/. */
src = strstr(si.source, "\\ai\\"); auto p = si.source.find("\\ai\\");
if (!src) src = strstr(si.source, "/ai/"); if (p == std::string_view::npos) p = si.source.find("/ai/");
if (src) { src = (p == std::string_view::npos) ? si.source : si.source.substr(p + 4);
src += 4;
} else {
src = si.source;
}
} }
pf(v,fmt::format("*FUNCTION [{}()] {} line [{}]\n",fn,src,si.line)); pf(v,fmt::format("*FUNCTION [{}()] {} line [{}]\n",fn,src,si.line));
level++; level++;
@ -47,8 +41,9 @@ void sqstd_printcallstack(HSQUIRRELVM v)
for(level=0;level<10;level++){ for(level=0;level<10;level++){
seq=0; seq=0;
while((name = sq_getlocal(v,level,seq))) std::optional<std::string_view> opt;
{ while ((opt = sq_getlocal(v,level,seq)).has_value()) {
std::string_view name = *opt;
seq++; seq++;
switch(sq_gettype(v,-1)) switch(sq_gettype(v,-1))
{ {
@ -66,10 +61,12 @@ void sqstd_printcallstack(HSQUIRRELVM v)
case OT_USERPOINTER: case OT_USERPOINTER:
pf(v,fmt::format("[{}] USERPOINTER\n",name)); pf(v,fmt::format("[{}] USERPOINTER\n",name));
break; break;
case OT_STRING: case OT_STRING: {
sq_getstring(v,-1,&s); std::string_view view;
pf(v,fmt::format("[{}] \"{}\"\n",name,s)); sq_getstring(v,-1,view);
pf(v,fmt::format("[{}] \"{}\"\n",name,view));
break; break;
}
case OT_TABLE: case OT_TABLE:
pf(v,fmt::format("[{}] TABLE\n",name)); pf(v,fmt::format("[{}] TABLE\n",name));
break; break;
@ -117,10 +114,10 @@ static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
{ {
SQPRINTFUNCTION pf = sq_getprintfunc(v); SQPRINTFUNCTION pf = sq_getprintfunc(v);
if(pf) { if(pf) {
const SQChar *sErr = nullptr; std::string_view error;
if(sq_gettop(v)>=1) { if(sq_gettop(v)>=1) {
if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { if(SQ_SUCCEEDED(sq_getstring(v,2,error))) {
pf(v,fmt::format("\nAN ERROR HAS OCCURRED [{}]\n",sErr)); pf(v,fmt::format("\nAN ERROR HAS OCCURRED [{}]\n",error));
} }
else{ else{
pf(v,"\nAN ERROR HAS OCCURRED [unknown]\n"); pf(v,"\nAN ERROR HAS OCCURRED [unknown]\n");
@ -131,7 +128,7 @@ static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
return 0; return 0;
} }
void _sqstd_compiler_error(HSQUIRRELVM v,const SQChar *sErr,const SQChar *sSource,SQInteger line,SQInteger column) void _sqstd_compiler_error(HSQUIRRELVM v,std::string_view sErr,std::string_view sSource,SQInteger line,SQInteger column)
{ {
SQPRINTFUNCTION pf = sq_getprintfunc(v); SQPRINTFUNCTION pf = sq_getprintfunc(v);
if(pf) { if(pf) {

View File

@ -67,7 +67,7 @@ SINGLE_ARG_FUNC(ceil, 1)
SINGLE_ARG_FUNC(exp, 100) SINGLE_ARG_FUNC(exp, 100)
#define _DECL_FUNC(name,nparams,tycheck) {#name,math_##name,nparams,tycheck} #define _DECL_FUNC(name,nparams,tycheck) {#name,math_##name,nparams,tycheck}
static SQRegFunction mathlib_funcs[] = { static const std::initializer_list<SQRegFunction> mathlib_funcs = {
_DECL_FUNC(sqrt,2,".n"), _DECL_FUNC(sqrt,2,".n"),
_DECL_FUNC(sin,2,".n"), _DECL_FUNC(sin,2,".n"),
_DECL_FUNC(cos,2,".n"), _DECL_FUNC(cos,2,".n"),
@ -84,11 +84,10 @@ static SQRegFunction mathlib_funcs[] = {
_DECL_FUNC(exp,2,".n"), _DECL_FUNC(exp,2,".n"),
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS #ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
_DECL_FUNC(srand,2,".n"), _DECL_FUNC(srand,2,".n"),
_DECL_FUNC(rand,1,nullptr), _DECL_FUNC(rand,1,std::nullopt),
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */ #endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
_DECL_FUNC(fabs,2,".n"), _DECL_FUNC(fabs,2,".n"),
_DECL_FUNC(abs,2,".n"), _DECL_FUNC(abs,2,".n"),
{nullptr,nullptr,0,nullptr},
}; };
#ifndef M_PI #ifndef M_PI
@ -97,21 +96,19 @@ static SQRegFunction mathlib_funcs[] = {
SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) SQRESULT sqstd_register_mathlib(HSQUIRRELVM v)
{ {
SQInteger i=0; for(auto &func : mathlib_funcs) {
while(mathlib_funcs[i].name!=nullptr) { sq_pushstring(v,func.name);
sq_pushstring(v,mathlib_funcs[i].name,-1); sq_newclosure(v,func.f,0);
sq_newclosure(v,mathlib_funcs[i].f,0); sq_setparamscheck(v,func.nparamscheck,func.typemask);
sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask); sq_setnativeclosurename(v,-1,func.name);
sq_setnativeclosurename(v,-1,mathlib_funcs[i].name);
sq_createslot(v,-3); sq_createslot(v,-3);
i++;
} }
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS #ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
sq_pushstring(v,"RAND_MAX",-1); sq_pushstring(v,"RAND_MAX");
sq_pushinteger(v,RAND_MAX); sq_pushinteger(v,RAND_MAX);
sq_createslot(v,-3); sq_createslot(v,-3);
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */ #endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
sq_pushstring(v,"PI",-1); sq_pushstring(v,"PI");
sq_pushfloat(v,(SQFloat)M_PI); sq_pushfloat(v,(SQFloat)M_PI);
sq_createslot(v,-3); sq_createslot(v,-3);
return SQ_OK; return SQ_OK;

View File

@ -1,632 +0,0 @@
/* see copyright notice in squirrel.h */
#include <squirrel.h>
#include <exception>
#include "sqstdstring.h"
#ifdef _UNICODE
#define scisprint iswprint
#else
#define scisprint isprint
#endif
#ifdef _DEBUG
static const SQChar *g_nnames[] =
{
"NONE","OP_GREEDY", "OP_OR",
"OP_EXPR","OP_NOCAPEXPR","OP_DOT", "OP_CLASS",
"OP_CCLASS","OP_NCLASS","OP_RANGE","OP_CHAR",
"OP_EOL","OP_BOL","OP_WB"
};
#endif
#define OP_GREEDY (MAX_CHAR+1) // * + ? {n}
#define OP_OR (MAX_CHAR+2)
#define OP_EXPR (MAX_CHAR+3) //parentesis ()
#define OP_NOCAPEXPR (MAX_CHAR+4) //parentesis (?:)
#define OP_DOT (MAX_CHAR+5)
#define OP_CLASS (MAX_CHAR+6)
#define OP_CCLASS (MAX_CHAR+7)
#define OP_NCLASS (MAX_CHAR+8) //negates class the [^
#define OP_RANGE (MAX_CHAR+9)
#define OP_CHAR (MAX_CHAR+10)
#define OP_EOL (MAX_CHAR+11)
#define OP_BOL (MAX_CHAR+12)
#define OP_WB (MAX_CHAR+13)
#define SQREX_SYMBOL_ANY_CHAR ('.')
#define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+')
#define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*')
#define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?')
#define SQREX_SYMBOL_BRANCH ('|')
#define SQREX_SYMBOL_END_OF_STRING ('$')
#define SQREX_SYMBOL_BEGINNING_OF_STRING ('^')
#define SQREX_SYMBOL_ESCAPE_CHAR ('\\')
typedef int SQRexNodeType;
typedef struct tagSQRexNode{
SQRexNodeType type;
SQInteger left;
SQInteger right;
SQInteger next;
}SQRexNode;
struct SQRex{
const SQChar *_eol;
const SQChar *_bol;
const SQChar *_p;
SQInteger _first;
SQInteger _op;
SQRexNode *_nodes;
SQInteger _nallocated;
SQInteger _nsize;
SQInteger _nsubexpr;
SQRexMatch *_matches;
SQInteger _currsubexp;
const SQChar **_error;
};
static SQInteger sqstd_rex_list(SQRex *exp);
static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type)
{
SQRexNode n;
n.type = type;
n.next = n.right = n.left = -1;
if(type == OP_EXPR)
n.right = exp->_nsubexpr++;
if(exp->_nallocated < (exp->_nsize + 1)) {
SQInteger oldsize = exp->_nallocated;
exp->_nallocated *= 2;
exp->_nodes = (SQRexNode *)sq_realloc(exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode));
}
exp->_nodes[exp->_nsize++] = n;
SQInteger newid = exp->_nsize - 1;
return (SQInteger)newid;
}
static void sqstd_rex_error(SQRex *exp,const SQChar *error)
{
if(exp->_error) *exp->_error = error;
throw std::exception();
}
static void sqstd_rex_expect(SQRex *exp, SQChar n){
if((*exp->_p) != n)
sqstd_rex_error(exp, "expected paren");
exp->_p++;
}
static SQChar sqstd_rex_escapechar(SQRex *exp)
{
if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){
exp->_p++;
switch(*exp->_p) {
case 'v': exp->_p++; return '\v';
case 'n': exp->_p++; return '\n';
case 't': exp->_p++; return '\t';
case 'r': exp->_p++; return '\r';
case 'f': exp->_p++; return '\f';
default: return (*exp->_p++);
}
} else if(!scisprint(*exp->_p)) sqstd_rex_error(exp,"letter expected");
return (*exp->_p++);
}
static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid)
{
SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS);
exp->_nodes[n].left = classid;
return n;
}
static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass)
{
SQChar t;
if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) {
exp->_p++;
switch(*exp->_p) {
case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n');
case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t');
case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r');
case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f');
case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v');
case 'a': case 'A': case 'w': case 'W': case 's': case 'S':
case 'd': case 'D': case 'x': case 'X': case 'c': case 'C':
case 'p': case 'P': case 'l': case 'u':
{
t = *exp->_p; exp->_p++;
return sqstd_rex_charclass(exp,t);
}
case 'b':
case 'B':
if(!isclass) {
SQInteger node = sqstd_rex_newnode(exp,OP_WB);
exp->_nodes[node].left = *exp->_p;
exp->_p++;
return node;
} //else default
default:
t = *exp->_p; exp->_p++;
return sqstd_rex_newnode(exp,t);
}
}
else if(!scisprint(*exp->_p)) {
sqstd_rex_error(exp,"letter expected");
}
t = *exp->_p; exp->_p++;
return sqstd_rex_newnode(exp,t);
}
static SQInteger sqstd_rex_class(SQRex *exp)
{
SQInteger ret = -1;
SQInteger first = -1,chain;
if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){
ret = sqstd_rex_newnode(exp,OP_NCLASS);
exp->_p++;
}else ret = sqstd_rex_newnode(exp,OP_CLASS);
if(*exp->_p == ']') sqstd_rex_error(exp,"empty class");
chain = ret;
while(*exp->_p != ']' && exp->_p != exp->_eol) {
if(*exp->_p == '-' && first != -1){
SQInteger r;
if(*exp->_p++ == ']') sqstd_rex_error(exp,"unfinished range");
r = sqstd_rex_newnode(exp,OP_RANGE);
if(exp->_nodes[first].type>*exp->_p) sqstd_rex_error(exp,"invalid range");
if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,"cannot use character classes in ranges");
exp->_nodes[r].left = exp->_nodes[first].type;
SQInteger t = sqstd_rex_escapechar(exp);
exp->_nodes[r].right = t;
exp->_nodes[chain].next = r;
chain = r;
first = -1;
}
else{
if(first!=-1){
SQInteger c = first;
exp->_nodes[chain].next = c;
chain = c;
first = sqstd_rex_charnode(exp,SQTrue);
}
else{
first = sqstd_rex_charnode(exp,SQTrue);
}
}
}
if(first!=-1){
SQInteger c = first;
exp->_nodes[chain].next = c;
chain = c;
first = -1;
}
/* hack? */
exp->_nodes[ret].left = exp->_nodes[ret].next;
exp->_nodes[ret].next = -1;
return ret;
}
static SQInteger sqstd_rex_parsenumber(SQRex *exp)
{
SQInteger ret = *exp->_p-'0';
SQInteger positions = 10;
exp->_p++;
while(isdigit(*exp->_p)) {
ret = ret*10+(*exp->_p++-'0');
if(positions==1000000000) sqstd_rex_error(exp,"overflow in numeric constant");
positions *= 10;
};
return ret;
}
static SQInteger sqstd_rex_element(SQRex *exp)
{
SQInteger ret = -1;
switch(*exp->_p)
{
case '(': {
SQInteger expr;
exp->_p++;
if(*exp->_p =='?') {
exp->_p++;
sqstd_rex_expect(exp,':');
expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR);
}
else
expr = sqstd_rex_newnode(exp,OP_EXPR);
SQInteger newn = sqstd_rex_list(exp);
exp->_nodes[expr].left = newn;
ret = expr;
sqstd_rex_expect(exp,')');
}
break;
case '[':
exp->_p++;
ret = sqstd_rex_class(exp);
sqstd_rex_expect(exp,']');
break;
case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break;
case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break;
default:
ret = sqstd_rex_charnode(exp,SQFalse);
break;
}
SQInteger op;
SQBool isgreedy = SQFalse;
unsigned short p0 = 0, p1 = 0;
switch(*exp->_p){
case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break;
case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break;
case '{':
exp->_p++;
if(!isdigit(*exp->_p)) sqstd_rex_error(exp,"number expected");
p0 = (unsigned short)sqstd_rex_parsenumber(exp);
/*******************************/
switch(*exp->_p) {
case '}':
p1 = p0; exp->_p++;
break;
case ',':
exp->_p++;
p1 = 0xFFFF;
if(isdigit(*exp->_p)){
p1 = (unsigned short)sqstd_rex_parsenumber(exp);
}
sqstd_rex_expect(exp,'}');
break;
default:
sqstd_rex_error(exp,", or } expected");
}
/*******************************/
isgreedy = SQTrue;
break;
}
if(isgreedy) {
SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY);
op = OP_GREEDY;
exp->_nodes[nnode].left = ret;
exp->_nodes[nnode].right = ((p0)<<16)|p1;
ret = nnode;
}
if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) {
SQInteger nnode = sqstd_rex_element(exp);
exp->_nodes[ret].next = nnode;
}
return ret;
}
static SQInteger sqstd_rex_list(SQRex *exp)
{
SQInteger ret=-1,e;
if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) {
exp->_p++;
ret = sqstd_rex_newnode(exp,OP_BOL);
}
e = sqstd_rex_element(exp);
if(ret != -1) {
exp->_nodes[ret].next = e;
}
else ret = e;
if(*exp->_p == SQREX_SYMBOL_BRANCH) {
SQInteger temp,tright;
exp->_p++;
temp = sqstd_rex_newnode(exp,OP_OR);
exp->_nodes[temp].left = ret;
tright = sqstd_rex_list(exp);
exp->_nodes[temp].right = tright;
ret = temp;
}
return ret;
}
static SQBool sqstd_rex_matchcclass(SQInteger cclass,SQChar c)
{
switch(cclass) {
case 'a': return isalpha(c)?SQTrue:SQFalse;
case 'A': return !isalpha(c)?SQTrue:SQFalse;
case 'w': return (isalnum(c) || c == '_')?SQTrue:SQFalse;
case 'W': return (!isalnum(c) && c != '_')?SQTrue:SQFalse;
case 's': return isspace(c)?SQTrue:SQFalse;
case 'S': return !isspace(c)?SQTrue:SQFalse;
case 'd': return isdigit(c)?SQTrue:SQFalse;
case 'D': return !isdigit(c)?SQTrue:SQFalse;
case 'x': return isxdigit(c)?SQTrue:SQFalse;
case 'X': return !isxdigit(c)?SQTrue:SQFalse;
case 'c': return iscntrl(c)?SQTrue:SQFalse;
case 'C': return !iscntrl(c)?SQTrue:SQFalse;
case 'p': return ispunct(c)?SQTrue:SQFalse;
case 'P': return !ispunct(c)?SQTrue:SQFalse;
case 'l': return islower(c)?SQTrue:SQFalse;
case 'u': return isupper(c)?SQTrue:SQFalse;
}
return SQFalse; /*cannot happen*/
}
static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,SQInteger c)
{
do {
switch(node->type) {
case OP_RANGE:
if(c >= node->left && c <= node->right) return SQTrue;
break;
case OP_CCLASS:
if(sqstd_rex_matchcclass(node->left,c)) return SQTrue;
break;
default:
if(c == node->type)return SQTrue;
}
} while((node->next != -1) && (node = &exp->_nodes[node->next]));
return SQFalse;
}
static const SQChar *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const SQChar *str,SQRexNode *next)
{
SQRexNodeType type = node->type;
switch(type) {
case OP_GREEDY: {
//SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : nullptr;
SQRexNode *greedystop = nullptr;
SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0;
const SQChar *s=str, *good = str;
if(node->next != -1) {
greedystop = &exp->_nodes[node->next];
}
else {
greedystop = next;
}
while((nmaches == 0xFFFF || nmaches < p1)) {
const SQChar *stop;
if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop)))
break;
nmaches++;
good=s;
if(greedystop) {
//checks that 0 matches satisfy the expression(if so skips)
//if not would always stop(for instance if is a '?')
if(greedystop->type != OP_GREEDY ||
(greedystop->type == OP_GREEDY && ((greedystop->right >> 16)&0x0000FFFF) != 0))
{
SQRexNode *gnext = nullptr;
if(greedystop->next != -1) {
gnext = &exp->_nodes[greedystop->next];
}else if(next && next->next != -1){
gnext = &exp->_nodes[next->next];
}
stop = sqstd_rex_matchnode(exp,greedystop,s,gnext);
if(stop) {
//if satisfied stop it
if(p0 == p1 && p0 == nmaches) break;
else if(nmaches >= p0 && p1 == 0xFFFF) break;
else if(nmaches >= p0 && nmaches <= p1) break;
}
}
}
if(s >= exp->_eol)
break;
}
if(p0 == p1 && p0 == nmaches) return good;
else if(nmaches >= p0 && p1 == 0xFFFF) return good;
else if(nmaches >= p0 && nmaches <= p1) return good;
return nullptr;
}
case OP_OR: {
const SQChar *asd = str;
SQRexNode *temp=&exp->_nodes[node->left];
while( (asd = sqstd_rex_matchnode(exp,temp,asd,nullptr)) ) {
if(temp->next != -1)
temp = &exp->_nodes[temp->next];
else
return asd;
}
asd = str;
temp = &exp->_nodes[node->right];
while( (asd = sqstd_rex_matchnode(exp,temp,asd,nullptr)) ) {
if(temp->next != -1)
temp = &exp->_nodes[temp->next];
else
return asd;
}
return nullptr;
break;
}
case OP_EXPR:
case OP_NOCAPEXPR:{
SQRexNode *n = &exp->_nodes[node->left];
const SQChar *cur = str;
SQInteger capture = -1;
if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) {
capture = exp->_currsubexp;
exp->_matches[capture].begin = cur;
exp->_currsubexp++;
}
do {
SQRexNode *subnext = nullptr;
if(n->next != -1) {
subnext = &exp->_nodes[n->next];
}else {
subnext = next;
}
if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) {
if(capture != -1){
exp->_matches[capture].begin = 0;
exp->_matches[capture].len = 0;
}
return nullptr;
}
} while((n->next != -1) && (n = &exp->_nodes[n->next]));
if(capture != -1)
exp->_matches[capture].len = cur - exp->_matches[capture].begin;
return cur;
}
case OP_WB:
if((str == exp->_bol && !isspace(*str))
|| (str == exp->_eol && !isspace(*(str-1)))
|| (!isspace(*str) && isspace(*(str+1)))
|| (isspace(*str) && !isspace(*(str+1))) ) {
return (node->left == 'b')?str:nullptr;
}
return (node->left == 'b')?nullptr:str;
case OP_BOL:
if(str == exp->_bol) return str;
return nullptr;
case OP_EOL:
if(str == exp->_eol) return str;
return nullptr;
case OP_DOT:{
*str++;
}
return str;
case OP_NCLASS:
case OP_CLASS:
if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) {
*str++;
return str;
}
return nullptr;
case OP_CCLASS:
if(sqstd_rex_matchcclass(node->left,*str)) {
*str++;
return str;
}
return nullptr;
default: /* char */
if(*str != (SQChar)node->type) return nullptr;
*str++;
return str;
}
return nullptr;
}
/* public api */
SQRex *sqstd_rex_compile(const SQChar *pattern,const SQChar **error)
{
SQRex *exp = (SQRex *)sq_malloc(sizeof(SQRex));
exp->_eol = exp->_bol = nullptr;
exp->_p = pattern;
exp->_nallocated = (SQInteger)strlen(pattern) * sizeof(SQChar);
exp->_nodes = (SQRexNode *)sq_malloc(exp->_nallocated * sizeof(SQRexNode));
exp->_nsize = 0;
exp->_matches = 0;
exp->_nsubexpr = 0;
exp->_first = sqstd_rex_newnode(exp,OP_EXPR);
exp->_error = error;
try {
SQInteger res = sqstd_rex_list(exp);
exp->_nodes[exp->_first].left = res;
if(*exp->_p!='\0')
sqstd_rex_error(exp,"unexpected character");
#ifdef _DEBUG
{
SQInteger nsize,i;
SQRexNode *t;
nsize = exp->_nsize;
t = &exp->_nodes[0];
printf("\n");
/* XXX -- The (int) casts are needed to silent warnings on 64bit systems (SQInteger is 64bit, %d assumes 32bit, (int) is 32bit) */
for(i = 0;i < nsize; i++) {
if(exp->_nodes[i].type>MAX_CHAR)
printf("[%02d] %10s ",(int)i,g_nnames[exp->_nodes[i].type-MAX_CHAR]);
else
printf("[%02d] %10c ",(int)i,exp->_nodes[i].type);
printf("left %02d right %02d next %02d\n",(int)exp->_nodes[i].left,(int)exp->_nodes[i].right,(int)exp->_nodes[i].next);
}
printf("\n");
}
#endif
exp->_matches = (SQRexMatch *) sq_malloc(exp->_nsubexpr * sizeof(SQRexMatch));
memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch));
}
catch (...) {
sqstd_rex_free(exp);
return nullptr;
}
return exp;
}
void sqstd_rex_free(SQRex *exp)
{
if(exp) {
if(exp->_nodes) sq_free(exp->_nodes,exp->_nallocated * sizeof(SQRexNode));
if(exp->_matches) sq_free(exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch));
sq_free(exp,sizeof(SQRex));
}
}
SQBool sqstd_rex_match(SQRex* exp,const SQChar* text)
{
const SQChar* res = nullptr;
exp->_bol = text;
exp->_eol = text + strlen(text);
exp->_currsubexp = 0;
res = sqstd_rex_matchnode(exp,exp->_nodes,text,nullptr);
if(res == nullptr || res != exp->_eol)
return SQFalse;
return SQTrue;
}
SQBool sqstd_rex_searchrange(SQRex* exp,const SQChar* text_begin,const SQChar* text_end,const SQChar** out_begin, const SQChar** out_end)
{
const SQChar *cur = nullptr;
SQInteger node = exp->_first;
if(text_begin >= text_end) return SQFalse;
exp->_bol = text_begin;
exp->_eol = text_end;
do {
cur = text_begin;
while(node != -1) {
exp->_currsubexp = 0;
cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,nullptr);
if(!cur)
break;
node = exp->_nodes[node].next;
}
*text_begin++;
} while(cur == nullptr && text_begin != text_end);
if(cur == nullptr)
return SQFalse;
--text_begin;
if(out_begin) *out_begin = text_begin;
if(out_end) *out_end = cur;
return SQTrue;
}
SQBool sqstd_rex_search(SQRex* exp,const SQChar* text, const SQChar** out_begin, const SQChar** out_end)
{
return sqstd_rex_searchrange(exp,text,text + strlen(text),out_begin,out_end);
}
SQInteger sqstd_rex_getsubexpcount(SQRex* exp)
{
return exp->_nsubexpr;
}
SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp)
{
if( n<0 || n >= exp->_nsubexpr) return SQFalse;
*subexp = exp->_matches[n];
return SQTrue;
}

View File

@ -1,270 +0,0 @@
/* see copyright notice in squirrel.h */
#include <squirrel.h>
#include <sqstdstring.h>
#define scstrchr strchr
#define scatoi atoi
#define scstrtok strtok
#define MAX_FORMAT_LEN 20
#define MAX_WFORMAT_LEN 3
#define ADDITIONAL_FORMAT_SPACE (100*sizeof(SQChar))
static SQInteger validate_format(HSQUIRRELVM v, SQChar *fmt, const SQChar *src, SQInteger n,SQInteger &width)
{
SQChar swidth[MAX_WFORMAT_LEN];
SQInteger wc = 0;
SQInteger start = n;
fmt[0] = '%';
while (scstrchr("-+ #0", src[n])) n++;
while (isdigit(src[n])) {
swidth[wc] = src[n];
n++;
wc++;
if(wc>=MAX_WFORMAT_LEN)
return sq_throwerror(v,"width format too long");
}
swidth[wc] = '\0';
if(wc > 0) {
width = atoi(swidth);
}
else
width = 0;
if (src[n] == '.') {
n++;
wc = 0;
while (isdigit(src[n])) {
swidth[wc] = src[n];
n++;
wc++;
if(wc>=MAX_WFORMAT_LEN)
return sq_throwerror(v,"precision format too long");
}
swidth[wc] = '\0';
if(wc > 0) {
width += atoi(swidth);
}
}
if (n-start > MAX_FORMAT_LEN )
return sq_throwerror(v,"format too long");
memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(SQChar));
fmt[(n-start)+2] = '\0';
return n;
}
static void __strip_l(const SQChar *str,const SQChar **start)
{
const SQChar *t = str;
while(((*t) != '\0') && isspace(*t)){ t++; }
*start = t;
}
static void __strip_r(const SQChar *str,SQInteger len,const SQChar **end)
{
if(len == 0) {
*end = str;
return;
}
const SQChar *t = &str[len-1];
while(t != str && isspace(*t)) { t--; }
*end = t+1;
}
static SQInteger _string_strip(HSQUIRRELVM v)
{
const SQChar *str,*start,*end;
sq_getstring(v,2,&str);
SQInteger len = sq_getsize(v,2);
__strip_l(str,&start);
__strip_r(str,len,&end);
sq_pushstring(v,start,end - start);
return 1;
}
static SQInteger _string_lstrip(HSQUIRRELVM v)
{
const SQChar *str,*start;
sq_getstring(v,2,&str);
__strip_l(str,&start);
sq_pushstring(v,start,-1);
return 1;
}
static SQInteger _string_rstrip(HSQUIRRELVM v)
{
const SQChar *str,*end;
sq_getstring(v,2,&str);
SQInteger len = sq_getsize(v,2);
__strip_r(str,len,&end);
sq_pushstring(v,str,end - str);
return 1;
}
static SQInteger _string_split(HSQUIRRELVM v)
{
const SQChar *str,*seps;
SQChar *stemp,*tok;
sq_getstring(v,2,&str);
sq_getstring(v,3,&seps);
if(sq_getsize(v,3) == 0) return sq_throwerror(v,"empty separators string");
SQInteger memsize = (sq_getsize(v,2)+1)*sizeof(SQChar);
stemp = sq_getscratchpad(v,memsize);
memcpy(stemp,str,memsize);
tok = scstrtok(stemp,seps);
sq_newarray(v,0);
while( tok != nullptr ) {
sq_pushstring(v,tok,-1);
sq_arrayappend(v,-2);
tok = scstrtok( nullptr, seps );
}
return 1;
}
#define SETUP_REX(v) \
SQRex *self = nullptr; \
sq_getinstanceup(v,1,(SQUserPointer *)&self,0);
static SQInteger _rexobj_releasehook(SQUserPointer p, SQInteger size)
{
SQRex *self = ((SQRex *)p);
sqstd_rex_free(self);
return 1;
}
static SQInteger _regexp_match(HSQUIRRELVM v)
{
SETUP_REX(v);
const SQChar *str;
sq_getstring(v,2,&str);
if(sqstd_rex_match(self,str) == SQTrue)
{
sq_pushbool(v,SQTrue);
return 1;
}
sq_pushbool(v,SQFalse);
return 1;
}
static void _addrexmatch(HSQUIRRELVM v,const SQChar *str,const SQChar *begin,const SQChar *end)
{
sq_newtable(v);
sq_pushstring(v,"begin",-1);
sq_pushinteger(v,begin - str);
sq_rawset(v,-3);
sq_pushstring(v,"end",-1);
sq_pushinteger(v,end - str);
sq_rawset(v,-3);
}
static SQInteger _regexp_search(HSQUIRRELVM v)
{
SETUP_REX(v);
const SQChar *str,*begin,*end;
SQInteger start = 0;
sq_getstring(v,2,&str);
if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
_addrexmatch(v,str,begin,end);
return 1;
}
return 0;
}
static SQInteger _regexp_capture(HSQUIRRELVM v)
{
SETUP_REX(v);
const SQChar *str,*begin,*end;
SQInteger start = 0;
sq_getstring(v,2,&str);
if(sq_gettop(v) > 2) sq_getinteger(v,3,&start);
if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) {
SQInteger n = sqstd_rex_getsubexpcount(self);
SQRexMatch match;
sq_newarray(v,0);
for(SQInteger i = 0;i < n; i++) {
sqstd_rex_getsubexp(self,i,&match);
if(match.len > 0)
_addrexmatch(v,str,match.begin,match.begin+match.len);
else
_addrexmatch(v,str,str,str); //empty match
sq_arrayappend(v,-2);
}
return 1;
}
return 0;
}
static SQInteger _regexp_subexpcount(HSQUIRRELVM v)
{
SETUP_REX(v);
sq_pushinteger(v,sqstd_rex_getsubexpcount(self));
return 1;
}
static SQInteger _regexp_constructor(HSQUIRRELVM v)
{
const SQChar *error,*pattern;
sq_getstring(v,2,&pattern);
SQRex *rex = sqstd_rex_compile(pattern,&error);
if(!rex) return sq_throwerror(v,error);
sq_setinstanceup(v,1,rex);
sq_setreleasehook(v,1,_rexobj_releasehook);
return 0;
}
static SQInteger _regexp__typeof(HSQUIRRELVM v)
{
sq_pushstring(v,"regexp",-1);
return 1;
}
#define _DECL_REX_FUNC(name,nparams,pmask) {#name,_regexp_##name,nparams,pmask}
static SQRegFunction rexobj_funcs[]={
_DECL_REX_FUNC(constructor,2,".s"),
_DECL_REX_FUNC(search,-2,"xsn"),
_DECL_REX_FUNC(match,2,"xs"),
_DECL_REX_FUNC(capture,-2,"xsn"),
_DECL_REX_FUNC(subexpcount,1,"x"),
_DECL_REX_FUNC(_typeof,1,"x"),
{0,0,0,0}
};
#define _DECL_FUNC(name,nparams,pmask) {#name,_string_##name,nparams,pmask}
static SQRegFunction stringlib_funcs[]={
_DECL_FUNC(format,-2,".s"),
_DECL_FUNC(strip,2,".s"),
_DECL_FUNC(lstrip,2,".s"),
_DECL_FUNC(rstrip,2,".s"),
_DECL_FUNC(split,3,".ss"),
{0,0,0,0}
};
SQInteger sqstd_register_stringlib(HSQUIRRELVM v)
{
sq_pushstring(v,"regexp",-1);
sq_newclass(v,SQFalse);
SQInteger i = 0;
while(rexobj_funcs[i].name != 0) {
SQRegFunction &f = rexobj_funcs[i];
sq_pushstring(v,f.name,-1);
sq_newclosure(v,f.f,0);
sq_setparamscheck(v,f.nparamscheck,f.typemask);
sq_setnativeclosurename(v,-1,f.name);
sq_createslot(v,-3);
i++;
}
sq_createslot(v,-3);
i = 0;
while(stringlib_funcs[i].name!=0)
{
sq_pushstring(v,stringlib_funcs[i].name,-1);
sq_newclosure(v,stringlib_funcs[i].f,0);
sq_setparamscheck(v,stringlib_funcs[i].nparamscheck,stringlib_funcs[i].typemask);
sq_setnativeclosurename(v,-1,stringlib_funcs[i].name);
sq_createslot(v,-3);
i++;
}
return 1;
}

View File

@ -18,6 +18,7 @@
#include "sqfuncstate.h" #include "sqfuncstate.h"
#include "sqclass.h" #include "sqclass.h"
#include "../../../core/string_consumer.hpp"
#include "../../../string_func.h" #include "../../../string_func.h"
#include "../../../safeguards.h" #include "../../../safeguards.h"
@ -131,7 +132,7 @@ void sq_close(HSQUIRRELVM v)
sq_delete(ss, SQSharedState); sq_delete(ss, SQSharedState);
} }
SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror) SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,std::string_view sourcename,SQBool raiseerror)
{ {
SQObjectPtr o; SQObjectPtr o;
if(Compile(v, read, p, sourcename, o, raiseerror != 0, _ss(v)->_debuginfo)) { if(Compile(v, read, p, sourcename, o, raiseerror != 0, _ss(v)->_debuginfo)) {
@ -172,12 +173,12 @@ SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po)
#endif #endif
} }
const SQChar *sq_objtostring(HSQOBJECT *o) std::optional<std::string_view> sq_objtostring(HSQOBJECT *o)
{ {
if(sq_type(*o) == OT_STRING) { if(sq_type(*o) == OT_STRING) {
return _stringval(*o); return _stringval(*o);
} }
return nullptr; return std::nullopt;
} }
SQInteger sq_objtointeger(HSQOBJECT *o) SQInteger sq_objtointeger(HSQOBJECT *o)
@ -209,11 +210,9 @@ void sq_pushnull(HSQUIRRELVM v)
v->Push(_null_); v->Push(_null_);
} }
void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len) void sq_pushstring(HSQUIRRELVM v,std::string_view s)
{ {
if(s) v->Push(SQObjectPtr(SQString::Create(_ss(v), s)));
v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len)));
else v->Push(_null_);
} }
void sq_pushinteger(HSQUIRRELVM v,SQInteger n) void sq_pushinteger(HSQUIRRELVM v,SQInteger n)
@ -375,7 +374,7 @@ SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparam
return sq_throwerror(v,"the object is not a closure"); return sq_throwerror(v,"the object is not a closure");
} }
SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name) SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,std::string_view name)
{ {
SQObject o = stack_get(v, idx); SQObject o = stack_get(v, idx);
if(sq_isnativeclosure(o)) { if(sq_isnativeclosure(o)) {
@ -386,16 +385,16 @@ SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name)
return sq_throwerror(v,"the object is not a nativeclosure"); return sq_throwerror(v,"the object is not a nativeclosure");
} }
SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask) SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,std::optional<std::string_view> typemask)
{ {
SQObject o = stack_get(v, -1); SQObject o = stack_get(v, -1);
if(!sq_isnativeclosure(o)) if(!sq_isnativeclosure(o))
return sq_throwerror(v, "native closure expected"); return sq_throwerror(v, "native closure expected");
SQNativeClosure *nc = _nativeclosure(o); SQNativeClosure *nc = _nativeclosure(o);
nc->_nparamscheck = nparamscheck; nc->_nparamscheck = nparamscheck;
if(typemask) { if(typemask.has_value()) {
SQIntVec res; SQIntVec res;
if(!CompileTypemask(res, typemask)) if(!CompileTypemask(res, *typemask))
return sq_throwerror(v, "invalid typemask"); return sq_throwerror(v, "invalid typemask");
nc->_typecheck.copy(res); nc->_typecheck.copy(res);
} }
@ -552,11 +551,11 @@ SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b)
return SQ_ERROR; return SQ_ERROR;
} }
SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c) SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,std::string_view &str)
{ {
SQObjectPtr *o = nullptr; SQObjectPtr *o = nullptr;
_GETSAFE_OBJ(v, idx, OT_STRING,o); _GETSAFE_OBJ(v, idx, OT_STRING,o);
*c = _stringval(*o); str = _stringval(*o);
return SQ_OK; return SQ_OK;
} }
@ -584,7 +583,7 @@ SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx)
SQObjectPtr &o = stack_get(v, idx); SQObjectPtr &o = stack_get(v, idx);
SQObjectType type = type(o); SQObjectType type = type(o);
switch(type) { switch(type) {
case OT_STRING: return _string(o)->_len; case OT_STRING: return _string(o)->View().size();
case OT_TABLE: return _table(o)->CountUsed(); case OT_TABLE: return _table(o)->CountUsed();
case OT_ARRAY: return _array(o)->Size(); case OT_ARRAY: return _array(o)->Size();
case OT_USERDATA: return _userdata(o)->_size; case OT_USERDATA: return _userdata(o)->_size;
@ -896,7 +895,7 @@ SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po)
return SQ_OK; return SQ_OK;
} }
const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx) std::optional<std::string_view> sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx)
{ {
SQUnsignedInteger cstksize=v->_callsstacksize; SQUnsignedInteger cstksize=v->_callsstacksize;
SQUnsignedInteger lvl=(cstksize-level)-1; SQUnsignedInteger lvl=(cstksize-level)-1;
@ -908,7 +907,7 @@ const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedIntege
} }
SQVM::CallInfo &ci=v->_callsstack[lvl]; SQVM::CallInfo &ci=v->_callsstack[lvl];
if(type(ci._closure)!=OT_CLOSURE) if(type(ci._closure)!=OT_CLOSURE)
return nullptr; return std::nullopt;
SQClosure *c=_closure(ci._closure); SQClosure *c=_closure(ci._closure);
SQFunctionProto *func=_funcproto(c->_function); SQFunctionProto *func=_funcproto(c->_function);
if(func->_noutervalues > (SQInteger)idx) { if(func->_noutervalues > (SQInteger)idx) {
@ -918,7 +917,7 @@ const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedIntege
idx -= func->_noutervalues; idx -= func->_noutervalues;
return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1); return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1);
} }
return nullptr; return std::nullopt;
} }
void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj) void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj)
@ -931,9 +930,9 @@ void sq_resetobject(HSQOBJECT *po)
po->_unVal.pUserPointer=nullptr;po->_type=OT_NULL; po->_unVal.pUserPointer=nullptr;po->_type=OT_NULL;
} }
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err, SQInteger len) SQRESULT sq_throwerror(HSQUIRRELVM v,std::string_view error)
{ {
v->_lasterror=SQString::Create(_ss(v),err, len); v->_lasterror=SQString::Create(_ss(v),error);
return -1; return -1;
} }
@ -978,6 +977,10 @@ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror,
if(!v->_suspended) { if(!v->_suspended) {
v->Pop(params);//pop closure and args v->Pop(params);//pop closure and args
} }
if (!v->_can_suspend && v->IsOpsTillSuspendError()) {
v->Raise_Error(fmt::format("excessive CPU usage in {}", v->_ops_till_suspend_error_label));
return SQ_ERROR;
}
if(retval){ if(retval){
v->Push(res); return SQ_OK; v->Push(res); return SQ_OK;
} }
@ -1075,7 +1078,7 @@ SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up)
return SQ_OK; return SQ_OK;
} }
SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize) std::span<char> sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize)
{ {
return _ss(v)->GetScratchPad(minsize); return _ss(v)->GetScratchPad(minsize);
} }
@ -1089,19 +1092,18 @@ SQInteger sq_collectgarbage(HSQUIRRELVM v)
#endif #endif
} }
const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) std::optional<std::string_view> sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
{ {
SQObjectPtr &self = stack_get(v,idx); SQObjectPtr &self = stack_get(v,idx);
const SQChar *name = nullptr;
if(type(self) == OT_CLOSURE) { if(type(self) == OT_CLOSURE) {
if(_closure(self)->_outervalues.size()>nval) { if(_closure(self)->_outervalues.size()>nval) {
v->Push(_closure(self)->_outervalues[nval]); v->Push(_closure(self)->_outervalues[nval]);
SQFunctionProto *fp = _funcproto(_closure(self)->_function); SQFunctionProto *fp = _funcproto(_closure(self)->_function);
SQOuterVar &ov = fp->_outervalues[nval]; SQOuterVar &ov = fp->_outervalues[nval];
name = _stringval(ov._name); return _stringval(ov._name);
} }
} }
return name; return std::nullopt;
} }
SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
@ -1253,44 +1255,16 @@ SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx)
return SQ_ERROR; return SQ_ERROR;
} }
struct BufState{
const SQChar *buf;
SQInteger ptr;
SQInteger size;
};
char32_t buf_lexfeed(SQUserPointer file) char32_t buf_lexfeed(SQUserPointer file)
{ {
/* Convert an UTF-8 character into a char32_t */ /* Convert an UTF-8 character into a char32_t */
BufState *buf = (BufState *)file; StringConsumer &consumer = *reinterpret_cast<StringConsumer *>(file);
const char *p = &buf->buf[buf->ptr]; return consumer.AnyBytesLeft() ? consumer.ReadUtf8(-1) : 0;
if (buf->size < buf->ptr + 1) return 0;
/* Read the first character, and get the length based on UTF-8 specs. If invalid, bail out. */
uint len = Utf8EncodedCharLen(*p);
if (len == 0) {
buf->ptr++;
return -1;
} }
/* Read the remaining bits. */ SQRESULT sq_compilebuffer(HSQUIRRELVM v,std::string_view buffer,std::string_view sourcename,SQBool raiseerror) {
if (buf->size < buf->ptr + len) return 0; StringConsumer consumer{buffer};
buf->ptr += len; return sq_compile(v, buf_lexfeed, &consumer, sourcename, raiseerror);
/* Convert the character, and when definitely invalid, bail out as well. */
char32_t c;
if (Utf8Decode(&c, p) != len) return -1;
return c;
}
SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror) {
BufState buf;
buf.buf = s;
buf.size = size;
buf.ptr = 0;
return sq_compile(v, buf_lexfeed, &buf, sourcename, raiseerror);
} }
void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx) void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx)
@ -1323,7 +1297,7 @@ void sq_free(void *p,SQUnsignedInteger size)
SQ_FREE(p,size); SQ_FREE(p,size);
} }
SQOpsLimiter::SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, const char *label) : _v(v) SQOpsLimiter::SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, std::string_view label) : _v(v)
{ {
this->_ops = v->_ops_till_suspend_error_threshold; this->_ops = v->_ops_till_suspend_error_threshold;
if (this->_ops == INT64_MIN) { if (this->_ops == INT64_MIN) {

View File

@ -5,6 +5,7 @@
#include "../../../stdafx.h" #include "../../../stdafx.h"
#include "../../fmt/format.h" #include "../../fmt/format.h"
#include "../../../core/string_consumer.hpp"
#include "sqpcheader.h" #include "sqpcheader.h"
#include "sqvm.h" #include "sqvm.h"
#include "sqstring.h" #include "sqstring.h"
@ -17,19 +18,20 @@
#include "../../../safeguards.h" #include "../../../safeguards.h"
bool str2num(const SQChar *s,SQObjectPtr &res) bool str2num(std::string_view s,SQObjectPtr &res)
{ {
SQChar *end; if(s.find('.') != std::string_view::npos){
if(strstr(s,".")){ char *end;
SQFloat r = SQFloat(strtod(s,&end)); std::string str{s};
if(s == end) return false; SQFloat r = SQFloat(strtod(str.c_str(),&end));
if(str.c_str() == end) return false;
res = r; res = r;
return true; return true;
} }
else{ else{
SQInteger r = SQInteger(strtol(s,&end,10)); auto val = ParseInteger<int64_t>(s);
if(s == end) return false; if (!val.has_value()) return false;
res = r; res = *val;
return true; return true;
} }
} }
@ -101,29 +103,29 @@ static SQInteger base_getstackinfos(HSQUIRRELVM v)
SQInteger level; SQInteger level;
SQStackInfos si; SQStackInfos si;
SQInteger seq = 0; SQInteger seq = 0;
const SQChar *name = nullptr;
sq_getinteger(v, -1, &level); sq_getinteger(v, -1, &level);
if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si))) if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si)))
{ {
const SQChar *fn = "unknown"; std::string_view fn = "unknown";
const SQChar *src = "unknown"; std::string_view src = "unknown";
if(si.funcname)fn = si.funcname; if(si.funcname)fn = si.funcname;
if(si.source)src = si.source; if(si.source)src = si.source;
sq_newtable(v); sq_newtable(v);
sq_pushstring(v, "func", -1); sq_pushstring(v, "func");
sq_pushstring(v, fn, -1); sq_pushstring(v, fn);
sq_createslot(v, -3); sq_createslot(v, -3);
sq_pushstring(v, "src", -1); sq_pushstring(v, "src");
sq_pushstring(v, src, -1); sq_pushstring(v, src);
sq_createslot(v, -3); sq_createslot(v, -3);
sq_pushstring(v, "line", -1); sq_pushstring(v, "line");
sq_pushinteger(v, si.line); sq_pushinteger(v, si.line);
sq_createslot(v, -3); sq_createslot(v, -3);
sq_pushstring(v, "locals", -1); sq_pushstring(v, "locals");
sq_newtable(v); sq_newtable(v);
seq=0; seq=0;
while ((name = sq_getlocal(v, level, seq))) { std::optional<std::string_view> name;
sq_pushstring(v, name, -1); while ((name = sq_getlocal(v, level, seq)).has_value()) {
sq_pushstring(v, *name);
sq_push(v, -2); sq_push(v, -2);
sq_createslot(v, -4); sq_createslot(v, -4);
sq_pop(v, 1); sq_pop(v, 1);
@ -169,9 +171,9 @@ static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,
static SQInteger base_print(HSQUIRRELVM v) static SQInteger base_print(HSQUIRRELVM v)
{ {
const SQChar *str; std::string_view str;
sq_tostring(v,2); sq_tostring(v,2);
sq_getstring(v,-1,&str); sq_getstring(v,-1,str);
if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,str); if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,str);
return 0; return 0;
} }
@ -180,14 +182,14 @@ static SQInteger base_print(HSQUIRRELVM v)
static SQInteger base_compilestring(HSQUIRRELVM v) static SQInteger base_compilestring(HSQUIRRELVM v)
{ {
SQInteger nargs=sq_gettop(v); SQInteger nargs=sq_gettop(v);
const SQChar *src=nullptr,*name="unnamedbuffer"; std::string_view src, name="unnamedbuffer";
SQInteger size; SQInteger size;
sq_getstring(v,2,&src); sq_getstring(v,2,src);
size=sq_getsize(v,2); size=sq_getsize(v,2);
if(nargs>2){ if(nargs>2){
sq_getstring(v,3,&name); sq_getstring(v,3,name);
} }
if(SQ_SUCCEEDED(sq_compilebuffer(v,src,size,name,SQFalse))) if(SQ_SUCCEEDED(sq_compilebuffer(v,src.substr(0, size),name,SQFalse)))
return 1; return 1;
else else
return SQ_ERROR; return SQ_ERROR;
@ -232,62 +234,59 @@ static SQInteger base_array(HSQUIRRELVM v)
static SQInteger base_type(HSQUIRRELVM v) static SQInteger base_type(HSQUIRRELVM v)
{ {
SQObjectPtr &o = stack_get(v,2); SQObjectPtr &o = stack_get(v,2);
v->Push(SQString::Create(_ss(v),GetTypeName(o),-1)); v->Push(SQString::Create(_ss(v),GetTypeName(o)));
return 1; return 1;
} }
static SQRegFunction base_funcs[]={ static const std::initializer_list<SQRegFunction> base_funcs={
//generic //generic
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS #ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
{"seterrorhandler",base_seterrorhandler,2, nullptr}, {"seterrorhandler",base_seterrorhandler,2, std::nullopt},
{"setdebughook",base_setdebughook,2, nullptr}, {"setdebughook",base_setdebughook,2, std::nullopt},
{"enabledebuginfo",base_enabledebuginfo,2, nullptr}, {"enabledebuginfo",base_enabledebuginfo,2, std::nullopt},
{"getstackinfos",base_getstackinfos,2, ".n"}, {"getstackinfos",base_getstackinfos,2, ".n"},
{"getroottable",base_getroottable,1, nullptr}, {"getroottable",base_getroottable,1, std::nullopt},
{"setroottable",base_setroottable,2, nullptr}, {"setroottable",base_setroottable,2, std::nullopt},
{"getconsttable",base_getconsttable,1, nullptr}, {"getconsttable",base_getconsttable,1, std::nullopt},
{"setconsttable",base_setconsttable,2, nullptr}, {"setconsttable",base_setconsttable,2, std::nullopt},
#endif #endif
{"assert",base_assert,2, nullptr}, {"assert",base_assert,2, std::nullopt},
{"print",base_print,2, nullptr}, {"print",base_print,2, std::nullopt},
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS #ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
{"compilestring",base_compilestring,-2, ".ss"}, {"compilestring",base_compilestring,-2, ".ss"},
{"newthread",base_newthread,2, ".c"}, {"newthread",base_newthread,2, ".c"},
{"suspend",base_suspend,-1, nullptr}, {"suspend",base_suspend,-1, std::nullopt},
#endif #endif
{"array",base_array,-2, ".n"}, {"array",base_array,-2, ".n"},
{"type",base_type,2, nullptr}, {"type",base_type,2, std::nullopt},
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS #ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
{"dummy",base_dummy,0,nullptr}, {"dummy",base_dummy,0,std::nullopt},
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
{"collectgarbage",base_collectgarbage,1, "t"}, {"collectgarbage",base_collectgarbage,1, "t"},
#endif #endif
#endif #endif
{nullptr,nullptr,0,nullptr}
}; };
void sq_base_register(HSQUIRRELVM v) void sq_base_register(HSQUIRRELVM v)
{ {
SQInteger i=0;
sq_pushroottable(v); sq_pushroottable(v);
while(base_funcs[i].name!=nullptr) { for(auto &func : base_funcs) {
sq_pushstring(v,base_funcs[i].name,-1); sq_pushstring(v,func.name);
sq_newclosure(v,base_funcs[i].f,0); sq_newclosure(v,func.f,0);
sq_setnativeclosurename(v,-1,base_funcs[i].name); sq_setnativeclosurename(v,-1,func.name);
sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask); sq_setparamscheck(v,func.nparamscheck,func.typemask);
sq_createslot(v,-3); sq_createslot(v,-3);
i++;
} }
sq_pushstring(v,"_version_",-1); sq_pushstring(v,"_version_");
sq_pushstring(v,SQUIRREL_VERSION,-1); sq_pushstring(v,SQUIRREL_VERSION);
sq_createslot(v,-3); sq_createslot(v,-3);
sq_pushstring(v,"_charsize_",-1); sq_pushstring(v,"_charsize_");
sq_pushinteger(v,sizeof(SQChar)); sq_pushinteger(v,sizeof(char));
sq_createslot(v,-3); sq_createslot(v,-3);
sq_pushstring(v,"_intsize_",-1); sq_pushstring(v,"_intsize_");
sq_pushinteger(v,sizeof(SQInteger)); sq_pushinteger(v,sizeof(SQInteger));
sq_createslot(v,-3); sq_createslot(v,-3);
sq_pushstring(v,"_floatsize_",-1); sq_pushstring(v,"_floatsize_");
sq_pushinteger(v,sizeof(SQFloat)); sq_pushinteger(v,sizeof(SQFloat));
sq_createslot(v,-3); sq_createslot(v,-3);
sq_pop(v,1); sq_pop(v,1);
@ -370,8 +369,8 @@ static SQInteger obj_clear(HSQUIRRELVM v)
static SQInteger number_delegate_tochar(HSQUIRRELVM v) static SQInteger number_delegate_tochar(HSQUIRRELVM v)
{ {
SQObject &o=stack_get(v,1); SQObject &o=stack_get(v,1);
SQChar c = (SQChar)tointeger(o); char c = static_cast<char>(tointeger(o));
v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1)); v->Push(SQString::Create(_ss(v),std::string_view(&c, 1)));
return 1; return 1;
} }
@ -409,16 +408,15 @@ static SQInteger table_rawget(HSQUIRRELVM v)
} }
SQRegFunction SQSharedState::_table_default_delegate_funcz[]={ /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_table_default_delegate_funcz={
{"len",default_delegate_len,1, "t"}, {"len",default_delegate_len,1, "t"},
{"rawget",table_rawget,2, "t"}, {"rawget",table_rawget,2, "t"},
{"rawset",table_rawset,3, "t"}, {"rawset",table_rawset,3, "t"},
{"rawdelete",table_rawdelete,2, "t"}, {"rawdelete",table_rawdelete,2, "t"},
{"rawin",container_rawexists,2, "t"}, {"rawin",container_rawexists,2, "t"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{"clear",obj_clear,1, "."}, {"clear",obj_clear,1, "."},
{nullptr,nullptr,0,nullptr}
}; };
//ARRAY DEFAULT DELEGATE/////////////////////////////////////// //ARRAY DEFAULT DELEGATE///////////////////////////////////////
@ -611,7 +609,7 @@ static SQInteger array_slice(HSQUIRRELVM v)
} }
SQRegFunction SQSharedState::_array_default_delegate_funcz[]={ /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_array_default_delegate_funcz={
{"len",default_delegate_len,1, "a"}, {"len",default_delegate_len,1, "a"},
{"append",array_append,2, "a"}, {"append",array_append,2, "a"},
{"extend",array_extend,2, "aa"}, {"extend",array_extend,2, "aa"},
@ -624,10 +622,9 @@ SQRegFunction SQSharedState::_array_default_delegate_funcz[]={
{"reverse",array_reverse,1, "a"}, {"reverse",array_reverse,1, "a"},
{"sort",array_sort,-1, "ac"}, {"sort",array_sort,-1, "ac"},
{"slice",array_slice,-1, "ann"}, {"slice",array_slice,-1, "ann"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{"clear",obj_clear,1, "."}, {"clear",obj_clear,1, "."},
{nullptr,nullptr,0,nullptr}
}; };
//STRING DEFAULT DELEGATE////////////////////////// //STRING DEFAULT DELEGATE//////////////////////////
@ -636,25 +633,25 @@ static SQInteger string_slice(HSQUIRRELVM v)
SQInteger sidx,eidx; SQInteger sidx,eidx;
SQObjectPtr o; SQObjectPtr o;
if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1; if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1;
SQInteger slen = _string(o)->_len; SQInteger slen = _string(o)->View().size();
if(sidx < 0)sidx = slen + sidx; if(sidx < 0)sidx = slen + sidx;
if(eidx < 0)eidx = slen + eidx; if(eidx < 0)eidx = slen + eidx;
if(eidx < sidx) return sq_throwerror(v,"wrong indexes"); if(eidx < sidx) return sq_throwerror(v,"wrong indexes");
if(eidx > slen) return sq_throwerror(v,"slice out of range"); if(eidx > slen) return sq_throwerror(v,"slice out of range");
v->Push(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx)); v->Push(SQString::Create(_ss(v),_stringval(o).substr(sidx,eidx-sidx)));
return 1; return 1;
} }
static SQInteger string_find(HSQUIRRELVM v) static SQInteger string_find(HSQUIRRELVM v)
{ {
SQInteger top,start_idx=0; SQInteger top,start_idx=0;
const SQChar *str,*substr,*ret; std::string_view str,substr;
if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){ if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,str)) && SQ_SUCCEEDED(sq_getstring(v,2,substr))){
if(top>2)sq_getinteger(v,3,&start_idx); if(top>2)sq_getinteger(v,3,&start_idx);
if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){ if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){
ret=strstr(&str[start_idx],substr); auto ret = str.find(substr, start_idx);
if(ret){ if(ret != std::string_view::npos){
sq_pushinteger(v,(SQInteger)(ret-str)); sq_pushinteger(v,static_cast<SQInteger>(ret));
return 1; return 1;
} }
} }
@ -666,11 +663,11 @@ static SQInteger string_find(HSQUIRRELVM v)
#define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \ #define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \
{ \ { \
SQObject str=stack_get(v,1); \ SQObject str=stack_get(v,1); \
SQInteger len=_string(str)->_len; \ std::string_view sThis=_stringval(str); \
const SQChar *sThis=_stringval(str); \ size_t len=sThis.size(); \
SQChar *sNew=(_ss(v)->GetScratchPad(len)); \ std::span<char> sNew=(_ss(v)->GetScratchPad(len)); \
for(SQInteger i=0;i<len;i++) sNew[i]=func(sThis[i]); \ for(size_t i=0;i<len;i++) sNew[i]=func(sThis[i]); \
v->Push(SQString::Create(_ss(v),sNew,len)); \ v->Push(SQString::Create(_ss(v),std::string_view(sNew.data(), len))); \
return 1; \ return 1; \
} }
@ -678,7 +675,7 @@ static SQInteger string_find(HSQUIRRELVM v)
STRING_TOFUNCZ(tolower) STRING_TOFUNCZ(tolower)
STRING_TOFUNCZ(toupper) STRING_TOFUNCZ(toupper)
SQRegFunction SQSharedState::_string_default_delegate_funcz[]={ /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_string_default_delegate_funcz={
{"len",default_delegate_len,1, "s"}, {"len",default_delegate_len,1, "s"},
{"tointeger",default_delegate_tointeger,1, "s"}, {"tointeger",default_delegate_tointeger,1, "s"},
{"tofloat",default_delegate_tofloat,1, "s"}, {"tofloat",default_delegate_tofloat,1, "s"},
@ -687,18 +684,16 @@ SQRegFunction SQSharedState::_string_default_delegate_funcz[]={
{"find",string_find,-2, "s s n "}, {"find",string_find,-2, "s s n "},
{"tolower",string_tolower,1, "s"}, {"tolower",string_tolower,1, "s"},
{"toupper",string_toupper,1, "s"}, {"toupper",string_toupper,1, "s"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{nullptr,nullptr,0,nullptr}
}; };
//INTEGER DEFAULT DELEGATE////////////////////////// //INTEGER DEFAULT DELEGATE//////////////////////////
SQRegFunction SQSharedState::_number_default_delegate_funcz[]={ /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_number_default_delegate_funcz={
{"tointeger",default_delegate_tointeger,1, "n|b"}, {"tointeger",default_delegate_tointeger,1, "n|b"},
{"tofloat",default_delegate_tofloat,1, "n|b"}, {"tofloat",default_delegate_tofloat,1, "n|b"},
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{"tochar",number_delegate_tochar,1, "n|b"}, {"tochar",number_delegate_tochar,1, "n|b"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{nullptr,nullptr,0,nullptr}
}; };
//CLOSURE DEFAULT DELEGATE////////////////////////// //CLOSURE DEFAULT DELEGATE//////////////////////////
@ -749,19 +744,19 @@ static SQInteger closure_getinfos(HSQUIRRELVM v) {
_array(params)->Set((SQInteger)n,f->_parameters[n]); _array(params)->Set((SQInteger)n,f->_parameters[n]);
} }
if(f->_varparams) { if(f->_varparams) {
_array(params)->Set(nparams-1,SQString::Create(_ss(v),"...",-1)); _array(params)->Set(nparams-1,SQString::Create(_ss(v),"..."));
} }
res->NewSlot(SQString::Create(_ss(v),"native",-1),false); res->NewSlot(SQString::Create(_ss(v),"native"),false);
res->NewSlot(SQString::Create(_ss(v),"name",-1),f->_name); res->NewSlot(SQString::Create(_ss(v),"name"),f->_name);
res->NewSlot(SQString::Create(_ss(v),"src",-1),f->_sourcename); res->NewSlot(SQString::Create(_ss(v),"src"),f->_sourcename);
res->NewSlot(SQString::Create(_ss(v),"parameters",-1),params); res->NewSlot(SQString::Create(_ss(v),"parameters"),params);
res->NewSlot(SQString::Create(_ss(v),"varargs",-1),f->_varparams); res->NewSlot(SQString::Create(_ss(v),"varargs"),f->_varparams);
} }
else { //OT_NATIVECLOSURE else { //OT_NATIVECLOSURE
SQNativeClosure *nc = _nativeclosure(o); SQNativeClosure *nc = _nativeclosure(o);
res->NewSlot(SQString::Create(_ss(v),"native",-1),true); res->NewSlot(SQString::Create(_ss(v),"native"),true);
res->NewSlot(SQString::Create(_ss(v),"name",-1),nc->_name); res->NewSlot(SQString::Create(_ss(v),"name"),nc->_name);
res->NewSlot(SQString::Create(_ss(v),"paramscheck",-1),nc->_nparamscheck); res->NewSlot(SQString::Create(_ss(v),"paramscheck"),nc->_nparamscheck);
SQObjectPtr typecheck; SQObjectPtr typecheck;
if(!nc->_typecheck.empty()) { if(!nc->_typecheck.empty()) {
typecheck = typecheck =
@ -770,23 +765,22 @@ static SQInteger closure_getinfos(HSQUIRRELVM v) {
_array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]); _array(typecheck)->Set((SQInteger)n,nc->_typecheck[n]);
} }
} }
res->NewSlot(SQString::Create(_ss(v),"typecheck",-1),typecheck); res->NewSlot(SQString::Create(_ss(v),"typecheck"),typecheck);
} }
v->Push(res); v->Push(res);
return 1; return 1;
} }
SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={ /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_closure_default_delegate_funcz={
{"call",closure_call,-1, "c"}, {"call",closure_call,-1, "c"},
{"pcall",closure_pcall,-1, "c"}, {"pcall",closure_pcall,-1, "c"},
{"acall",closure_acall,2, "ca"}, {"acall",closure_acall,2, "ca"},
{"pacall",closure_pacall,2, "ca"}, {"pacall",closure_pacall,2, "ca"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{"bindenv",closure_bindenv,2, "c x|y|t"}, {"bindenv",closure_bindenv,2, "c x|y|t"},
{"getinfos",closure_getinfos,1, "c"}, {"getinfos",closure_getinfos,1, "c"},
{nullptr,nullptr,0,nullptr}
}; };
//GENERATOR DEFAULT DELEGATE //GENERATOR DEFAULT DELEGATE
@ -801,11 +795,10 @@ static SQInteger generator_getstatus(HSQUIRRELVM v)
return 1; return 1;
} }
SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={ /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_generator_default_delegate_funcz={
{"getstatus",generator_getstatus,1, "g"}, {"getstatus",generator_getstatus,1, "g"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{nullptr,nullptr,0,nullptr}
}; };
//THREAD DEFAULT DELEGATE //THREAD DEFAULT DELEGATE
@ -871,13 +864,13 @@ static SQInteger thread_getstatus(HSQUIRRELVM v)
SQObjectPtr &o = stack_get(v,1); SQObjectPtr &o = stack_get(v,1);
switch(sq_getvmstate(_thread(o))) { switch(sq_getvmstate(_thread(o))) {
case SQ_VMSTATE_IDLE: case SQ_VMSTATE_IDLE:
sq_pushstring(v,"idle",-1); sq_pushstring(v,"idle");
break; break;
case SQ_VMSTATE_RUNNING: case SQ_VMSTATE_RUNNING:
sq_pushstring(v,"running",-1); sq_pushstring(v,"running");
break; break;
case SQ_VMSTATE_SUSPENDED: case SQ_VMSTATE_SUSPENDED:
sq_pushstring(v,"suspended",-1); sq_pushstring(v,"suspended");
break; break;
default: default:
return sq_throwerror(v,"internal VM error"); return sq_throwerror(v,"internal VM error");
@ -885,13 +878,12 @@ static SQInteger thread_getstatus(HSQUIRRELVM v)
return 1; return 1;
} }
SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = { /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_thread_default_delegate_funcz = {
{"call", thread_call, -1, "v"}, {"call", thread_call, -1, "v"},
{"wakeup", thread_wakeup, -1, "v"}, {"wakeup", thread_wakeup, -1, "v"},
{"getstatus", thread_getstatus, 1, "v"}, {"getstatus", thread_getstatus, 1, "v"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{nullptr,nullptr,0,nullptr},
}; };
static SQInteger class_getattributes(HSQUIRRELVM v) static SQInteger class_getattributes(HSQUIRRELVM v)
@ -915,14 +907,13 @@ static SQInteger class_instance(HSQUIRRELVM v)
return SQ_ERROR; return SQ_ERROR;
} }
SQRegFunction SQSharedState::_class_default_delegate_funcz[] = { /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_class_default_delegate_funcz = {
{"getattributes", class_getattributes, 2, "y."}, {"getattributes", class_getattributes, 2, "y."},
{"setattributes", class_setattributes, 3, "y.."}, {"setattributes", class_setattributes, 3, "y.."},
{"rawin",container_rawexists,2, "y"}, {"rawin",container_rawexists,2, "y"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{"instance",class_instance,1, "y"}, {"instance",class_instance,1, "y"},
{nullptr,nullptr,0,nullptr}
}; };
static SQInteger instance_getclass(HSQUIRRELVM v) static SQInteger instance_getclass(HSQUIRRELVM v)
@ -932,12 +923,11 @@ static SQInteger instance_getclass(HSQUIRRELVM v)
return SQ_ERROR; return SQ_ERROR;
} }
SQRegFunction SQSharedState::_instance_default_delegate_funcz[] = { /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_instance_default_delegate_funcz = {
{"getclass", instance_getclass, 1, "x"}, {"getclass", instance_getclass, 1, "x"},
{"rawin",container_rawexists,2, "x"}, {"rawin",container_rawexists,2, "x"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{nullptr,nullptr,0,nullptr}
}; };
static SQInteger weakref_ref(HSQUIRRELVM v) static SQInteger weakref_ref(HSQUIRRELVM v)
@ -947,11 +937,10 @@ static SQInteger weakref_ref(HSQUIRRELVM v)
return 1; return 1;
} }
SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = { /* static */ const std::initializer_list<SQRegFunction> SQSharedState::_weakref_default_delegate_funcz = {
{"ref",weakref_ref,1, "r"}, {"ref",weakref_ref,1, "r"},
{"weakref",obj_delegate_weakref,1, nullptr }, {"weakref",obj_delegate_weakref,1, std::nullopt },
{"tostring",default_delegate_tostring,1, "."}, {"tostring",default_delegate_tostring,1, "."},
{nullptr,nullptr,0,nullptr}
}; };

View File

@ -57,16 +57,12 @@ typedef sqvector<ExpState> ExpStateVec;
class SQCompiler class SQCompiler
{ {
public: public:
SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo) : _token(0), _fs(nullptr), _lex(_ss(v), rg, up), _debugline(0), _debugop(0) SQCompiler(SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, std::string_view sourcename, bool raiseerror, bool lineinfo) : _token(0), _fs(nullptr), _lex(_ss(v), rg, up), _debugline(0), _debugop(0)
{ {
_vm=v; _vm=v;
_sourcename = SQString::Create(_ss(v), sourcename); _sourcename = SQString::Create(_ss(v), sourcename);
_lineinfo = lineinfo;_raiseerror = raiseerror; _lineinfo = lineinfo;_raiseerror = raiseerror;
} }
[[noreturn]] void Error(const std::string &msg)
{
throw CompileException(msg);
}
void Lex(){ _token = _lex.Lex();} void Lex(){ _token = _lex.Lex();}
void PushExpState(){ _expstates.push_back(ExpState()); } void PushExpState(){ _expstates.push_back(ExpState()); }
bool IsDerefToken(SQInteger tok) bool IsDerefToken(SQInteger tok)
@ -92,7 +88,7 @@ public:
//do nothing //do nothing
} }
else { else {
const SQChar *etypename; std::string_view etypename;
if(tok > 255) { if(tok > 255) {
switch(tok) switch(tok)
{ {
@ -109,21 +105,21 @@ public:
etypename = "FLOAT"; etypename = "FLOAT";
break; break;
default: default:
etypename = _lex.Tok2Str(tok); etypename = _lex.Tok2Str(tok).value_or("<unknown>");
} }
Error(fmt::format("expected '{}'", etypename)); throw CompileException(fmt::format("expected '{}'", etypename));
} }
Error(fmt::format("expected '{:c}'", tok)); throw CompileException(fmt::format("expected '{:c}'", tok));
} }
} }
SQObjectPtr ret; SQObjectPtr ret;
switch(tok) switch(tok)
{ {
case TK_IDENTIFIER: case TK_IDENTIFIER:
ret = _fs->CreateString(_lex._svalue); ret = _fs->CreateString(_lex.View());
break; break;
case TK_STRING_LITERAL: case TK_STRING_LITERAL:
ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); ret = _fs->CreateString(_lex.View());
break; break;
case TK_INTEGER: case TK_INTEGER:
ret = SQObjectPtr(_lex._nvalue); ret = SQObjectPtr(_lex._nvalue);
@ -140,7 +136,7 @@ public:
{ {
if(_token == ';') { Lex(); return; } if(_token == ';') { Lex(); return; }
if(!IsEndOfStatement()) { if(!IsEndOfStatement()) {
Error("end of statement expected (; or lf)"); throw CompileException("end of statement expected (; or lf)");
} }
} }
void MoveIfCurrentTargetIsLocal() { void MoveIfCurrentTargetIsLocal() {
@ -233,7 +229,7 @@ public:
} }
break;} break;}
case TK_BREAK: case TK_BREAK:
if(_fs->_breaktargets.size() <= 0)Error("'break' has to be in a loop block"); if(_fs->_breaktargets.size() <= 0)throw CompileException("'break' has to be in a loop block");
if(_fs->_breaktargets.top() > 0){ if(_fs->_breaktargets.top() > 0){
_fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0);
} }
@ -243,7 +239,7 @@ public:
Lex(); Lex();
break; break;
case TK_CONTINUE: case TK_CONTINUE:
if(_fs->_continuetargets.size() <= 0)Error("'continue' has to be in a loop block"); if(_fs->_continuetargets.size() <= 0)throw CompileException("'continue' has to be in a loop block");
if(_fs->_continuetargets.top() > 0) { if(_fs->_continuetargets.top() > 0) {
_fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0); _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0);
} }
@ -356,19 +352,19 @@ public:
SQInteger op = _token; SQInteger op = _token;
SQInteger ds = _exst._deref; SQInteger ds = _exst._deref;
bool freevar = _exst._freevar; bool freevar = _exst._freevar;
if(ds == DEREF_NO_DEREF) Error("can't assign expression"); if(ds == DEREF_NO_DEREF) throw CompileException("can't assign expression");
Lex(); Expression(); Lex(); Expression();
switch(op){ switch(op){
case TK_NEWSLOT: case TK_NEWSLOT:
if(freevar) Error("free variables cannot be modified"); if(freevar) throw CompileException("free variables cannot be modified");
if(ds == DEREF_FIELD) if(ds == DEREF_FIELD)
EmitDerefOp(_OP_NEWSLOT); EmitDerefOp(_OP_NEWSLOT);
else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local else //if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
Error("can't 'create' a local slot"); throw CompileException("can't 'create' a local slot");
break; break;
case '=': //ASSIGN case '=': //ASSIGN
if(freevar) Error("free variables cannot be modified"); if(freevar) throw CompileException("free variables cannot be modified");
if(ds == DEREF_FIELD) if(ds == DEREF_FIELD)
EmitDerefOp(_OP_SET); EmitDerefOp(_OP_SET);
else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
@ -533,7 +529,7 @@ public:
if(_token == TK_PARENT) { if(_token == TK_PARENT) {
Lex(); Lex();
if(!NeedGet()) if(!NeedGet())
Error("parent cannot be set"); throw CompileException("parent cannot be set");
SQInteger src = _fs->PopTarget(); SQInteger src = _fs->PopTarget();
_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src); _fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src);
} }
@ -546,7 +542,7 @@ public:
} }
break; break;
case '[': case '[':
if(_lex._prevtoken == '\n') Error("cannot brake deref/or comma needed after [exp]=exp slot declaration"); if(_lex._prevtoken == '\n') throw CompileException("cannot brake deref/or comma needed after [exp]=exp slot declaration");
Lex(); Expression(); Expect(']'); Lex(); Expression(); Expect(']');
pos = -1; pos = -1;
if(NeedGet()) Emit2ArgsOP(_OP_GET); if(NeedGet()) Emit2ArgsOP(_OP_GET);
@ -598,7 +594,7 @@ public:
switch(_token) switch(_token)
{ {
case TK_STRING_LITERAL: { case TK_STRING_LITERAL: {
_fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex._svalue,_lex._longstr.size()-1))); _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(_lex.View())));
Lex(); Lex();
} }
break; break;
@ -618,7 +614,7 @@ public:
SQObject id; SQObject id;
SQObject constant; SQObject constant;
switch(_token) { switch(_token) {
case TK_IDENTIFIER: id = _fs->CreateString(_lex._svalue); break; case TK_IDENTIFIER: id = _fs->CreateString(_lex.View()); break;
case TK_THIS: id = _fs->CreateString("this"); break; case TK_THIS: id = _fs->CreateString("this"); break;
case TK_CONSTRUCTOR: id = _fs->CreateString("constructor"); break; case TK_CONSTRUCTOR: id = _fs->CreateString("constructor"); break;
} }
@ -638,7 +634,7 @@ public:
Expect('.'); constid = Expect(TK_IDENTIFIER); Expect('.'); constid = Expect(TK_IDENTIFIER);
if(!_table(constant)->Get(constid,constval)) { if(!_table(constant)->Get(constid,constval)) {
constval.Null(); constval.Null();
Error(fmt::format("invalid constant [{}.{}]", _stringval(id),_stringval(constid))); throw CompileException(fmt::format("invalid constant [{}.{}]", _stringval(id),_stringval(constid)));
} }
} }
else { else {
@ -742,7 +738,7 @@ public:
case TK_DELEGATE : DelegateExpr(); break; case TK_DELEGATE : DelegateExpr(); break;
case '(': Lex(); CommaExpr(); Expect(')'); case '(': Lex(); CommaExpr(); Expect(')');
break; break;
default: Error("expression expected"); default: throw CompileException("expression expected");
} }
return -1; return -1;
} }
@ -771,7 +767,7 @@ public:
nargs++; nargs++;
if(_token == ','){ if(_token == ','){
Lex(); Lex();
if(_token == ')') Error("expression expected, found ')'"); if(_token == ')') throw CompileException("expression expected, found ')'");
} }
} }
Lex(); Lex();
@ -1082,13 +1078,13 @@ public:
_exst._funcarg = false; _exst._funcarg = false;
PrefixedExpr(); PrefixedExpr();
es = PopExpState(); es = PopExpState();
if(es._deref == DEREF_NO_DEREF) Error("invalid class name"); if(es._deref == DEREF_NO_DEREF) throw CompileException("invalid class name");
if(es._deref == DEREF_FIELD) { if(es._deref == DEREF_FIELD) {
ClassExp(); ClassExp();
EmitDerefOp(_OP_NEWSLOT); EmitDerefOp(_OP_NEWSLOT);
_fs->PopTarget(); _fs->PopTarget();
} }
else Error("cannot create a class in a local with the syntax(class <local>)"); else throw CompileException("cannot create a class in a local with the syntax(class <local>)");
} }
SQObject ExpectScalar() SQObject ExpectScalar()
{ {
@ -1103,7 +1099,7 @@ public:
val._unVal.fFloat = _lex._fvalue; val._unVal.fFloat = _lex._fvalue;
break; break;
case TK_STRING_LITERAL: case TK_STRING_LITERAL:
val = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1); val = _fs->CreateString(_lex.View());
break; break;
case '-': case '-':
Lex(); Lex();
@ -1118,12 +1114,12 @@ public:
val._unVal.fFloat = -_lex._fvalue; val._unVal.fFloat = -_lex._fvalue;
break; break;
default: default:
Error("scalar expected : integer,float"); throw CompileException("scalar expected : integer,float");
val._type = OT_NULL; // Silent compile-warning val._type = OT_NULL; // Silent compile-warning
} }
break; break;
default: default:
Error("scalar expected : integer,float or string"); throw CompileException("scalar expected : integer,float or string");
val._type = OT_NULL; // Silent compile-warning val._type = OT_NULL; // Silent compile-warning
} }
Lex(); Lex();
@ -1226,9 +1222,9 @@ public:
_exst._funcarg = false; _exst._funcarg = false;
PrefixedExpr(); PrefixedExpr();
es = PopExpState(); es = PopExpState();
if(es._deref == DEREF_NO_DEREF) Error("can't delete an expression"); if(es._deref == DEREF_NO_DEREF) throw CompileException("can't delete an expression");
if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE); if(es._deref == DEREF_FIELD) Emit2ArgsOP(_OP_DELETE);
else Error("cannot delete a local"); else throw CompileException("cannot delete a local");
} }
void PrefixIncDec(SQInteger token) void PrefixIncDec(SQInteger token)
{ {
@ -1255,10 +1251,10 @@ public:
SQInteger defparams = 0; SQInteger defparams = 0;
while(_token!=')') { while(_token!=')') {
if(_token == TK_VARPARAMS) { if(_token == TK_VARPARAMS) {
if(defparams > 0) Error("function with default parameters cannot have variable number of parameters"); if(defparams > 0) throw CompileException("function with default parameters cannot have variable number of parameters");
funcstate->_varparams = true; funcstate->_varparams = true;
Lex(); Lex();
if(_token != ')') Error("expected ')'"); if(_token != ')') throw CompileException("expected ')'");
break; break;
} }
else { else {
@ -1271,10 +1267,10 @@ public:
defparams++; defparams++;
} }
else { else {
if(defparams > 0) Error("expected '='"); if(defparams > 0) throw CompileException("expected '='");
} }
if(_token == ',') Lex(); if(_token == ',') Lex();
else if(_token != ')') Error("expected ')' or ','"); else if(_token != ')') throw CompileException("expected ')' or ','");
} }
} }
Expect(')'); Expect(')');
@ -1289,7 +1285,7 @@ public:
//outers are treated as implicit local variables //outers are treated as implicit local variables
funcstate->AddOuterValue(paramname); funcstate->AddOuterValue(paramname);
if(_token == ',') Lex(); if(_token == ',') Lex();
else if(_token != ')') Error("expected ')' or ','"); else if(_token != ')') throw CompileException("expected ')' or ','");
} }
Lex(); Lex();
} }
@ -1346,7 +1342,7 @@ private:
SQVM *_vm; SQVM *_vm;
}; };
bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo) bool Compile(SQVM *vm,SQLEXREADFUNC rg, SQUserPointer up, std::string_view sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo)
{ {
SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo); SQCompiler p(vm, rg, up, sourcename, raiseerror, lineinfo);
return p.Compile(out); return p.Compile(out);

View File

@ -73,5 +73,5 @@ struct SQVM;
using CompileException = std::runtime_error; using CompileException = std::runtime_error;
bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo); bool Compile(SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, std::string_view sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo);
#endif //_SQCOMPILER_H_ #endif //_SQCOMPILER_H_

View File

@ -37,7 +37,7 @@ SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si)
{ {
SQInteger cssize = v->_callsstacksize; SQInteger cssize = v->_callsstacksize;
if (cssize > level) { if (cssize > level) {
memset(si, 0, sizeof(SQStackInfos)); *si = {};
SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; SQVM::CallInfo &ci = v->_callsstack[cssize-level-1];
switch (type(ci._closure)) { switch (type(ci._closure)) {
case OT_CLOSURE:{ case OT_CLOSURE:{
@ -101,15 +101,15 @@ void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2)
void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type) void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type)
{ {
SQObjectPtr exptypes = SQString::Create(_ss(this), "", -1); SQObjectPtr exptypes = SQString::Create(_ss(this), "");
SQInteger found = 0; SQInteger found = 0;
for(SQInteger i=0; i<16; i++) for(SQInteger i=0; i<16; i++)
{ {
SQInteger mask = 0x00000001LL << i; SQInteger mask = 0x00000001LL << i;
if(typemask & (mask)) { if(typemask & (mask)) {
if(found>0) StringCat(exptypes,SQString::Create(_ss(this), "|", -1), exptypes); if(found>0) StringCat(exptypes,SQString::Create(_ss(this), "|"), exptypes);
found ++; found ++;
StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1), exptypes); StringCat(exptypes,SQString::Create(_ss(this), IdType2Name((SQObjectType)mask)), exptypes);
} }
} }
Raise_Error(fmt::format("parameter {} has an invalid type '{}' ; expected: '{}'", nparam, IdType2Name((SQObjectType)type), _stringval(exptypes))); Raise_Error(fmt::format("parameter {} has an invalid type '{}' ; expected: '{}'", nparam, IdType2Name((SQObjectType)type), _stringval(exptypes)));

View File

@ -114,7 +114,7 @@ public:
this->~SQFunctionProto(); this->~SQFunctionProto();
sq_vm_free(this,size); sq_vm_free(this,size);
} }
const SQChar* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop); std::optional<std::string_view> GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop);
SQInteger GetLine(SQInstruction *curr); SQInteger GetLine(SQInstruction *curr);
bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);

View File

@ -110,11 +110,6 @@ SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent)
} }
void SQFuncState::Error(const SQChar *err)
{
throw CompileException(err);
}
#ifdef _DEBUG_DUMP #ifdef _DEBUG_DUMP
void SQFuncState::Dump(SQFunctionProto *func) void SQFuncState::Dump(SQFunctionProto *func)
{ {
@ -234,7 +229,7 @@ SQInteger SQFuncState::GetConstant(const SQObject &cons)
_nliterals++; _nliterals++;
if(_nliterals > MAX_LITERALS) { if(_nliterals > MAX_LITERALS) {
val.Null(); val.Null();
Error("internal compiler error: too many literals"); throw CompileException("internal compiler error: too many literals");
} }
} }
return _integer(val); return _integer(val);
@ -264,7 +259,7 @@ SQInteger SQFuncState::AllocStackPos()
SQInteger npos=_vlocals.size(); SQInteger npos=_vlocals.size();
_vlocals.push_back(SQLocalVarInfo()); _vlocals.push_back(SQLocalVarInfo());
if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) { if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) {
if(_stacksize>MAX_FUNC_STACKSIZE) Error("internal compiler error: too many locals"); if(_stacksize>MAX_FUNC_STACKSIZE) throw CompileException("internal compiler error: too many locals");
_stacksize=_vlocals.size(); _stacksize=_vlocals.size();
} }
return npos; return npos;
@ -499,9 +494,9 @@ void SQFuncState::AddInstruction(SQInstruction &i)
_instructions.push_back(i); _instructions.push_back(i);
} }
SQObject SQFuncState::CreateString(const SQChar *s,SQInteger len) SQObject SQFuncState::CreateString(std::string_view s)
{ {
SQObjectPtr ns(SQString::Create(_sharedstate,s,len)); SQObjectPtr ns(SQString::Create(_sharedstate,s));
_table(_strings)->NewSlot(ns,(SQInteger)1); _table(_strings)->NewSlot(ns,(SQInteger)1);
return std::move(ns); return std::move(ns);
} }
@ -539,7 +534,7 @@ SQFunctionProto *SQFuncState::BuildProto()
for(SQUnsignedInteger no = 0; no < _lineinfos.size(); no++) f->_lineinfos[no] = _lineinfos[no]; for(SQUnsignedInteger no = 0; no < _lineinfos.size(); no++) f->_lineinfos[no] = _lineinfos[no];
for(SQUnsignedInteger no = 0; no < _defaultparams.size(); no++) f->_defaultparams[no] = _defaultparams[no]; for(SQUnsignedInteger no = 0; no < _defaultparams.size(); no++) f->_defaultparams[no] = _defaultparams[no];
memcpy(f->_instructions,&_instructions[0],(size_t)_instructions.size()*sizeof(SQInstruction)); std::copy_n(&_instructions[0], _instructions.size(), f->_instructions);
f->_varparams = _varparams; f->_varparams = _varparams;

View File

@ -11,7 +11,6 @@ struct SQFuncState
#ifdef _DEBUG_DUMP #ifdef _DEBUG_DUMP
void Dump(SQFunctionProto *func); void Dump(SQFunctionProto *func);
#endif #endif
[[noreturn]] void Error(const SQChar *err);
SQFuncState *PushChildState(SQSharedState *ss); SQFuncState *PushChildState(SQSharedState *ss);
void PopChildState(); void PopChildState();
void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);} void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);}
@ -43,7 +42,7 @@ struct SQFuncState
SQInteger TopTarget(); SQInteger TopTarget();
SQInteger GetUpTarget(SQInteger n); SQInteger GetUpTarget(SQInteger n);
bool IsLocal(SQUnsignedInteger stkpos); bool IsLocal(SQUnsignedInteger stkpos);
SQObject CreateString(const SQChar *s,SQInteger len = -1); SQObject CreateString(std::string_view s);
SQObject CreateTable(); SQObject CreateTable();
bool IsConstant(const SQObject &name,SQObject &e); bool IsConstant(const SQObject &name,SQObject &e);
SQInteger _returnexp; SQInteger _returnexp;

View File

@ -12,6 +12,7 @@
#include "sqlexer.h" #include "sqlexer.h"
#include "../../../core/utf8.hpp" #include "../../../core/utf8.hpp"
#include "../../../core/string_consumer.hpp"
#include "../../../safeguards.h" #include "../../../safeguards.h"
@ -84,22 +85,16 @@ SQLexer::SQLexer(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up)
_prevtoken = -1; _prevtoken = -1;
_curtoken = -1; _curtoken = -1;
_svalue = nullptr;
_nvalue = 0; _nvalue = 0;
_fvalue = 0; _fvalue = 0;
Next(); Next();
} }
[[noreturn]] void SQLexer::Error(const SQChar *err)
{
throw CompileException(err);
}
void SQLexer::Next() void SQLexer::Next()
{ {
char32_t t = _readf(_up); char32_t t = _readf(_up);
if(t > MAX_CHAR) Error("Invalid character"); if(t > MAX_CHAR) throw CompileException("Invalid character");
if(t != 0) { if(t != 0) {
_currdata = t; _currdata = t;
return; return;
@ -107,7 +102,7 @@ void SQLexer::Next()
_currdata = SQUIRREL_EOB; _currdata = SQUIRREL_EOB;
} }
const SQChar *SQLexer::Tok2Str(SQInteger tok) std::optional<std::string_view> SQLexer::Tok2Str(SQInteger tok)
{ {
SQObjectPtr itr, key, val; SQObjectPtr itr, key, val;
SQInteger nitr; SQInteger nitr;
@ -116,7 +111,7 @@ const SQChar *SQLexer::Tok2Str(SQInteger tok)
if(((SQInteger)_integer(val)) == tok) if(((SQInteger)_integer(val)) == tok)
return _stringval(key); return _stringval(key);
} }
return nullptr; return std::nullopt;
} }
void SQLexer::LexBlockComment() void SQLexer::LexBlockComment()
@ -126,7 +121,7 @@ void SQLexer::LexBlockComment()
switch(CUR_CHAR) { switch(CUR_CHAR) {
case '*': { NEXT(); if(CUR_CHAR == '/') { done = true; NEXT(); }}; continue; case '*': { NEXT(); if(CUR_CHAR == '/') { done = true; NEXT(); }}; continue;
case '\n': _currentline++; NEXT(); continue; case '\n': _currentline++; NEXT(); continue;
case SQUIRREL_EOB: Error("missing \"*/\" in comment"); case SQUIRREL_EOB: throw CompileException("missing \"*/\" in comment");
default: NEXT(); default: NEXT();
} }
} }
@ -195,11 +190,11 @@ SQInteger SQLexer::Lex()
SQInteger stype; SQInteger stype;
NEXT(); NEXT();
if(CUR_CHAR != '"') if(CUR_CHAR != '"')
Error("string expected"); throw CompileException("string expected");
if((stype=ReadString('"',true))!=-1) { if((stype=ReadString('"',true))!=-1) {
RETURN_TOKEN(stype); RETURN_TOKEN(stype);
} }
Error("error parsing the string"); throw CompileException("error parsing the string");
} }
case '"': case '"':
case '\'': { case '\'': {
@ -207,7 +202,7 @@ SQInteger SQLexer::Lex()
if((stype=ReadString(CUR_CHAR,false))!=-1){ if((stype=ReadString(CUR_CHAR,false))!=-1){
RETURN_TOKEN(stype); RETURN_TOKEN(stype);
} }
Error("error parsing the string"); throw CompileException("error parsing the string");
} }
case '{': case '}': case '(': case ')': case '[': case ']': case '{': case '}': case '(': case ')': case '[': case ']':
case ';': case ',': case '?': case '^': case '~': case ';': case ',': case '?': case '^': case '~':
@ -217,7 +212,7 @@ SQInteger SQLexer::Lex()
NEXT(); NEXT();
if (CUR_CHAR != '.'){ RETURN_TOKEN('.') } if (CUR_CHAR != '.'){ RETURN_TOKEN('.') }
NEXT(); NEXT();
if (CUR_CHAR != '.'){ Error("invalid token '..'"); } if (CUR_CHAR != '.'){ throw CompileException("invalid token '..'"); }
NEXT(); NEXT();
RETURN_TOKEN(TK_VARPARAMS); RETURN_TOKEN(TK_VARPARAMS);
case '&': case '&':
@ -263,7 +258,7 @@ SQInteger SQLexer::Lex()
} }
else { else {
SQInteger c = CUR_CHAR; SQInteger c = CUR_CHAR;
if (iscntrl((int)c)) Error("unexpected character(control)"); if (iscntrl((int)c)) throw CompileException("unexpected character(control)");
NEXT(); NEXT();
RETURN_TOKEN(c); RETURN_TOKEN(c);
} }
@ -274,7 +269,7 @@ SQInteger SQLexer::Lex()
return 0; return 0;
} }
SQInteger SQLexer::GetIDType(SQChar *s) SQInteger SQLexer::GetIDType(std::string_view s)
{ {
SQObjectPtr t; SQObjectPtr t;
if(_keywords->Get(SQString::Create(_sharedstate, s), t)) { if(_keywords->Get(SQString::Create(_sharedstate, s), t)) {
@ -293,10 +288,10 @@ SQInteger SQLexer::ReadString(char32_t ndelim,bool verbatim)
while(CUR_CHAR != ndelim) { while(CUR_CHAR != ndelim) {
switch(CUR_CHAR) { switch(CUR_CHAR) {
case SQUIRREL_EOB: case SQUIRREL_EOB:
Error("unfinished string"); throw CompileException("unfinished string");
return -1; return -1;
case '\n': case '\n':
if(!verbatim) Error("newline in a constant"); if(!verbatim) throw CompileException("newline in a constant");
APPEND_CHAR(CUR_CHAR); NEXT(); APPEND_CHAR(CUR_CHAR); NEXT();
_currentline++; _currentline++;
break; break;
@ -308,18 +303,18 @@ SQInteger SQLexer::ReadString(char32_t ndelim,bool verbatim)
NEXT(); NEXT();
switch(CUR_CHAR) { switch(CUR_CHAR) {
case 'x': NEXT(); { case 'x': NEXT(); {
if(!isxdigit(CUR_CHAR)) Error("hexadecimal number expected"); if(!isxdigit(CUR_CHAR)) throw CompileException("hexadecimal number expected");
const SQInteger maxdigits = 4; const SQInteger maxdigits = 4;
SQChar temp[maxdigits+1]; char temp[maxdigits];
SQInteger n = 0; size_t n = 0;
while(isxdigit(CUR_CHAR) && n < maxdigits) { while(isxdigit(CUR_CHAR) && n < maxdigits) {
temp[n] = CUR_CHAR; temp[n] = CUR_CHAR;
n++; n++;
NEXT(); NEXT();
} }
temp[n] = 0; auto val = ParseInteger(std::string_view{temp, n}, 16);
SQChar *sTemp; if (!val.has_value()) throw CompileException("hexadecimal number expected");
APPEND_CHAR((SQChar)strtoul(temp,&sTemp,16)); APPEND_CHAR(static_cast<char>(*val));
} }
break; break;
case 't': APPEND_CHAR('\t'); NEXT(); break; case 't': APPEND_CHAR('\t'); NEXT(); break;
@ -334,7 +329,7 @@ SQInteger SQLexer::ReadString(char32_t ndelim,bool verbatim)
case '"': APPEND_CHAR('"'); NEXT(); break; case '"': APPEND_CHAR('"'); NEXT(); break;
case '\'': APPEND_CHAR('\''); NEXT(); break; case '\'': APPEND_CHAR('\''); NEXT(); break;
default: default:
Error("unrecognised escaper char"); throw CompileException("unrecognised escaper char");
break; break;
} }
} }
@ -353,49 +348,16 @@ SQInteger SQLexer::ReadString(char32_t ndelim,bool verbatim)
break; break;
} }
} }
TERMINATE_BUFFER();
SQInteger len = _longstr.size()-1;
if(ndelim == '\'') { if(ndelim == '\'') {
if(len == 0) Error("empty constant"); if(_longstr.empty()) throw CompileException("empty constant");
if(len > 1) Error("constant too long"); if(_longstr.size() > 1) throw CompileException("constant too long");
_nvalue = _longstr[0]; _nvalue = _longstr[0];
return TK_INTEGER; return TK_INTEGER;
} }
_svalue = &_longstr[0];
return TK_STRING_LITERAL; return TK_STRING_LITERAL;
} }
void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res) SQInteger scisodigit(char c) { return c >= '0' && c <= '7'; }
{
*res = 0;
while(*s != 0)
{
if(isdigit(*s)) *res = (*res)*16+((*s++)-'0');
else if(isxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10);
else { assert(0); }
}
}
void LexInteger(const SQChar *s,SQUnsignedInteger *res)
{
*res = 0;
while(*s != 0)
{
*res = (*res)*10+((*s++)-'0');
}
}
SQInteger scisodigit(SQChar c) { return c >= '0' && c <= '7'; }
void LexOctal(const SQChar *s,SQUnsignedInteger *res)
{
*res = 0;
while(*s != 0)
{
if(scisodigit(*s)) *res = (*res)*8+((*s++)-'0');
else { assert(0); }
}
}
SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; } SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; }
@ -409,7 +371,6 @@ SQInteger SQLexer::ReadNumber()
#define TSCIENTIFIC 4 #define TSCIENTIFIC 4
#define TOCTAL 5 #define TOCTAL 5
SQInteger type = TINT, firstchar = CUR_CHAR; SQInteger type = TINT, firstchar = CUR_CHAR;
SQChar *sTemp;
INIT_TEMP_STRING(); INIT_TEMP_STRING();
NEXT(); NEXT();
if(firstchar == '0' && (toupper(CUR_CHAR) == 'X' || scisodigit(CUR_CHAR)) ) { if(firstchar == '0' && (toupper(CUR_CHAR) == 'X' || scisodigit(CUR_CHAR)) ) {
@ -419,7 +380,7 @@ SQInteger SQLexer::ReadNumber()
APPEND_CHAR(CUR_CHAR); APPEND_CHAR(CUR_CHAR);
NEXT(); NEXT();
} }
if(isdigit(CUR_CHAR)) Error("invalid octal number"); if(isdigit(CUR_CHAR)) throw CompileException("invalid octal number");
} }
else { else {
NEXT(); NEXT();
@ -428,7 +389,7 @@ SQInteger SQLexer::ReadNumber()
APPEND_CHAR(CUR_CHAR); APPEND_CHAR(CUR_CHAR);
NEXT(); NEXT();
} }
if(_longstr.size() > MAX_HEX_DIGITS) Error("too many digits for an Hex number"); if(_longstr.size() > MAX_HEX_DIGITS) throw CompileException("too many digits for an Hex number");
} }
} }
else { else {
@ -436,7 +397,7 @@ SQInteger SQLexer::ReadNumber()
while (CUR_CHAR == '.' || isdigit(CUR_CHAR) || isexponent(CUR_CHAR)) { while (CUR_CHAR == '.' || isdigit(CUR_CHAR) || isexponent(CUR_CHAR)) {
if(CUR_CHAR == '.' || isexponent(CUR_CHAR)) type = TFLOAT; if(CUR_CHAR == '.' || isexponent(CUR_CHAR)) type = TFLOAT;
if(isexponent(CUR_CHAR)) { if(isexponent(CUR_CHAR)) {
if(type != TFLOAT) Error("invalid numeric format"); if(type != TFLOAT) throw CompileException("invalid numeric format");
type = TSCIENTIFIC; type = TSCIENTIFIC;
APPEND_CHAR(CUR_CHAR); APPEND_CHAR(CUR_CHAR);
NEXT(); NEXT();
@ -444,27 +405,29 @@ SQInteger SQLexer::ReadNumber()
APPEND_CHAR(CUR_CHAR); APPEND_CHAR(CUR_CHAR);
NEXT(); NEXT();
} }
if(!isdigit(CUR_CHAR)) Error("exponent expected"); if(!isdigit(CUR_CHAR)) throw CompileException("exponent expected");
} }
APPEND_CHAR(CUR_CHAR); APPEND_CHAR(CUR_CHAR);
NEXT(); NEXT();
} }
} }
TERMINATE_BUFFER();
switch(type) { switch(type) {
case TSCIENTIFIC: case TSCIENTIFIC:
case TFLOAT: case TFLOAT: {
_fvalue = (SQFloat)strtod(&_longstr[0],&sTemp); std::string str{View()};
char *sTemp;
_fvalue = (SQFloat)strtod(str.c_str(),&sTemp);
return TK_FLOAT; return TK_FLOAT;
}
case TINT: case TINT:
LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue); _nvalue = ParseInteger<SQUnsignedInteger>(View(), 10).value_or(0);
return TK_INTEGER; return TK_INTEGER;
case THEX: case THEX:
LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); _nvalue = ParseInteger<SQUnsignedInteger>(View(), 16).value_or(0);
return TK_INTEGER; return TK_INTEGER;
case TOCTAL: case TOCTAL:
LexOctal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); _nvalue = ParseInteger<SQUnsignedInteger>(View(), 8).value_or(0);
return TK_INTEGER; return TK_INTEGER;
} }
return 0; return 0;
@ -478,10 +441,6 @@ SQInteger SQLexer::ReadID()
APPEND_CHAR(CUR_CHAR); APPEND_CHAR(CUR_CHAR);
NEXT(); NEXT();
} while(isalnum(CUR_CHAR) || CUR_CHAR == '_'); } while(isalnum(CUR_CHAR) || CUR_CHAR == '_');
TERMINATE_BUFFER(); res = GetIDType(View());
res = GetIDType(&_longstr[0]);
if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) {
_svalue = &_longstr[0];
}
return res; return res;
} }

View File

@ -6,11 +6,10 @@ struct SQLexer
{ {
~SQLexer(); ~SQLexer();
SQLexer(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up); SQLexer(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up);
[[noreturn]] void Error(const SQChar *err);
SQInteger Lex(); SQInteger Lex();
const SQChar *Tok2Str(SQInteger tok); std::optional<std::string_view> Tok2Str(SQInteger tok);
private: private:
SQInteger GetIDType(SQChar *s); SQInteger GetIDType(std::string_view s);
SQInteger ReadString(char32_t ndelim,bool verbatim); SQInteger ReadString(char32_t ndelim,bool verbatim);
SQInteger ReadNumber(); SQInteger ReadNumber();
void LexBlockComment(); void LexBlockComment();
@ -20,21 +19,21 @@ private:
SQTable *_keywords; SQTable *_keywords;
void INIT_TEMP_STRING() { _longstr.resize(0); } void INIT_TEMP_STRING() { _longstr.resize(0); }
void APPEND_CHAR(char32_t c); void APPEND_CHAR(char32_t c);
void TERMINATE_BUFFER() { _longstr.push_back('\0'); }
public: public:
SQInteger _prevtoken; SQInteger _prevtoken;
SQInteger _currentline; SQInteger _currentline;
SQInteger _lasttokenline; SQInteger _lasttokenline;
SQInteger _currentcolumn; SQInteger _currentcolumn;
const SQChar *_svalue;
SQInteger _nvalue; SQInteger _nvalue;
SQFloat _fvalue; SQFloat _fvalue;
SQLEXREADFUNC _readf; SQLEXREADFUNC _readf;
SQUserPointer _up; SQUserPointer _up;
char32_t _currdata; char32_t _currdata;
SQSharedState *_sharedstate; SQSharedState *_sharedstate;
sqvector<SQChar> _longstr; sqvector<char> _longstr;
std::string_view View() const { return std::string_view(_longstr._vals, _longstr.size()); }
}; };
#endif #endif

View File

@ -18,7 +18,7 @@
#include "../../../safeguards.h" #include "../../../safeguards.h"
const SQChar *IdType2Name(SQObjectType type) std::string_view IdType2Name(SQObjectType type)
{ {
switch(_RAW_TYPE(type)) switch(_RAW_TYPE(type))
{ {
@ -42,25 +42,25 @@ const SQChar *IdType2Name(SQObjectType type)
case _RT_INSTANCE: return "instance"; case _RT_INSTANCE: return "instance";
case _RT_WEAKREF: return "weakref"; case _RT_WEAKREF: return "weakref";
default: default:
return nullptr; NOT_REACHED();
} }
} }
const SQChar *GetTypeName(const SQObjectPtr &obj1) std::string_view GetTypeName(const SQObjectPtr &obj1)
{ {
return IdType2Name(type(obj1)); return IdType2Name(type(obj1));
} }
SQString *SQString::Create(SQSharedState *ss,const SQChar *s,SQInteger len) SQString *SQString::Create(SQSharedState *ss,std::string_view s)
{ {
SQString *str=ADD_STRING(ss,s,len); SQString *str=ss->_stringtable->Add(s);
str->_sharedstate=ss; str->_sharedstate=ss;
return str; return str;
} }
void SQString::Release() void SQString::Release()
{ {
REMOVE_STRING(_sharedstate,this); _sharedstate->_stringtable->Remove(this);
} }
SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
@ -202,24 +202,21 @@ void SQArray::Extend(const SQArray *a){
Append(a->_values[i]); Append(a->_values[i]);
} }
const SQChar* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop) std::optional<std::string_view> SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop)
{ {
SQUnsignedInteger nvars=_nlocalvarinfos; SQUnsignedInteger nvars=_nlocalvarinfos;
const SQChar *res=nullptr;
if(nvars>=nseq){ if(nvars>=nseq){
for(SQUnsignedInteger i=0;i<nvars;i++){ for(SQUnsignedInteger i=0;i<nvars;i++){
if(_localvarinfos[i]._start_op<=nop && _localvarinfos[i]._end_op>=nop) if(_localvarinfos[i]._start_op<=nop && _localvarinfos[i]._end_op>=nop) {
{
if(nseq==0){ if(nseq==0){
vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]); vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]);
res=_stringval(_localvarinfos[i]._name); return _stringval(_localvarinfos[i]._name);
break;
} }
nseq--; nseq--;
} }
} }
} }
return res; return std::nullopt;
} }
SQInteger SQFunctionProto::GetLine(SQInstruction *curr) SQInteger SQFunctionProto::GetLine(SQInstruction *curr)
@ -273,10 +270,13 @@ bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o
{ {
_CHECK_IO(SafeWrite(v,write,up,&type(o),sizeof(SQObjectType))); _CHECK_IO(SafeWrite(v,write,up,&type(o),sizeof(SQObjectType)));
switch(type(o)){ switch(type(o)){
case OT_STRING: case OT_STRING: {
_CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger))); auto str = _string(o)->Span();
_CHECK_IO(SafeWrite(v,write,up,_stringval(o),_string(o)->_len)); SQInteger len = str.size();
_CHECK_IO(SafeWrite(v,write,up,&len,sizeof(len)));
_CHECK_IO(SafeWrite(v,write,up,str.data(),len));
break; break;
}
case OT_INTEGER: case OT_INTEGER:
_CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break; _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break;
case OT_FLOAT: case OT_FLOAT:
@ -297,11 +297,11 @@ bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o)
switch(t){ switch(t){
case OT_STRING:{ case OT_STRING:{
SQInteger len; SQInteger len;
_CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger))); _CHECK_IO(SafeRead(v,read,up,&len,sizeof(len)));
_CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(len),len)); _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(len).data(),len));
o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len); o=SQString::Create(_ss(v),std::string_view(_ss(v)->GetScratchPad(-1).data(),len));
}
break; break;
}
case OT_INTEGER:{ case OT_INTEGER:{
SQInteger i; SQInteger i;
_CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break; _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break;
@ -323,7 +323,7 @@ bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o)
bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
{ {
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD)); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD));
_CHECK_IO(WriteTag(v,write,up,sizeof(SQChar))); _CHECK_IO(WriteTag(v,write,up,sizeof(char)));
_CHECK_IO(_funcproto(_function)->Save(v,up,write)); _CHECK_IO(_funcproto(_function)->Save(v,up,write));
_CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL)); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL));
return true; return true;
@ -332,7 +332,7 @@ bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write)
bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret)
{ {
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD));
_CHECK_IO(CheckTag(v,read,up,sizeof(SQChar))); _CHECK_IO(CheckTag(v,read,up,sizeof(char)));
SQObjectPtr func; SQObjectPtr func;
_CHECK_IO(SQFunctionProto::Load(v,up,read,func)); _CHECK_IO(SQFunctionProto::Load(v,up,read,func));
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL));

View File

@ -138,7 +138,7 @@ struct SQObjectPtr;
#define _refcounted(obj) ((obj)._unVal.pRefCounted) #define _refcounted(obj) ((obj)._unVal.pRefCounted)
#define _rawval(obj) ((obj)._unVal.raw) #define _rawval(obj) ((obj)._unVal.raw)
#define _stringval(obj) (obj)._unVal.pString->_val #define _stringval(obj) (obj)._unVal.pString->View()
#define _userdataval(obj) (obj)._unVal.pUserData->_val #define _userdataval(obj) (obj)._unVal.pUserData->_val
#define tofloat(num) ((type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num)) #define tofloat(num) ((type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num))
@ -357,7 +357,7 @@ struct SQObjectPtr : public SQObject
return *this; return *this;
} }
private: private:
SQObjectPtr(const SQChar *){} //safety SQObjectPtr(const char *) = delete; //safety
}; };
inline void _Swap(SQObject &a,SQObject &b) inline void _Swap(SQObject &a,SQObject &b)
@ -449,8 +449,8 @@ struct SQDelegable : public CHAINABLE_OBJ {
SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx); SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx);
typedef sqvector<SQObjectPtr> SQObjectPtrVec; typedef sqvector<SQObjectPtr> SQObjectPtrVec;
typedef sqvector<SQInteger> SQIntVec; typedef sqvector<SQInteger> SQIntVec;
const SQChar *GetTypeName(const SQObjectPtr &obj1); std::string_view GetTypeName(const SQObjectPtr &obj1);
const SQChar *IdType2Name(SQObjectType type); std::string_view IdType2Name(SQObjectType type);

View File

@ -87,7 +87,7 @@ enum SQOpcode
}; };
struct SQInstructionDesc { struct SQInstructionDesc {
const SQChar *name; std::string_view name;
}; };
struct SQInstruction struct SQInstruction

View File

@ -14,6 +14,7 @@
#include "sqarray.h" #include "sqarray.h"
#include "squserdata.h" #include "squserdata.h"
#include "sqclass.h" #include "sqclass.h"
#include "../../../core/string_consumer.hpp"
#include "../../../safeguards.h" #include "../../../safeguards.h"
@ -32,14 +33,12 @@ SQObjectPtr _minusone_((SQInteger)-1);
_table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \ _table(_metamethodsmap)->NewSlot(_metamethods->back(),(SQInteger)(_metamethods->size()-1)); \
} }
bool CompileTypemask(SQIntVec &res,const SQChar *typemask) bool CompileTypemask(SQIntVec &res,std::string_view typemask)
{ {
SQInteger i = 0;
SQInteger mask = 0; SQInteger mask = 0;
while(typemask[i] != 0) { StringConsumer consumer{typemask};
while (consumer.AnyBytesLeft()) {
switch(typemask[i]){ switch(consumer.ReadChar()){
case 'o': mask |= _RT_NULL; break; case 'o': mask |= _RT_NULL; break;
case 'i': mask |= _RT_INTEGER; break; case 'i': mask |= _RT_INTEGER; break;
case 'f': mask |= _RT_FLOAT; break; case 'f': mask |= _RT_FLOAT; break;
@ -56,37 +55,32 @@ bool CompileTypemask(SQIntVec &res,const SQChar *typemask)
case 'x': mask |= _RT_INSTANCE; break; case 'x': mask |= _RT_INSTANCE; break;
case 'y': mask |= _RT_CLASS; break; case 'y': mask |= _RT_CLASS; break;
case 'r': mask |= _RT_WEAKREF; break; case 'r': mask |= _RT_WEAKREF; break;
case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue; case '.': mask = -1; res.push_back(mask); mask = 0; continue;
case ' ': i++; continue; //ignores spaces case ' ': continue; //ignores spaces
default: default:
return false; return false;
} }
i++;
if(typemask[i] == '|') { if(consumer.ReadCharIf('|')) {
i++; if(!consumer.AnyBytesLeft()) return false;
if(typemask[i] == 0)
return false;
continue; continue;
} }
res.push_back(mask); res.push_back(mask);
mask = 0; mask = 0;
} }
return true; return true;
} }
SQTable *CreateDefaultDelegate(SQSharedState *ss,SQRegFunction *funcz) SQTable *CreateDefaultDelegate(SQSharedState *ss,const std::initializer_list<SQRegFunction> &funcz)
{ {
SQInteger i=0;
SQTable *t=SQTable::Create(ss,0); SQTable *t=SQTable::Create(ss,0);
while(funcz[i].name!=nullptr){ for (auto &func : funcz) {
SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f); SQNativeClosure *nc = SQNativeClosure::Create(ss,func.f);
nc->_nparamscheck = funcz[i].nparamscheck; nc->_nparamscheck = func.nparamscheck;
nc->_name = SQString::Create(ss,funcz[i].name); nc->_name = SQString::Create(ss,func.name);
if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask)) if(func.typemask.has_value() && !CompileTypemask(nc->_typecheck,*func.typemask))
return nullptr; return nullptr;
t->NewSlot(SQString::Create(ss,funcz[i].name),nc); t->NewSlot(SQString::Create(ss,func.name),nc);
i++;
} }
return t; return t;
} }
@ -97,8 +91,6 @@ SQSharedState::SQSharedState()
_printfunc = nullptr; _printfunc = nullptr;
_debuginfo = false; _debuginfo = false;
_notifyallexceptions = false; _notifyallexceptions = false;
_scratchpad=nullptr;
_scratchpadsize=0;
_collectable_free_processing = false; _collectable_free_processing = false;
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
_gc_chain=nullptr; _gc_chain=nullptr;
@ -212,7 +204,6 @@ SQSharedState::~SQSharedState()
sq_delete(_systemstrings,SQObjectPtrVec); sq_delete(_systemstrings,SQObjectPtrVec);
sq_delete(_metamethods,SQObjectPtrVec); sq_delete(_metamethods,SQObjectPtrVec);
sq_delete(_stringtable,SQStringTable); sq_delete(_stringtable,SQStringTable);
if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
} }
@ -357,19 +348,16 @@ void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
} }
#endif #endif
SQChar* SQSharedState::GetScratchPad(SQInteger size) std::span<char> SQSharedState::GetScratchPad(SQInteger size)
{ {
SQInteger newsize; SQInteger newsize;
if(size>0) { if(size>0) {
if(_scratchpadsize < size) { if(_scratchpad.size() < static_cast<size_t>(size)) {
newsize = size + (size>>1); newsize = size + (size>>1);
_scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize); _scratchpad.resize(newsize);
_scratchpadsize = newsize; }else if(_scratchpad.size() >= static_cast<size_t>(size<<5)) {
newsize = _scratchpad.size() >> 1;
}else if(_scratchpadsize >= (size<<5)) { _scratchpad.resize(newsize);
newsize = _scratchpadsize >> 1;
_scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
_scratchpadsize = newsize;
} }
} }
return _scratchpad; return _scratchpad;
@ -550,36 +538,36 @@ void SQStringTable::AllocNodes(SQInteger size)
{ {
_numofslots = size; _numofslots = size;
_strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots); _strings = (SQString**)SQ_MALLOC(sizeof(SQString*)*_numofslots);
memset(_strings,0,sizeof(SQString*)*(size_t)_numofslots); std::fill_n(_strings, _numofslots, nullptr);
} }
SQString *SQStringTable::Add(const SQChar *news,SQInteger len) static const std::hash<std::string_view> string_table_hash{};
SQString *SQStringTable::Add(std::string_view new_string)
{ {
if(len<0) size_t len = new_string.size();
len = (SQInteger)strlen(news); auto slot = string_table_hash(new_string) & (_numofslots-1);
SQHash h = ::_hashstr(news,(size_t)len)&(_numofslots-1);
SQString *s; SQString *s;
for (s = _strings[h]; s; s = s->_next){ for (s = _strings[slot]; s; s = s->_next){
if(s->_len == len && (!memcmp(news,s->_val,(size_t)len))) if(s->View() == new_string) return s; //found
return s; //found
} }
SQString *t=(SQString *)SQ_MALLOC(len+sizeof(SQString)); SQString *t=(SQString *)SQ_MALLOC(len+sizeof(SQString));
new (t) SQString(news, len); new (t) SQString(new_string);
t->_next = _strings[h]; t->_next = _strings[slot];
_strings[h] = t; _strings[slot] = t;
_slotused++; _slotused++;
if (_slotused > _numofslots) /* too crowded? */ if (_slotused > _numofslots) /* too crowded? */
Resize(_numofslots*2); Resize(_numofslots*2);
return t; return t;
} }
SQString::SQString(const SQChar *news, SQInteger len) SQString::SQString(std::string_view new_string)
{ {
memcpy(_val,news,(size_t)len); std::ranges::copy(new_string, _val);
_val[len] = '\0'; _val[new_string.size()] = '\0';
_len = len; _len = new_string.size();
_hash = ::_hashstr(news,(size_t)len); _hash = string_table_hash(new_string);
_next = nullptr; _next = nullptr;
_sharedstate = nullptr; _sharedstate = nullptr;
} }
@ -615,7 +603,7 @@ void SQStringTable::Remove(SQString *bs)
else else
_strings[h] = s->_next; _strings[h] = s->_next;
_slotused--; _slotused--;
SQInteger slen = s->_len; size_t slen = s->View().size();
s->~SQString(); s->~SQString();
SQ_FREE(s,sizeof(SQString) + slen); SQ_FREE(s,sizeof(SQString) + slen);
return; return;

View File

@ -13,7 +13,7 @@ struct SQStringTable
{ {
SQStringTable(); SQStringTable();
~SQStringTable(); ~SQStringTable();
SQString *Add(const SQChar *,SQInteger len); SQString *Add(std::string_view str);
void Remove(SQString *); void Remove(SQString *);
private: private:
void Resize(SQInteger size); void Resize(SQInteger size);
@ -49,9 +49,6 @@ private:
RefNode **_buckets; RefNode **_buckets;
}; };
#define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len)
#define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr)
struct SQObjectPtr; struct SQObjectPtr;
struct SQSharedState struct SQSharedState
@ -59,7 +56,7 @@ struct SQSharedState
SQSharedState(); SQSharedState();
~SQSharedState(); ~SQSharedState();
public: public:
SQChar* GetScratchPad(SQInteger size); std::span<char> GetScratchPad(SQInteger size);
SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name);
void DelayFinalFree(SQCollectable *collectable); void DelayFinalFree(SQCollectable *collectable);
#ifndef NO_GARBAGE_COLLECTOR #ifndef NO_GARBAGE_COLLECTOR
@ -84,33 +81,32 @@ public:
#endif #endif
SQObjectPtr _root_vm; SQObjectPtr _root_vm;
SQObjectPtr _table_default_delegate; SQObjectPtr _table_default_delegate;
static SQRegFunction _table_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _table_default_delegate_funcz;
SQObjectPtr _array_default_delegate; SQObjectPtr _array_default_delegate;
static SQRegFunction _array_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _array_default_delegate_funcz;
SQObjectPtr _string_default_delegate; SQObjectPtr _string_default_delegate;
static SQRegFunction _string_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _string_default_delegate_funcz;
SQObjectPtr _number_default_delegate; SQObjectPtr _number_default_delegate;
static SQRegFunction _number_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _number_default_delegate_funcz;
SQObjectPtr _generator_default_delegate; SQObjectPtr _generator_default_delegate;
static SQRegFunction _generator_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _generator_default_delegate_funcz;
SQObjectPtr _closure_default_delegate; SQObjectPtr _closure_default_delegate;
static SQRegFunction _closure_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _closure_default_delegate_funcz;
SQObjectPtr _thread_default_delegate; SQObjectPtr _thread_default_delegate;
static SQRegFunction _thread_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _thread_default_delegate_funcz;
SQObjectPtr _class_default_delegate; SQObjectPtr _class_default_delegate;
static SQRegFunction _class_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _class_default_delegate_funcz;
SQObjectPtr _instance_default_delegate; SQObjectPtr _instance_default_delegate;
static SQRegFunction _instance_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _instance_default_delegate_funcz;
SQObjectPtr _weakref_default_delegate; SQObjectPtr _weakref_default_delegate;
static SQRegFunction _weakref_default_delegate_funcz[]; static const std::initializer_list<SQRegFunction> _weakref_default_delegate_funcz;
SQCOMPILERERROR _compilererrorhandler; SQCOMPILERERROR _compilererrorhandler;
SQPRINTFUNCTION _printfunc; SQPRINTFUNCTION _printfunc;
bool _debuginfo; bool _debuginfo;
bool _notifyallexceptions; bool _notifyallexceptions;
private: private:
SQChar *_scratchpad; std::vector<char> _scratchpad;
SQInteger _scratchpadsize;
}; };
#define _sp(s) (_sharedstate->GetScratchPad(s)) #define _sp(s) (_sharedstate->GetScratchPad(s))
@ -133,6 +129,6 @@ extern SQObjectPtr _false_;
extern SQObjectPtr _one_; extern SQObjectPtr _one_;
extern SQObjectPtr _minusone_; extern SQObjectPtr _minusone_;
bool CompileTypemask(SQIntVec &res,const SQChar *typemask); bool CompileTypemask(SQIntVec &res,std::string_view typemask);
#endif //_SQSTATE_H_ #endif //_SQSTATE_H_

View File

@ -2,29 +2,23 @@
#ifndef _SQSTRING_H_ #ifndef _SQSTRING_H_
#define _SQSTRING_H_ #define _SQSTRING_H_
inline SQHash _hashstr (const SQChar *s, size_t l)
{
SQHash h = (SQHash)l; /* seed */
size_t step = (l>>5)|1; /* if string is too long, don't hash all its chars */
for (; l>=step; l-=step)
h = h ^ ((h<<5)+(h>>2)+(unsigned short)*(s++));
return h;
}
struct SQString : public SQRefCounted struct SQString : public SQRefCounted
{ {
SQString(const SQChar *news, SQInteger len); SQString(std::string_view str);
~SQString(){} ~SQString(){}
public: public:
static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 ); static SQString *Create(SQSharedState *ss, std::string_view str);
static SQString *Create(SQSharedState *ss, const std::string &str) { return Create(ss, str.data(), str.size()); }
SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
void Release() override; void Release() override;
SQSharedState *_sharedstate; SQSharedState *_sharedstate;
SQString *_next; //chain for the string table SQString *_next; //chain for the string table
std::size_t _hash;
std::string_view View() const { return std::string_view(this->_val, this->_len); }
std::span<char> Span() { return std::span<char>(this->_val, this->_len); }
private:
SQInteger _len; SQInteger _len;
SQHash _hash; char _val[1];
SQChar _val[1];
}; };

View File

@ -31,7 +31,7 @@ struct SQUserData : SQDelegable
SQInteger _size; SQInteger _size;
SQRELEASEHOOK _hook; SQRELEASEHOOK _hook;
SQUserPointer _typetag; SQUserPointer _typetag;
SQChar _val[1]; char _val[1];
}; };
#endif //_SQUSERDATA_H_ #endif //_SQUSERDATA_H_

View File

@ -93,7 +93,7 @@ public:
{ {
_vals[idx].~T(); _vals[idx].~T();
if(idx < (_size - 1)) { if(idx < (_size - 1)) {
memmove(static_cast<void *>(&_vals[idx]), &_vals[idx+1], sizeof(T) * (_size - (size_t)idx - 1)); std::move(&_vals[idx + 1], &_vals[_size], &_vals[idx]);
} }
_size--; _size--;
} }

View File

@ -117,7 +117,6 @@ SQVM::SQVM(SQSharedState *ss)
_in_stackoverflow = false; _in_stackoverflow = false;
_ops_till_suspend = 0; _ops_till_suspend = 0;
_ops_till_suspend_error_threshold = INT64_MIN; _ops_till_suspend_error_threshold = INT64_MIN;
_ops_till_suspend_error_label = nullptr;
_callsstack = nullptr; _callsstack = nullptr;
_callsstacksize = 0; _callsstacksize = 0;
_alloccallsstacksize = 0; _alloccallsstacksize = 0;
@ -198,7 +197,7 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result)
SQObjectPtr res; SQObjectPtr res;
switch(type(o1)){ switch(type(o1)){
case OT_STRING: case OT_STRING:
_RET_SUCCEED(strcmp(_stringval(o1),_stringval(o2))); _RET_SUCCEED(_stringval(o1).compare(_stringval(o2)));
case OT_INTEGER: case OT_INTEGER:
/* FS#3954: wrong integer comparison */ /* FS#3954: wrong integer comparison */
_RET_SUCCEED((_integer(o1)<_integer(o2))?-1:(_integer(o1)==_integer(o2))?0:1); _RET_SUCCEED((_integer(o1)<_integer(o2))?-1:(_integer(o1)==_integer(o2))?0:1);
@ -302,11 +301,7 @@ bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &
SQObjectPtr a, b; SQObjectPtr a, b;
ToString(str, a); ToString(str, a);
ToString(obj, b); ToString(obj, b);
SQInteger l = _string(a)->_len , ol = _string(b)->_len; dest = SQString::Create(_ss(this), fmt::format("{}{}", _stringval(a), _stringval(b)));
SQChar *s = _sp(l + ol + 1);
memcpy(s, _stringval(a), (size_t)l);
memcpy(s + l, _stringval(b), (size_t)ol);
dest = SQString::Create(_ss(this), _spval, l + ol);
return true; return true;
} }
@ -1253,7 +1248,8 @@ bool SQVM::Get(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,
if(fetchroot) { if(fetchroot) {
if(_rawval(STK(0)) == _rawval(self) && if(_rawval(STK(0)) == _rawval(self) &&
type(STK(0)) == type(self)) { type(STK(0)) == type(self)) {
return _table(_roottable)->Get(key,dest); if (_table(_roottable)->Get(key,dest)) return true;
return _table(_ss(this)->_consts)->Get(key,dest);
} }
} }
return false; return false;
@ -1288,9 +1284,10 @@ bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPt
case OT_STRING: case OT_STRING:
if(sq_isnumeric(key)){ if(sq_isnumeric(key)){
SQInteger n=tointeger(key); SQInteger n=tointeger(key);
if(abs((int)n)<_string(self)->_len){ std::string_view str = _stringval(self);
if(n<0)n=_string(self)->_len-n; if(std::abs(n) < static_cast<SQInteger>(str.size())){
dest=SQInteger(_stringval(self)[n]); if(n<0)n=str.size()+n;
dest=SQInteger(str[n]);
return true; return true;
} }
return false; return false;

View File

@ -169,7 +169,7 @@ public:
SQBool _can_suspend; SQBool _can_suspend;
SQInteger _ops_till_suspend; SQInteger _ops_till_suspend;
SQInteger _ops_till_suspend_error_threshold; SQInteger _ops_till_suspend_error_threshold;
const char *_ops_till_suspend_error_label; std::string_view _ops_till_suspend_error_label;
SQBool _in_stackoverflow; SQBool _in_stackoverflow;
bool ShouldSuspend() bool ShouldSuspend()

View File

@ -279,6 +279,10 @@ add_files(
newgrf_animation_type.h newgrf_animation_type.h
newgrf_badge.cpp newgrf_badge.cpp
newgrf_badge.h newgrf_badge.h
newgrf_badge_config.cpp
newgrf_badge_config.h
newgrf_badge_gui.cpp
newgrf_badge_gui.h
newgrf_badge_type.h newgrf_badge_type.h
newgrf_callbacks.h newgrf_callbacks.h
newgrf_canal.cpp newgrf_canal.cpp

View File

@ -135,8 +135,8 @@ public:
static AIScannerLibrary *GetScannerLibrary(); static AIScannerLibrary *GetScannerLibrary();
/** Wrapper function for AIScanner::HasAI */ /** Wrapper function for AIScanner::HasAI */
static bool HasAI(const struct ContentInfo *ci, bool md5sum); static bool HasAI(const ContentInfo &ci, bool md5sum);
static bool HasAILibrary(const ContentInfo *ci, bool md5sum); static bool HasAILibrary(const ContentInfo &ci, bool md5sum);
private: private:
static uint frame_counter; ///< Tick counter for the AI code static uint frame_counter; ///< Tick counter for the AI code
static std::unique_ptr<AIScannerInfo> scanner_info; ///< ScriptScanner instance that is used to find AIs static std::unique_ptr<AIScannerInfo> scanner_info; ///< ScriptScanner instance that is used to find AIs

View File

@ -334,12 +334,12 @@
* @param md5sum whether to check the MD5 checksum * @param md5sum whether to check the MD5 checksum
* @return true iff we have an AI (library) matching. * @return true iff we have an AI (library) matching.
*/ */
/* static */ bool AI::HasAI(const ContentInfo *ci, bool md5sum) /* static */ bool AI::HasAI(const ContentInfo &ci, bool md5sum)
{ {
return AI::scanner_info->HasScript(ci, md5sum); return AI::scanner_info->HasScript(ci, md5sum);
} }
/* static */ bool AI::HasAILibrary(const ContentInfo *ci, bool md5sum) /* static */ bool AI::HasAILibrary(const ContentInfo &ci, bool md5sum)
{ {
return AI::scanner_library->HasScript(ci, md5sum); return AI::scanner_library->HasScript(ci, md5sum);
} }

View File

@ -84,7 +84,7 @@ static constexpr NWidgetPart _nested_ai_config_widgets[] = {
/** Window definition for the configure AI window. */ /** Window definition for the configure AI window. */
static WindowDesc _ai_config_desc( static WindowDesc _ai_config_desc(
WDP_CENTER, nullptr, 0, 0, WDP_CENTER, {}, 0, 0,
WC_GAME_OPTIONS, WC_NONE, WC_GAME_OPTIONS, WC_NONE,
{}, {},
_nested_ai_config_widgets _nested_ai_config_widgets
@ -142,7 +142,7 @@ struct AIConfigWindow : public Window {
case WID_AIC_LIST: case WID_AIC_LIST:
this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height; this->line_height = GetCharacterHeight(FS_NORMAL) + padding.height;
resize.height = this->line_height; fill.height = resize.height = this->line_height;
size.height = 8 * this->line_height; size.height = 8 * this->line_height;
break; break;
} }
@ -218,7 +218,7 @@ struct AIConfigWindow : public Window {
if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TFT_CONTENT_END) { if (widget >= WID_AIC_TEXTFILE && widget < WID_AIC_TEXTFILE + TFT_CONTENT_END) {
if (this->selected_slot == CompanyID::Invalid() || AIConfig::GetConfig(this->selected_slot) == nullptr) return; if (this->selected_slot == CompanyID::Invalid() || AIConfig::GetConfig(this->selected_slot) == nullptr) return;
ShowScriptTextfileWindow((TextfileType)(widget - WID_AIC_TEXTFILE), this->selected_slot); ShowScriptTextfileWindow(this, static_cast<TextfileType>(widget - WID_AIC_TEXTFILE), this->selected_slot);
return; return;
} }
@ -250,23 +250,23 @@ struct AIConfigWindow : public Window {
} }
case WID_AIC_LIST: { // Select a slot case WID_AIC_LIST: { // Select a slot
this->selected_slot = (CompanyID)this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget); this->selected_slot = static_cast<CompanyID>(this->vscroll->GetScrolledRowFromWidget(pt.y, this, widget));
this->InvalidateData(); this->InvalidateData();
if (click_count > 1 && IsEditable(this->selected_slot)) ShowScriptListWindow((CompanyID)this->selected_slot, _ctrl_pressed); if (click_count > 1 && IsEditable(this->selected_slot)) ShowScriptListWindow(this->selected_slot, _ctrl_pressed);
break; break;
} }
case WID_AIC_MOVE_UP: case WID_AIC_MOVE_UP:
if (IsEditable(this->selected_slot) && IsEditable((CompanyID)(this->selected_slot - 1))) { if (IsEditable(this->selected_slot) && IsEditable(static_cast<CompanyID>(this->selected_slot - 1))) {
std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot - 1]); std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot - 1]);
this->selected_slot = CompanyID(this->selected_slot - 1); this->selected_slot = static_cast<CompanyID>(this->selected_slot - 1);
this->vscroll->ScrollTowards(this->selected_slot.base()); this->vscroll->ScrollTowards(this->selected_slot.base());
this->InvalidateData(); this->InvalidateData();
} }
break; break;
case WID_AIC_MOVE_DOWN: case WID_AIC_MOVE_DOWN:
if (IsEditable(this->selected_slot) && IsEditable((CompanyID)(this->selected_slot + 1))) { if (IsEditable(this->selected_slot) && IsEditable(static_cast<CompanyID>(this->selected_slot + 1))) {
std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot + 1]); std::swap(GetGameSettings().script_config.ai[this->selected_slot], GetGameSettings().script_config.ai[this->selected_slot + 1]);
++this->selected_slot; ++this->selected_slot;
this->vscroll->ScrollTowards(this->selected_slot.base()); this->vscroll->ScrollTowards(this->selected_slot.base());
@ -282,11 +282,11 @@ struct AIConfigWindow : public Window {
} }
case WID_AIC_CHANGE: // choose other AI case WID_AIC_CHANGE: // choose other AI
if (IsEditable(this->selected_slot)) ShowScriptListWindow((CompanyID)this->selected_slot, _ctrl_pressed); if (IsEditable(this->selected_slot)) ShowScriptListWindow(this->selected_slot, _ctrl_pressed);
break; break;
case WID_AIC_CONFIGURE: // change the settings for an AI case WID_AIC_CONFIGURE: // change the settings for an AI
ShowScriptSettingsWindow((CompanyID)this->selected_slot); ShowScriptSettingsWindow(this->selected_slot);
break; break;
case WID_AIC_CONTENT_DOWNLOAD: case WID_AIC_CONTENT_DOWNLOAD:
@ -320,8 +320,8 @@ struct AIConfigWindow : public Window {
this->SetWidgetDisabledState(WID_AIC_INCREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MAX_COMPETITORS_INTERVAL); this->SetWidgetDisabledState(WID_AIC_INCREASE_INTERVAL, GetGameSettings().difficulty.competitors_interval == MAX_COMPETITORS_INTERVAL);
this->SetWidgetDisabledState(WID_AIC_CHANGE, !IsEditable(this->selected_slot)); this->SetWidgetDisabledState(WID_AIC_CHANGE, !IsEditable(this->selected_slot));
this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == CompanyID::Invalid() || config->GetConfigList()->empty()); this->SetWidgetDisabledState(WID_AIC_CONFIGURE, this->selected_slot == CompanyID::Invalid() || config->GetConfigList()->empty());
this->SetWidgetDisabledState(WID_AIC_MOVE_UP, !IsEditable(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot - 1))); this->SetWidgetDisabledState(WID_AIC_MOVE_UP, !IsEditable(this->selected_slot) || !IsEditable(static_cast<CompanyID>(this->selected_slot - 1)));
this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, !IsEditable(this->selected_slot) || !IsEditable((CompanyID)(this->selected_slot + 1))); this->SetWidgetDisabledState(WID_AIC_MOVE_DOWN, !IsEditable(this->selected_slot) || !IsEditable(static_cast<CompanyID>(this->selected_slot + 1)));
this->SetWidgetDisabledState(WID_AIC_OPEN_URL, this->selected_slot == CompanyID::Invalid() || config->GetInfo() == nullptr || config->GetInfo()->GetURL().empty()); this->SetWidgetDisabledState(WID_AIC_OPEN_URL, this->selected_slot == CompanyID::Invalid() || config->GetInfo() == nullptr || config->GetInfo()->GetURL().empty());
for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) { for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {

View File

@ -27,14 +27,14 @@ static bool CheckAPIVersion(const std::string &api_version)
return std::ranges::find(AIInfo::ApiVersions, api_version) != std::end(AIInfo::ApiVersions); return std::ranges::find(AIInfo::ApiVersions, api_version) != std::end(AIInfo::ApiVersions);
} }
template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq_pushstring(vm, "AIInfo", -1); return 1; } template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq_pushstring(vm, "AIInfo"); return 1; }
/* static */ void AIInfo::RegisterAPI(Squirrel *engine) /* static */ void AIInfo::RegisterAPI(Squirrel &engine)
{ {
/* Create the AIInfo class, and add the RegisterAI function */ /* Create the AIInfo class, and add the RegisterAI function */
DefSQClass<AIInfo, ScriptType::AI> SQAIInfo("AIInfo"); DefSQClass<AIInfo, ScriptType::AI> SQAIInfo("AIInfo");
SQAIInfo.PreRegister(engine); SQAIInfo.PreRegister(engine);
SQAIInfo.AddConstructor<void (AIInfo::*)(), 1>(engine, "x"); SQAIInfo.AddConstructor<void (AIInfo::*)()>(engine, "x");
SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting"); SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddSetting, "AddSetting");
SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels"); SQAIInfo.DefSQAdvancedMethod(engine, &AIInfo::AddLabels, "AddLabels");
SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "CONFIG_NONE"); SQAIInfo.DefSQConst(engine, ScriptConfigFlags{}.base(), "CONFIG_NONE");
@ -50,8 +50,8 @@ template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq
SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::InGame}.base(), "AICONFIG_INGAME"); SQAIInfo.DefSQConst(engine, ScriptConfigFlags{ScriptConfigFlag::InGame}.base(), "AICONFIG_INGAME");
SQAIInfo.PostRegister(engine); SQAIInfo.PostRegister(engine);
engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx"); engine.AddMethod("RegisterAI", &AIInfo::Constructor, "tx");
engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx"); engine.AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, "tx");
} }
/* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm) /* static */ SQInteger AIInfo::Constructor(HSQUIRRELVM vm)
@ -61,11 +61,12 @@ template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq
if (SQ_FAILED(sq_getinstanceup(vm, 2, &instance, nullptr)) || instance == nullptr) return sq_throwerror(vm, "Pass an instance of a child class of AIInfo to RegisterAI"); if (SQ_FAILED(sq_getinstanceup(vm, 2, &instance, nullptr)) || instance == nullptr) return sq_throwerror(vm, "Pass an instance of a child class of AIInfo to RegisterAI");
AIInfo *info = (AIInfo *)instance; AIInfo *info = (AIInfo *)instance;
SQInteger res = ScriptInfo::Constructor(vm, info); SQInteger res = ScriptInfo::Constructor(vm, *info);
if (res != 0) return res; if (res != 0) return res;
if (info->engine->MethodExists(info->SQ_instance, "MinVersionToLoad")) { if (info->engine->MethodExists(info->SQ_instance, "MinVersionToLoad")) {
if (!info->engine->CallIntegerMethod(info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR; if (!info->engine->CallIntegerMethod(info->SQ_instance, "MinVersionToLoad", &info->min_loadable_version, MAX_GET_OPS)) return SQ_ERROR;
if (info->min_loadable_version < 0) return SQ_ERROR;
} else { } else {
info->min_loadable_version = info->GetVersion(); info->min_loadable_version = info->GetVersion();
} }
@ -101,13 +102,13 @@ template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq
AIInfo *info = (AIInfo *)instance; AIInfo *info = (AIInfo *)instance;
info->api_version = *std::rbegin(AIInfo::ApiVersions); info->api_version = *std::rbegin(AIInfo::ApiVersions);
SQInteger res = ScriptInfo::Constructor(vm, info); SQInteger res = ScriptInfo::Constructor(vm, *info);
if (res != 0) return res; if (res != 0) return res;
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */ /* Remove the link to the real instance, else it might get deleted by RegisterAI() */
sq_setinstanceup(vm, 2, nullptr); sq_setinstanceup(vm, 2, nullptr);
/* Register the AI to the base system */ /* Register the AI to the base system */
static_cast<AIScannerInfo *>(info->GetScanner())->SetDummyAI(info); static_cast<AIScannerInfo *>(info->GetScanner())->SetDummyAI(std::unique_ptr<AIInfo>(info));
return 0; return 0;
} }
@ -124,12 +125,12 @@ bool AIInfo::CanLoadFromVersion(int version) const
} }
/* static */ void AILibrary::RegisterAPI(Squirrel *engine) /* static */ void AILibrary::RegisterAPI(Squirrel &engine)
{ {
/* Create the AILibrary class, and add the RegisterLibrary function */ /* Create the AILibrary class, and add the RegisterLibrary function */
engine->AddClassBegin("AILibrary"); engine.AddClassBegin("AILibrary");
engine->AddClassEnd(); engine.AddClassEnd();
engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx"); engine.AddMethod("RegisterLibrary", &AILibrary::Constructor, "tx");
} }
/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm) /* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
@ -137,7 +138,7 @@ bool AIInfo::CanLoadFromVersion(int version) const
/* Create a new library */ /* Create a new library */
AILibrary *library = new AILibrary(); AILibrary *library = new AILibrary();
SQInteger res = ScriptInfo::Constructor(vm, library); SQInteger res = ScriptInfo::Constructor(vm, *library);
if (res != 0) { if (res != 0) {
delete library; delete library;
return res; return res;

View File

@ -23,7 +23,7 @@ public:
/** /**
* Register the functions of this class. * Register the functions of this class.
*/ */
static void RegisterAPI(Squirrel *engine); static void RegisterAPI(Squirrel &engine);
/** /**
* Create an AI, using this AIInfo as start-template. * Create an AI, using this AIInfo as start-template.
@ -64,7 +64,7 @@ public:
/** /**
* Register the functions of this class. * Register the functions of this class.
*/ */
static void RegisterAPI(Squirrel *engine); static void RegisterAPI(Squirrel &engine);
/** /**
* Create an AI, using this AIInfo as start-template. * Create an AI, using this AIInfo as start-template.

View File

@ -27,7 +27,7 @@
#include "table/strings.h" #include "table/strings.h"
/* Manually include the Text glue. */ /* Manually include the Text glue. */
#include "../script/api/template/template_text.hpp.sq" #include "../script/api/template/template_text.sq.hpp"
/* Convert all AI related classes to Squirrel data. */ /* Convert all AI related classes to Squirrel data. */
#include "../script/api/ai/ai_includes.hpp" #include "../script/api/ai/ai_includes.hpp"
@ -43,7 +43,7 @@ void AIInstance::Initialize(AIInfo *info)
this->api_version = info->GetAPIVersion(); this->api_version = info->GetAPIVersion();
/* Register the AIController (including the "import" command) */ /* Register the AIController (including the "import" command) */
SQAIController_Register(this->engine); SQAIController_Register(*this->engine);
ScriptInstance::Initialize(info->GetMainScript(), info->GetInstanceName(), _current_company); ScriptInstance::Initialize(info->GetMainScript(), info->GetInstanceName(), _current_company);
} }
@ -53,7 +53,7 @@ void AIInstance::RegisterAPI()
ScriptInstance::RegisterAPI(); ScriptInstance::RegisterAPI();
/* Register all classes */ /* Register all classes */
SQAI_RegisterAll(this->engine); SQAI_RegisterAll(*this->engine);
if (!this->LoadCompatibilityScripts(AI_DIR, AIInfo::ApiVersions)) this->Died(); if (!this->LoadCompatibilityScripts(AI_DIR, AIInfo::ApiVersions)) this->Died();
} }

View File

@ -22,11 +22,8 @@
#include "../safeguards.h" #include "../safeguards.h"
AIScannerInfo::AIScannerInfo() : AIScannerInfo::AIScannerInfo() = default;
ScriptScanner(), AIScannerInfo::~AIScannerInfo() = default;
info_dummy(nullptr)
{
}
void AIScannerInfo::Initialize() void AIScannerInfo::Initialize()
{ {
@ -39,22 +36,17 @@ void AIScannerInfo::Initialize()
Script_CreateDummyInfo(this->engine->GetVM(), "AI", "ai"); Script_CreateDummyInfo(this->engine->GetVM(), "AI", "ai");
} }
void AIScannerInfo::SetDummyAI(class AIInfo *info) void AIScannerInfo::SetDummyAI(std::unique_ptr<class AIInfo> &&info)
{ {
this->info_dummy = info; this->info_dummy = std::move(info);
} }
AIScannerInfo::~AIScannerInfo() std::string AIScannerInfo::GetScriptName(ScriptInfo &info)
{ {
delete this->info_dummy; return info.GetName();
} }
std::string AIScannerInfo::GetScriptName(ScriptInfo *info) void AIScannerInfo::RegisterAPI(class Squirrel &engine)
{
return info->GetName();
}
void AIScannerInfo::RegisterAPI(class Squirrel *engine)
{ {
AIInfo::RegisterAPI(engine); AIInfo::RegisterAPI(engine);
} }
@ -63,7 +55,7 @@ AIInfo *AIScannerInfo::SelectRandomAI() const
{ {
if (_game_mode == GM_MENU) { if (_game_mode == GM_MENU) {
Debug(script, 0, "The intro game should not use AI, loading 'dummy' AI."); Debug(script, 0, "The intro game should not use AI, loading 'dummy' AI.");
return this->info_dummy; return this->info_dummy.get();
} }
/* Filter for AIs suitable as Random AI. */ /* Filter for AIs suitable as Random AI. */
@ -72,7 +64,7 @@ AIInfo *AIScannerInfo::SelectRandomAI() const
uint num_random_ais = std::ranges::distance(random_ais); uint num_random_ais = std::ranges::distance(random_ais);
if (num_random_ais == 0) { if (num_random_ais == 0) {
Debug(script, 0, "No suitable AI found, loading 'dummy' AI."); Debug(script, 0, "No suitable AI found, loading 'dummy' AI.");
return this->info_dummy; return this->info_dummy.get();
} }
/* Pick a random AI */ /* Pick a random AI */
@ -125,13 +117,13 @@ void AIScannerLibrary::Initialize()
ScriptScanner::Initialize("AIScanner"); ScriptScanner::Initialize("AIScanner");
} }
std::string AIScannerLibrary::GetScriptName(ScriptInfo *info) std::string AIScannerLibrary::GetScriptName(ScriptInfo &info)
{ {
AILibrary *library = static_cast<AILibrary *>(info); AILibrary &library = static_cast<AILibrary &>(info);
return fmt::format("{}.{}", library->GetCategory(), library->GetInstanceName()); return fmt::format("{}.{}", library.GetCategory(), library.GetInstanceName());
} }
void AIScannerLibrary::RegisterAPI(class Squirrel *engine) void AIScannerLibrary::RegisterAPI(class Squirrel &engine)
{ {
AILibrary::RegisterAPI(engine); AILibrary::RegisterAPI(engine);
} }

View File

@ -37,17 +37,17 @@ public:
/** /**
* Set the Dummy AI. * Set the Dummy AI.
*/ */
void SetDummyAI(class AIInfo *info); void SetDummyAI(std::unique_ptr<class AIInfo> &&info);
protected: protected:
std::string GetScriptName(ScriptInfo *info) override; std::string GetScriptName(ScriptInfo &info) override;
const char *GetFileName() const override { return PATHSEP "info.nut"; } std::string_view GetFileName() const override { return PATHSEP "info.nut"; }
Subdirectory GetDirectory() const override { return AI_DIR; } Subdirectory GetDirectory() const override { return AI_DIR; }
const char *GetScannerName() const override { return "AIs"; } std::string_view GetScannerName() const override { return "AIs"; }
void RegisterAPI(class Squirrel *engine) override; void RegisterAPI(class Squirrel &engine) override;
private: private:
AIInfo *info_dummy; ///< The dummy AI. std::unique_ptr<AIInfo> info_dummy; ///< The dummy AI.
}; };
class AIScannerLibrary : public ScriptScanner { class AIScannerLibrary : public ScriptScanner {
@ -63,11 +63,11 @@ public:
class AILibrary *FindLibrary(const std::string &library, int version); class AILibrary *FindLibrary(const std::string &library, int version);
protected: protected:
std::string GetScriptName(ScriptInfo *info) override; std::string GetScriptName(ScriptInfo &info) override;
const char *GetFileName() const override { return PATHSEP "library.nut"; } std::string_view GetFileName() const override { return PATHSEP "library.nut"; }
Subdirectory GetDirectory() const override { return AI_LIBRARY_DIR; } Subdirectory GetDirectory() const override { return AI_LIBRARY_DIR; }
const char *GetScannerName() const override { return "AI Libraries"; } std::string_view GetScannerName() const override { return "AI Libraries"; }
void RegisterAPI(class Squirrel *engine) override; void RegisterAPI(class Squirrel &engine) override;
}; };
#endif /* AI_SCANNER_HPP */ #endif /* AI_SCANNER_HPP */

View File

@ -175,7 +175,7 @@ void Aircraft::GetImage(Direction direction, EngineImageType image_type, Vehicle
{ {
uint8_t spritenum = this->spritenum; uint8_t spritenum = this->spritenum;
if (is_custom_sprite(spritenum)) { if (IsCustomVehicleSpriteNum(spritenum)) {
GetCustomVehicleSprite(this, direction, image_type, result); GetCustomVehicleSprite(this, direction, image_type, result);
if (result->IsValid()) return; if (result->IsValid()) return;
@ -191,7 +191,7 @@ void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteS
assert(v->subtype == AIR_HELICOPTER); assert(v->subtype == AIR_HELICOPTER);
const Aircraft *w = v->Next()->Next(); const Aircraft *w = v->Next()->Next();
if (is_custom_sprite(v->spritenum)) { if (IsCustomVehicleSpriteNum(v->spritenum)) {
GetCustomRotorSprite(v, image_type, result); GetCustomRotorSprite(v, image_type, result);
if (result->IsValid()) return; if (result->IsValid()) return;
} }
@ -205,7 +205,7 @@ static void GetAircraftIcon(EngineID engine, EngineImageType image_type, Vehicle
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
uint8_t spritenum = e->u.air.image_index; uint8_t spritenum = e->u.air.image_index;
if (is_custom_sprite(spritenum)) { if (IsCustomVehicleSpriteNum(spritenum)) {
GetCustomVehicleIcon(engine, DIR_W, image_type, result); GetCustomVehicleIcon(engine, DIR_W, image_type, result);
if (result->IsValid()) return; if (result->IsValid()) return;
@ -1430,7 +1430,7 @@ static void AircraftLandAirplane(Aircraft *v)
v->UpdateDeltaXY(); v->UpdateDeltaXY();
AirportTileAnimationTrigger(st, vt, AAT_STATION_AIRPLANE_LAND); TriggerAirportTileAnimation(st, vt, AirportAnimationTrigger::AirplaneTouchdown);
if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) { if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
SndPlayVehicleFx(SND_17_SKID_PLANE, v); SndPlayVehicleFx(SND_17_SKID_PLANE, v);

View File

@ -82,7 +82,7 @@ void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, Eng
int x_offs = UnScaleGUI(rect.left); int x_offs = UnScaleGUI(rect.left);
int x = rtl ? r.right - width - x_offs : r.left - x_offs; int x = rtl ? r.right - width - x_offs : r.left - x_offs;
/* This magic -1 offset is related to the sprite_y_offsets in build_vehicle_gui.cpp */ /* This magic -1 offset is related to the sprite_y_offsets in build_vehicle_gui.cpp */
int y = ScaleSpriteTrad(-1) + CenterBounds(r.top, r.bottom, 0); int y = ScaleSpriteTrad(-1) + CentreBounds(r.top, r.bottom, 0);
bool helicopter = v->subtype == AIR_HELICOPTER; bool helicopter = v->subtype == AIR_HELICOPTER;
int heli_offs = 0; int heli_offs = 0;

View File

@ -21,7 +21,7 @@
#include "company_base.h" #include "company_base.h"
#include "station_type.h" #include "station_type.h"
#include "newgrf_airport.h" #include "newgrf_airport.h"
#include "newgrf_badge.h" #include "newgrf_badge_gui.h"
#include "newgrf_callbacks.h" #include "newgrf_callbacks.h"
#include "dropdown_type.h" #include "dropdown_type.h"
#include "dropdown_func.h" #include "dropdown_func.h"
@ -204,9 +204,9 @@ static constexpr NWidgetPart _nested_air_toolbar_widgets[] = {
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN), NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_AIRPORT), SetFill(0, 1), SetMinimalSize(42, 22), SetSpriteTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_AIRPORT), SetFill(0, 1), SetToolbarMinimalSize(2), SetSpriteTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP),
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), SetFill(1, 1), EndContainer(), NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetToolbarSpacerMinimalSize(), SetFill(1, 1), EndContainer(),
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetSpriteTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetToolbarMinimalSize(1), SetSpriteTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
EndContainer(), EndContainer(),
}; };
@ -401,7 +401,7 @@ public:
case WID_AP_AIRPORT_SPRITE: case WID_AP_AIRPORT_SPRITE:
if (this->preview_sprite != 0) { if (this->preview_sprite != 0) {
Dimension d = GetSpriteSize(this->preview_sprite); Dimension d = GetSpriteSize(this->preview_sprite);
DrawSprite(this->preview_sprite, COMPANY_SPRITE_COLOUR(_local_company), CenterBounds(r.left, r.right, d.width), CenterBounds(r.top, r.bottom, d.height)); DrawSprite(this->preview_sprite, GetCompanyPalette(_local_company), CentreBounds(r.left, r.right, d.width), CentreBounds(r.top, r.bottom, d.height));
} }
break; break;
@ -561,7 +561,7 @@ public:
this->SelectOtherAirport(-1); this->SelectOtherAirport(-1);
} }
void OnDropdownSelect(WidgetID widget, int index) override void OnDropdownSelect(WidgetID widget, int index, int) override
{ {
if (widget == WID_AP_CLASS_DROPDOWN) { if (widget == WID_AP_CLASS_DROPDOWN) {
_selected_airport_class = (AirportClassID)index; _selected_airport_class = (AirportClassID)index;
@ -575,7 +575,7 @@ public:
CheckRedrawStationCoverage(this); CheckRedrawStationCoverage(this);
} }
IntervalTimer<TimerGameCalendar> yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) { const IntervalTimer<TimerGameCalendar> yearly_interval = {{TimerGameCalendar::YEAR, TimerGameCalendar::Priority::NONE}, [this](auto) {
this->InvalidateData(); this->InvalidateData();
}}; }};
}; };
@ -618,7 +618,7 @@ static constexpr NWidgetPart _nested_build_airport_widgets[] = {
}; };
static WindowDesc _build_airport_desc( static WindowDesc _build_airport_desc(
WDP_AUTO, nullptr, 0, 0, WDP_AUTO, {}, 0, 0,
WC_BUILD_STATION, WC_BUILD_TOOLBAR, WC_BUILD_STATION, WC_BUILD_TOOLBAR,
WindowDefaultFlag::Construction, WindowDefaultFlag::Construction,
_nested_build_airport_widgets _nested_build_airport_widgets

View File

@ -80,20 +80,18 @@ uint CountArticulatedParts(EngineID engine_type, bool purchase_window)
* either, so it doesn't matter how many articulated parts there are. */ * either, so it doesn't matter how many articulated parts there are. */
if (!Vehicle::CanAllocateItem()) return 0; if (!Vehicle::CanAllocateItem()) return 0;
Vehicle *v = nullptr; std::unique_ptr<Vehicle> v;
if (!purchase_window) { if (!purchase_window) {
v = new Vehicle(); v = std::make_unique<Vehicle>();
v->engine_type = engine_type; v->engine_type = engine_type;
v->owner = _current_company; v->owner = _current_company;
} }
uint i; uint i;
for (i = 1; i < MAX_ARTICULATED_PARTS; i++) { for (i = 1; i < MAX_ARTICULATED_PARTS; i++) {
if (GetNextArticulatedPart(i, engine_type, v) == EngineID::Invalid()) break; if (GetNextArticulatedPart(i, engine_type, v.get()) == EngineID::Invalid()) break;
} }
delete v;
return i - 1; return i - 1;
} }
@ -432,7 +430,10 @@ void AddArticulatedParts(Vehicle *first)
if (flip_image) v->spritenum++; if (flip_image) v->spritenum++;
if (v->type == VEH_TRAIN && TestVehicleBuildProbability(v, v->engine_type, BuildProbabilityType::Reversed)) SetBit(Train::From(v)->flags, VRF_REVERSE_DIRECTION); if (v->type == VEH_TRAIN) {
auto prob = TestVehicleBuildProbability(v, v->engine_type, BuildProbabilityType::Reversed);
if (prob.has_value()) Train::From(v)->flags.Set(VehicleRailFlag::Flipped, prob.value());
}
v->UpdatePosition(); v->UpdatePosition();
} }
} }

View File

@ -17,9 +17,12 @@
#include "safeguards.h" #include "safeguards.h"
/**
* @return true, if the textbuf was updated.
*/
bool AutoCompletion::AutoComplete() bool AutoCompletion::AutoComplete()
{ {
// We are pressing TAB for the first time after reset. /* We are pressing TAB for the first time after reset. */
if (this->suggestions.empty()) { if (this->suggestions.empty()) {
this->InitSuggestions(this->textbuf->GetText()); this->InitSuggestions(this->textbuf->GetText());
if (this->suggestions.empty()) { if (this->suggestions.empty()) {
@ -29,11 +32,11 @@ bool AutoCompletion::AutoComplete()
return true; return true;
} }
// We are pressing TAB again on the same text. /* We are pressing TAB again on the same text. */
if (this->current_suggestion_index + 1 < this->suggestions.size()) { if (this->current_suggestion_index + 1 < this->suggestions.size()) {
this->ApplySuggestion(prefix, this->suggestions[++this->current_suggestion_index]); this->ApplySuggestion(prefix, this->suggestions[++this->current_suggestion_index]);
} else { } else {
// We are out of options, restore original text. /* We are out of options, restore original text. */
this->textbuf->Assign(initial_buf); this->textbuf->Assign(initial_buf);
this->Reset(); this->Reset();
} }

View File

@ -32,7 +32,6 @@ public:
} }
virtual ~AutoCompletion() = default; virtual ~AutoCompletion() = default;
// Returns true the textbuf was updated.
bool AutoComplete(); bool AutoComplete();
void Reset(); void Reset();

View File

@ -185,9 +185,9 @@ static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_ty
CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false); CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v; const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
for (const Order *o : u->Orders()) { for (const Order &o : u->Orders()) {
if (!o->IsRefit() || o->IsAutoRefit()) continue; if (!o.IsRefit() || o.IsAutoRefit()) continue;
CargoType cargo_type = o->GetRefitCargo(); CargoType cargo_type = o.GetRefitCargo();
if (!HasBit(union_refit_mask_a, cargo_type)) continue; if (!HasBit(union_refit_mask_a, cargo_type)) continue;
if (!HasBit(union_refit_mask_b, cargo_type)) return false; if (!HasBit(union_refit_mask_b, cargo_type)) return false;
@ -206,13 +206,12 @@ static int GetIncompatibleRefitOrderIdForAutoreplace(const Vehicle *v, EngineID
{ {
CargoTypes union_refit_mask = GetUnionOfArticulatedRefitMasks(engine_type, false); CargoTypes union_refit_mask = GetUnionOfArticulatedRefitMasks(engine_type, false);
const Order *o;
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v; const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
const OrderList *orders = u->orders; const OrderList *orders = u->orders;
if (orders == nullptr) return -1; if (orders == nullptr) return -1;
for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) { for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
o = orders->GetOrderAt(i); const Order *o = orders->GetOrderAt(i);
if (!o->IsRefit()) continue; if (!o->IsRefit()) continue;
if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i; if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i;
} }
@ -373,9 +372,13 @@ static CommandCost BuildReplacementVehicle(Vehicle *old_veh, Vehicle **new_vehic
} }
/* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */ /* Try to reverse the vehicle, but do not care if it fails as the new type might not be reversible */
if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) { if (new_veh->type == VEH_TRAIN && Train::From(old_veh)->flags.Test(VehicleRailFlag::Flipped)) {
/* Only copy the reverse state if neither old or new vehicle implements reverse-on-build probability callback. */
if (!TestVehicleBuildProbability(old_veh, old_veh->engine_type, BuildProbabilityType::Reversed).has_value() &&
!TestVehicleBuildProbability(new_veh, new_veh->engine_type, BuildProbabilityType::Reversed).has_value()) {
Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(DoCommandFlag::Execute, new_veh->index, true); Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(DoCommandFlag::Execute, new_veh->index, true);
} }
}
return cost; return cost;
} }

View File

@ -311,7 +311,7 @@ public:
case WID_RV_LEFT_MATRIX: case WID_RV_LEFT_MATRIX:
case WID_RV_RIGHT_MATRIX: case WID_RV_RIGHT_MATRIX:
resize.height = GetEngineListHeight(this->window_number); fill.height = resize.height = GetEngineListHeight(this->window_number);
size.height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize.height; size.height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize.height;
break; break;
@ -406,11 +406,11 @@ public:
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: case WID_RV_TRAIN_WAGONREMOVE_TOGGLE:
if (const Group *g = Group::GetIfValid(this->sel_group); g != nullptr) { if (const Group *g = Group::GetIfValid(this->sel_group); g != nullptr) {
bool remove_wagon = g->flags.Test(GroupFlag::ReplaceWagonRemoval); bool remove_wagon = g->flags.Test(GroupFlag::ReplaceWagonRemoval);
return GetString(STR_GROUP_NAME, sel_group, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); return GetString(STR_REPLACE_REMOVE_WAGON, STR_GROUP_NAME, sel_group, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
} else { } else {
const Company *c = Company::Get(_local_company); const Company *c = Company::Get(_local_company);
bool remove_wagon = c->settings.renew_keep_length; bool remove_wagon = c->settings.renew_keep_length;
return GetString(STR_GROUP_DEFAULT_TRAINS + this->window_number, std::monostate{}, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); return GetString(STR_REPLACE_REMOVE_WAGON, STR_GROUP_DEFAULT_TRAINS + this->window_number, std::monostate{}, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
} }
break; break;
@ -552,7 +552,7 @@ public:
if (g != nullptr) { if (g != nullptr) {
Command<CMD_SET_GROUP_FLAG>::Post(this->sel_group, GroupFlag::ReplaceWagonRemoval, !g->flags.Test(GroupFlag::ReplaceWagonRemoval), _ctrl_pressed); Command<CMD_SET_GROUP_FLAG>::Post(this->sel_group, GroupFlag::ReplaceWagonRemoval, !g->flags.Test(GroupFlag::ReplaceWagonRemoval), _ctrl_pressed);
} else { } else {
// toggle renew_keep_length /* toggle renew_keep_length */
Command<CMD_CHANGE_COMPANY_SETTING>::Post("company.renew_keep_length", Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1); Command<CMD_CHANGE_COMPANY_SETTING>::Post("company.renew_keep_length", Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1);
} }
break; break;
@ -623,7 +623,7 @@ public:
} }
} }
void OnDropdownSelect(WidgetID widget, int index) override void OnDropdownSelect(WidgetID widget, int index, int) override
{ {
switch (widget) { switch (widget) {
case WID_RV_SORT_DROPDOWN: case WID_RV_SORT_DROPDOWN:
@ -744,7 +744,7 @@ static constexpr NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
NWidget(NWID_VERTICAL), NWidget(NWID_VERTICAL),
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetStringTip(STR_REPLACE_REMOVE_WAGON, STR_REPLACE_REMOVE_WAGON_TOOLTIP), SetFill(1, 0), SetResize(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetToolTip(STR_REPLACE_REMOVE_WAGON_TOOLTIP), SetFill(1, 0), SetResize(1, 0),
EndContainer(), EndContainer(),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),

View File

@ -16,8 +16,9 @@
#include "3rdparty/md5/md5.h" #include "3rdparty/md5/md5.h"
#include <unordered_map> #include <unordered_map>
/* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */
struct IniFile; struct IniFile;
struct IniGroup;
struct IniItem;
struct ContentInfo; struct ContentInfo;
/** Structure holding filename and MD5 information about a single file */ /** Structure holding filename and MD5 information about a single file */
@ -47,7 +48,7 @@ template <class T> struct BaseSetTraits;
*/ */
template <class T> template <class T>
struct BaseSet { struct BaseSet {
typedef std::unordered_map<std::string, std::string> TranslatedStrings; typedef std::unordered_map<std::string, std::string, StringHash, std::equal_to<>> TranslatedStrings;
/** Number of files in this set */ /** Number of files in this set */
static constexpr size_t NUM_FILES = BaseSetTraits<T>::num_files; static constexpr size_t NUM_FILES = BaseSetTraits<T>::num_files;
@ -62,21 +63,13 @@ struct BaseSet {
std::string url; ///< URL for information about the base set std::string url; ///< URL for information about the base set
TranslatedStrings description; ///< Description of the base set TranslatedStrings description; ///< Description of the base set
uint32_t shortname = 0; ///< Four letter short variant of the name uint32_t shortname = 0; ///< Four letter short variant of the name
uint32_t version = 0; ///< The version of this base set std::vector<uint32_t> version; ///< The version of this base set
bool fallback = false; ///< This set is a fallback set, i.e. it should be used only as last resort bool fallback = false; ///< This set is a fallback set, i.e. it should be used only as last resort
std::array<MD5File, BaseSet<T>::NUM_FILES> files{}; ///< All files part of this set std::array<MD5File, BaseSet<T>::NUM_FILES> files{}; ///< All files part of this set
uint found_files = 0; ///< Number of the files that could be found uint found_files = 0; ///< Number of the files that could be found
uint valid_files = 0; ///< Number of the files that could be found and are valid uint valid_files = 0; ///< Number of the files that could be found and are valid
T *next = nullptr; ///< The next base set in this list
/** Free everything we allocated */
~BaseSet()
{
delete this->next;
}
/** /**
* Get the number of missing files. * Get the number of missing files.
* @return the number * @return the number
@ -96,6 +89,9 @@ struct BaseSet {
return BaseSet<T>::NUM_FILES - this->valid_files; return BaseSet<T>::NUM_FILES - this->valid_files;
} }
void LogError(std::string_view full_filename, std::string_view detail, int level = 0) const;
const IniItem *GetMandatoryItem(std::string_view full_filename, const IniGroup &group, std::string_view name) const;
bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename, bool allow_empty_filename = true); bool FillSetDetails(const IniFile &ini, const std::string &path, const std::string &full_filename, bool allow_empty_filename = true);
void CopyCompatibleConfig([[maybe_unused]] const T &src) {} void CopyCompatibleConfig([[maybe_unused]] const T &src) {}
@ -107,7 +103,7 @@ struct BaseSet {
* @param isocode the isocode to search for * @param isocode the isocode to search for
* @return the description * @return the description
*/ */
const std::string &GetDescription(const std::string &isocode) const const std::string &GetDescription(std::string_view isocode) const
{ {
if (!isocode.empty()) { if (!isocode.empty()) {
/* First the full ISO code */ /* First the full ISO code */
@ -166,9 +162,9 @@ struct BaseSet {
template <class Tbase_set> template <class Tbase_set>
class BaseMedia : FileScanner { class BaseMedia : FileScanner {
protected: protected:
static inline Tbase_set *available_sets = nullptr; ///< All available sets static inline std::vector<std::unique_ptr<Tbase_set>> available_sets; ///< All available sets
static inline Tbase_set *duplicate_sets = nullptr; ///< All sets that aren't available, but needed for not downloading base sets when a newer version than the one on BaNaNaS is loaded. static inline std::vector<std::unique_ptr<Tbase_set>> duplicate_sets; ///< All sets that aren't available, but needed for not downloading base sets when a newer version than the one on BaNaNaS is loaded.
static inline const Tbase_set *used_set = nullptr; ///< The currently used set static inline const Tbase_set *used_set; ///< The currently used set
bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override; bool AddFile(const std::string &filename, size_t basepath_length, const std::string &tar_filename) override;
@ -176,7 +172,13 @@ protected:
* Get the extension that is used to identify this set. * Get the extension that is used to identify this set.
* @return the extension * @return the extension
*/ */
static const char *GetExtension(); static std::string_view GetExtension();
/**
* Return the duplicate sets.
* @return The duplicate sets.
*/
static std::span<const std::unique_ptr<Tbase_set>> GetDuplicateSets() { return BaseMedia<Tbase_set>::duplicate_sets; }
public: public:
/** /**
* Determine the graphics pack that has to be used. * Determine the graphics pack that has to be used.
@ -194,7 +196,11 @@ public:
return num + fs.Scan(GetExtension(), BASESET_DIR, Tbase_set::SEARCH_IN_TARS); return num + fs.Scan(GetExtension(), BASESET_DIR, Tbase_set::SEARCH_IN_TARS);
} }
static Tbase_set *GetAvailableSets(); /**
* Return the available sets.
* @return The available sets.
*/
static std::span<const std::unique_ptr<Tbase_set>> GetAvailableSets() { return BaseMedia<Tbase_set>::available_sets; }
static bool SetSet(const Tbase_set *set); static bool SetSet(const Tbase_set *set);
static bool SetSetByName(const std::string &name); static bool SetSetByName(const std::string &name);
@ -211,7 +217,7 @@ public:
* @param md5sum whether to check the MD5 checksum * @param md5sum whether to check the MD5 checksum
* @return true iff we have an set matching. * @return true iff we have an set matching.
*/ */
static bool HasSet(const ContentInfo *ci, bool md5sum); static bool HasSet(const ContentInfo &ci, bool md5sum);
}; };
/** /**
@ -219,9 +225,9 @@ public:
* @param ci The content info to compare it to. * @param ci The content info to compare it to.
* @param md5sum Should the MD5 checksum be tested as well? * @param md5sum Should the MD5 checksum be tested as well?
* @param s The list with sets. * @param s The list with sets.
* @return The filename of the first file of the base set, or \c nullptr if there is no match. * @return The filename of the first file of the base set, or \c std::nullopt if there is no match.
*/ */
template <class Tbase_set> template <class Tbase_set>
const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s); std::optional<std::string_view> TryGetBaseSetFile(const ContentInfo &ci, bool md5sum, std::span<const std::unique_ptr<Tbase_set>> sets);
#endif /* BASE_MEDIA_BASE_H */ #endif /* BASE_MEDIA_BASE_H */

View File

@ -14,19 +14,38 @@
#include "ini_type.h" #include "ini_type.h"
#include "string_func.h" #include "string_func.h"
#include "error_func.h" #include "error_func.h"
#include "core/string_consumer.hpp"
#include "3rdparty/fmt/ranges.h"
extern void CheckExternalFiles(); extern void CheckExternalFiles();
/** /**
* Try to read a single piece of metadata and return false if it doesn't exist. * Log error from reading basesets.
* @param full_filename the full filename of the loaded file
* @param detail detail log message
* @param level debug level
*/
template <class T>
void BaseSet<T>::LogError(std::string_view full_filename, std::string_view detail, int level) const
{
Debug(misc, level, "Loading base {}set details failed: {}", BaseSet<T>::SET_TYPE, full_filename);
Debug(misc, level, " {}", detail);
}
/**
* Try to read a single piece of metadata and return nullptr if it doesn't exist.
* Log error, if the data is missing.
* @param full_filename the full filename of the loaded file (for error reporting purposes)
* @param group ini group to read from
* @param name the name of the item to fetch. * @param name the name of the item to fetch.
*/ */
#define fetch_metadata(name) \ template <class T>
item = metadata->GetItem(name); \ const IniItem *BaseSet<T>::GetMandatoryItem(std::string_view full_filename, const IniGroup &group, std::string_view name) const
if (item == nullptr || !item->value.has_value() || item->value->empty()) { \ {
Debug(grf, 0, "Base {}set detail loading: {} field missing.", BaseSet::SET_TYPE, name); \ auto *item = group.GetItem(name);
Debug(grf, 0, " Is {} readable for the user running OpenTTD?", full_filename); \ if (item != nullptr && item->value.has_value() && !item->value->empty()) return item;
return false; \ this->LogError(full_filename, fmt::format("{}.{} field missing.", group.name, name));
return nullptr;
} }
/** /**
@ -42,16 +61,17 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
{ {
const IniGroup *metadata = ini.GetGroup("metadata"); const IniGroup *metadata = ini.GetGroup("metadata");
if (metadata == nullptr) { if (metadata == nullptr) {
Debug(grf, 0, "Base {}set detail loading: metadata missing.", BaseSet<T>::SET_TYPE); this->LogError(full_filename, "Is the file readable for the user running OpenTTD?");
Debug(grf, 0, " Is {} readable for the user running OpenTTD?", full_filename);
return false; return false;
} }
const IniItem *item; const IniItem *item;
fetch_metadata("name"); item = this->GetMandatoryItem(full_filename, *metadata, "name");
if (item == nullptr) return false;
this->name = *item->value; this->name = *item->value;
fetch_metadata("description"); item = this->GetMandatoryItem(full_filename, *metadata, "description");
if (item == nullptr) return false;
this->description[std::string{}] = *item->value; this->description[std::string{}] = *item->value;
item = metadata->GetItem("url"); item = metadata->GetItem("url");
@ -64,13 +84,24 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
this->description[titem.name.substr(12)] = titem.value.value_or(""); this->description[titem.name.substr(12)] = titem.value.value_or("");
} }
fetch_metadata("shortname"); item = this->GetMandatoryItem(full_filename, *metadata, "shortname");
if (item == nullptr) return false;
for (uint i = 0; (*item->value)[i] != '\0' && i < 4; i++) { for (uint i = 0; (*item->value)[i] != '\0' && i < 4; i++) {
this->shortname |= ((uint8_t)(*item->value)[i]) << (i * 8); this->shortname |= ((uint8_t)(*item->value)[i]) << (i * 8);
} }
fetch_metadata("version"); item = this->GetMandatoryItem(full_filename, *metadata, "version");
this->version = atoi(item->value->c_str()); if (item == nullptr) return false;
for (StringConsumer consumer{*item->value};;) {
auto value = consumer.TryReadIntegerBase<uint32_t>(10);
bool valid = value.has_value();
if (valid) this->version.push_back(*value);
if (valid && !consumer.AnyBytesLeft()) break;
if (!valid || !consumer.ReadIf(".")) {
this->LogError(full_filename, fmt::format("metadata.version field is invalid: {}", *item->value));
return false;
}
}
item = metadata->GetItem("fallback"); item = metadata->GetItem("fallback");
this->fallback = (item != nullptr && item->value && *item->value != "0" && *item->value != "false"); this->fallback = (item != nullptr && item->value && *item->value != "0" && *item->value != "false");
@ -80,13 +111,18 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
const IniGroup *md5s = ini.GetGroup("md5s"); const IniGroup *md5s = ini.GetGroup("md5s");
const IniGroup *origin = ini.GetGroup("origin"); const IniGroup *origin = ini.GetGroup("origin");
auto file_names = BaseSet<T>::GetFilenames(); auto file_names = BaseSet<T>::GetFilenames();
bool original_set =
std::byteswap(this->shortname) == 'TTDD' || // TTD DOS graphics, TTD DOS music
std::byteswap(this->shortname) == 'TTDW' || // TTD WIN graphics, TTD WIN music
std::byteswap(this->shortname) == 'TTDO' || // TTD sound
std::byteswap(this->shortname) == 'TTOD'; // TTO music
for (uint i = 0; i < BaseSet<T>::NUM_FILES; i++) { for (uint i = 0; i < BaseSet<T>::NUM_FILES; i++) {
MD5File *file = &this->files[i]; MD5File *file = &this->files[i];
/* Find the filename first. */ /* Find the filename first. */
item = files != nullptr ? files->GetItem(file_names[i]) : nullptr; item = files != nullptr ? files->GetItem(file_names[i]) : nullptr;
if (item == nullptr || (!item->value.has_value() && !allow_empty_filename)) { if (item == nullptr || (!item->value.has_value() && !allow_empty_filename)) {
Debug(grf, 0, "No {} file for: {} (in {})", BaseSet<T>::SET_TYPE, file_names[i], full_filename); this->LogError(full_filename, fmt::format("files.{} field missing", file_names[i]));
return false; return false;
} }
@ -104,34 +140,19 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
/* Then find the MD5 checksum */ /* Then find the MD5 checksum */
item = md5s != nullptr ? md5s->GetItem(filename) : nullptr; item = md5s != nullptr ? md5s->GetItem(filename) : nullptr;
if (item == nullptr || !item->value.has_value()) { if (item == nullptr || !item->value.has_value()) {
Debug(grf, 0, "No MD5 checksum specified for: {} (in {})", filename, full_filename); this->LogError(full_filename, fmt::format("md5s.{} field missing", filename));
return false; return false;
} }
const char *c = item->value->c_str(); if (!ConvertHexToBytes(*item->value, file->hash)) {
for (size_t i = 0; i < file->hash.size() * 2; i++, c++) { this->LogError(full_filename, fmt::format("md5s.{} is malformed: {}", filename, *item->value));
uint j;
if ('0' <= *c && *c <= '9') {
j = *c - '0';
} else if ('a' <= *c && *c <= 'f') {
j = *c - 'a' + 10;
} else if ('A' <= *c && *c <= 'F') {
j = *c - 'A' + 10;
} else {
Debug(grf, 0, "Malformed MD5 checksum specified for: {} (in {})", filename, full_filename);
return false; return false;
} }
if (i % 2 == 0) {
file->hash[i / 2] = j << 4;
} else {
file->hash[i / 2] |= j;
}
}
/* Then find the warning message when the file's missing */ /* Then find the warning message when the file's missing */
item = origin != nullptr ? origin->GetItem(filename) : nullptr; item = origin != nullptr ? origin->GetItem(filename) : nullptr;
if (item == nullptr) item = origin != nullptr ? origin->GetItem("default") : nullptr; if (item == nullptr) item = origin != nullptr ? origin->GetItem("default") : nullptr;
if (item == nullptr || !item->value.has_value()) { if (item == nullptr || !item->value.has_value()) {
Debug(grf, 1, "No origin warning message specified for: {}", filename); this->LogError(full_filename, fmt::format("origin.{} field missing", filename), 1);
file->missing_warning.clear(); file->missing_warning.clear();
} else { } else {
file->missing_warning = item->value.value(); file->missing_warning = item->value.value();
@ -148,12 +169,14 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
break; break;
case MD5File::CR_MISMATCH: case MD5File::CR_MISMATCH:
Debug(grf, 1, "MD5 checksum mismatch for: {} (in {})", filename, full_filename); /* This is normal for original sample.cat, which either matches with orig_dos or orig_win. */
this->LogError(full_filename, fmt::format("MD5 checksum mismatch for: {}", filename), original_set ? 1 : 0);
this->found_files++; this->found_files++;
break; break;
case MD5File::CR_NO_FILE: case MD5File::CR_NO_FILE:
Debug(grf, 1, "The file {} specified in {} is missing", filename, full_filename); /* Missing files is normal for the original basesets. Use lower debug level */
this->LogError(full_filename, fmt::format("File is missing: {}", filename), original_set ? 1 : 0);
break; break;
} }
} }
@ -164,10 +187,9 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
template <class Tbase_set> template <class Tbase_set>
bool BaseMedia<Tbase_set>::AddFile(const std::string &filename, size_t basepath_length, const std::string &) bool BaseMedia<Tbase_set>::AddFile(const std::string &filename, size_t basepath_length, const std::string &)
{ {
bool ret = false; Debug(misc, 1, "Checking {} for base {} set", filename, BaseSet<Tbase_set>::SET_TYPE);
Debug(grf, 1, "Checking {} for base {} set", filename, BaseSet<Tbase_set>::SET_TYPE);
Tbase_set *set = new Tbase_set(); auto set = std::make_unique<Tbase_set>();
IniFile ini{}; IniFile ini{};
std::string path{ filename, basepath_length }; std::string path{ filename, basepath_length };
ini.LoadFromDisk(path, BASESET_DIR); ini.LoadFromDisk(path, BASESET_DIR);
@ -179,60 +201,44 @@ bool BaseMedia<Tbase_set>::AddFile(const std::string &filename, size_t basepath_
path.clear(); path.clear();
} }
if (set->FillSetDetails(ini, path, filename)) { if (!set->FillSetDetails(ini, path, filename)) return false;
Tbase_set *duplicate = nullptr;
for (Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) { auto existing = std::ranges::find_if(BaseMedia<Tbase_set>::available_sets, [&set](const auto &c) { return c->name == set->name || c->shortname == set->shortname; });
if (c->name == set->name || c->shortname == set->shortname) { if (existing != std::end(BaseMedia<Tbase_set>::available_sets)) {
duplicate = c;
break;
}
}
if (duplicate != nullptr) {
/* The more complete set takes precedence over the version number. */ /* The more complete set takes precedence over the version number. */
if ((duplicate->valid_files == set->valid_files && duplicate->version >= set->version) || if (((*existing)->valid_files == set->valid_files && (*existing)->version >= set->version) ||
duplicate->valid_files > set->valid_files) { (*existing)->valid_files > set->valid_files) {
Debug(grf, 1, "Not adding {} ({}) as base {} set (duplicate, {})", set->name, set->version,
Debug(misc, 1, "Not adding {} ({}) as base {} set (duplicate, {})", set->name, fmt::join(set->version, "."),
BaseSet<Tbase_set>::SET_TYPE, BaseSet<Tbase_set>::SET_TYPE,
duplicate->valid_files > set->valid_files ? "less valid files" : "lower version"); (*existing)->valid_files > set->valid_files ? "fewer valid files" : "lower version");
set->next = BaseMedia<Tbase_set>::duplicate_sets;
BaseMedia<Tbase_set>::duplicate_sets = set;
} else {
Tbase_set **prev = &BaseMedia<Tbase_set>::available_sets;
while (*prev != duplicate) prev = &(*prev)->next;
*prev = set; duplicate_sets.push_back(std::move(set));
set->next = duplicate->next; return false;
}
/* Keep baseset configuration, if compatible */
set->CopyCompatibleConfig(*duplicate);
/* If the duplicate set is currently used (due to rescanning this can happen) /* If the duplicate set is currently used (due to rescanning this can happen)
* update the currently used set to the new one. This will 'lie' about the * update the currently used set to the new one. This will 'lie' about the
* version number until a new game is started which isn't a big problem */ * version number until a new game is started which isn't a big problem */
if (BaseMedia<Tbase_set>::used_set == duplicate) BaseMedia<Tbase_set>::used_set = set; if (BaseMedia<Tbase_set>::used_set == existing->get()) BaseMedia<Tbase_set>::used_set = set.get();
Debug(grf, 1, "Removing {} ({}) as base {} set (duplicate, {})", duplicate->name, duplicate->version, /* Keep baseset configuration, if compatible */
BaseSet<Tbase_set>::SET_TYPE, set->CopyCompatibleConfig(**existing);
duplicate->valid_files < set->valid_files ? "less valid files" : "lower version");
duplicate->next = BaseMedia<Tbase_set>::duplicate_sets; Debug(misc, 1, "Removing {} ({}) as base {} set (duplicate, {})", (*existing)->name, fmt::join((*existing)->version, "."), BaseSet<Tbase_set>::SET_TYPE,
BaseMedia<Tbase_set>::duplicate_sets = duplicate; (*existing)->valid_files < set->valid_files ? "fewer valid files" : "lower version");
ret = true;
} /* Existing set is worse, move it to duplicates and replace with the current set. */
duplicate_sets.push_back(std::move(*existing));
Debug(misc, 1, "Adding {} ({}) as base {} set", set->name, fmt::join(set->version, "."), BaseSet<Tbase_set>::SET_TYPE);
*existing = std::move(set);
} else { } else {
Tbase_set **last = &BaseMedia<Tbase_set>::available_sets;
while (*last != nullptr) last = &(*last)->next;
*last = set;
ret = true;
}
if (ret) {
Debug(grf, 1, "Adding {} ({}) as base {} set", set->name, set->version, BaseSet<Tbase_set>::SET_TYPE); Debug(grf, 1, "Adding {} ({}) as base {} set", set->name, set->version, BaseSet<Tbase_set>::SET_TYPE);
} available_sets.push_back(std::move(set));
} else {
delete set;
} }
return ret; return true;
} }
/** /**
@ -264,9 +270,9 @@ template <class Tbase_set>
return SetSet(nullptr); return SetSet(nullptr);
} }
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) { for (const auto &s : BaseMedia<Tbase_set>::available_sets) {
if (name == s->name) { if (name == s->name) {
return SetSet(s); return SetSet(s.get());
} }
} }
return false; return false;
@ -284,9 +290,9 @@ template <class Tbase_set>
return SetSet(nullptr); return SetSet(nullptr);
} }
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) { for (const auto &s : BaseMedia<Tbase_set>::available_sets) {
if (shortname == s->shortname) { if (shortname == s->shortname) {
return SetSet(s); return SetSet(s.get());
} }
} }
return false; return false;
@ -300,7 +306,7 @@ template <class Tbase_set>
/* static */ void BaseMedia<Tbase_set>::GetSetsList(std::back_insert_iterator<std::string> &output_iterator) /* static */ void BaseMedia<Tbase_set>::GetSetsList(std::back_insert_iterator<std::string> &output_iterator)
{ {
fmt::format_to(output_iterator, "List of {} sets:\n", BaseSet<Tbase_set>::SET_TYPE); fmt::format_to(output_iterator, "List of {} sets:\n", BaseSet<Tbase_set>::SET_TYPE);
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) { for (const auto &s : BaseMedia<Tbase_set>::available_sets) {
fmt::format_to(output_iterator, "{:>18}: {}", s->name, s->GetDescription({})); fmt::format_to(output_iterator, "{:>18}: {}", s->name, s->GetDescription({}));
int invalid = s->GetNumInvalid(); int invalid = s->GetNumInvalid();
if (invalid != 0) { if (invalid != 0) {
@ -319,28 +325,28 @@ template <class Tbase_set>
#include "network/core/tcp_content_type.h" #include "network/core/tcp_content_type.h"
template <class Tbase_set> const char *TryGetBaseSetFile(const ContentInfo *ci, bool md5sum, const Tbase_set *s) template <class Tbase_set> std::optional<std::string_view> TryGetBaseSetFile(const ContentInfo &ci, bool md5sum, std::span<const std::unique_ptr<Tbase_set>> sets)
{ {
for (; s != nullptr; s = s->next) { for (const auto &s : sets) {
if (s->GetNumMissing() != 0) continue; if (s->GetNumMissing() != 0) continue;
if (s->shortname != ci->unique_id) continue; if (s->shortname != ci.unique_id) continue;
if (!md5sum) return s->files[0].filename.c_str(); if (!md5sum) return s->files[0].filename;
MD5Hash md5; MD5Hash md5;
for (const auto &file : s->files) { for (const auto &file : s->files) {
md5 ^= file.hash; md5 ^= file.hash;
} }
if (md5 == ci->md5sum) return s->files[0].filename.c_str(); if (md5 == ci.md5sum) return s->files[0].filename;
} }
return nullptr; return std::nullopt;
} }
template <class Tbase_set> template <class Tbase_set>
/* static */ bool BaseMedia<Tbase_set>::HasSet(const ContentInfo *ci, bool md5sum) /* static */ bool BaseMedia<Tbase_set>::HasSet(const ContentInfo &ci, bool md5sum)
{ {
return (TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::available_sets) != nullptr) || return TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::GetAvailableSets()).has_value() ||
(TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::duplicate_sets) != nullptr); TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::GetDuplicateSets()).has_value();
} }
/** /**
@ -350,12 +356,9 @@ template <class Tbase_set>
template <class Tbase_set> template <class Tbase_set>
/* static */ int BaseMedia<Tbase_set>::GetNumSets() /* static */ int BaseMedia<Tbase_set>::GetNumSets()
{ {
int n = 0; return std::ranges::count_if(BaseMedia<Tbase_set>::GetAvailableSets(), [](const auto &set) {
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) { return set.get() == BaseMedia<Tbase_set>::used_set || set->GetNumMissing() == 0;
if (s != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue; });
n++;
}
return n;
} }
/** /**
@ -366,8 +369,8 @@ template <class Tbase_set>
/* static */ int BaseMedia<Tbase_set>::GetIndexOfUsedSet() /* static */ int BaseMedia<Tbase_set>::GetIndexOfUsedSet()
{ {
int n = 0; int n = 0;
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) { for (const auto &s : BaseMedia<Tbase_set>::available_sets) {
if (s == BaseMedia<Tbase_set>::used_set) return n; if (s.get() == BaseMedia<Tbase_set>::used_set) return n;
if (s->GetNumMissing() != 0) continue; if (s->GetNumMissing() != 0) continue;
n++; n++;
} }
@ -381,9 +384,9 @@ template <class Tbase_set>
template <class Tbase_set> template <class Tbase_set>
/* static */ const Tbase_set *BaseMedia<Tbase_set>::GetSet(int index) /* static */ const Tbase_set *BaseMedia<Tbase_set>::GetSet(int index)
{ {
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) { for (const auto &s : BaseMedia<Tbase_set>::available_sets) {
if (s != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue; if (s.get() != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue;
if (index == 0) return s; if (index == 0) return s.get();
index--; index--;
} }
FatalError("Base{}::GetSet(): index {} out of range", BaseSet<Tbase_set>::SET_TYPE, index); FatalError("Base{}::GetSet(): index {} out of range", BaseSet<Tbase_set>::SET_TYPE, index);
@ -398,13 +401,3 @@ template <class Tbase_set>
{ {
return BaseMedia<Tbase_set>::used_set; return BaseMedia<Tbase_set>::used_set;
} }
/**
* Return the available sets.
* @return The available sets.
*/
template <class Tbase_set>
/* static */ Tbase_set *BaseMedia<Tbase_set>::GetAvailableSets()
{
return BaseMedia<Tbase_set>::available_sets;
}

View File

@ -74,9 +74,9 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
TimerGameCalendar::Date build_date{}; ///< Date of construction TimerGameCalendar::Date build_date{}; ///< Date of construction
uint16_t random_bits = 0; ///< Random bits assigned to this station uint16_t random_bits = 0; ///< Random bits assigned to this station
uint8_t waiting_triggers = 0; ///< Waiting triggers (NewGRF) for this station StationRandomTriggers waiting_random_triggers; ///< Waiting triggers (NewGRF), shared by all station parts/tiles, road stops, ... essentially useless and broken by design.
uint8_t cached_anim_triggers = 0; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen. StationAnimationTriggers cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, used to determine if trigger processing should happen.
uint8_t cached_roadstop_anim_triggers = 0; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen. StationAnimationTriggers cached_roadstop_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask for road stops, used to determine if trigger processing should happen.
CargoTypes cached_cargo_triggers{}; ///< NOSAVE: Combined cargo trigger bitmask CargoTypes cached_cargo_triggers{}; ///< NOSAVE: Combined cargo trigger bitmask
CargoTypes cached_roadstop_cargo_triggers{}; ///< NOSAVE: Combined cargo trigger bitmask for road stops CargoTypes cached_roadstop_cargo_triggers{}; ///< NOSAVE: Combined cargo trigger bitmask for road stops

View File

@ -25,8 +25,8 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
{ {
const SpriteData *src = (const SpriteData *)bp->sprite; const SpriteData *src = (const SpriteData *)bp->sprite;
const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]); const Colour *src_px = reinterpret_cast<const Colour *>(src->data + src->offset[0][zoom]);
const uint16_t *src_n = (const uint16_t *)(src->data + src->offset[zoom][1]); const uint16_t *src_n = reinterpret_cast<const uint16_t *>(src->data + src->offset[1][zoom]);
for (uint i = bp->skip_top; i != 0; i--) { for (uint i = bp->skip_top; i != 0; i--) {
src_px = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px); src_px = (const Colour *)((const uint8_t *)src_px + *(const uint32_t *)src_px);
@ -389,11 +389,11 @@ void Blitter_32bppAnim::CopyFromBuffer(void *video, const void *src, int width,
Colour *dst_pal = dst; Colour *dst_pal = dst;
uint16_t *anim_pal = anim_line; uint16_t *anim_pal = anim_line;
memcpy(static_cast<void *>(dst), usrc, width * sizeof(uint32_t)); std::copy_n(usrc, width, reinterpret_cast<uint32_t *>(dst));
usrc += width; usrc += width;
dst += _screen.pitch; dst += _screen.pitch;
/* Copy back the anim-buffer */ /* Copy back the anim-buffer */
memcpy(anim_line, usrc, width * sizeof(uint16_t)); std::copy_n(reinterpret_cast<const uint16_t *>(usrc), width, anim_line);
usrc = (const uint32_t *)&((const uint16_t *)usrc)[width]; usrc = (const uint32_t *)&((const uint16_t *)usrc)[width];
anim_line += this->anim_buf_pitch; anim_line += this->anim_buf_pitch;
@ -428,11 +428,11 @@ void Blitter_32bppAnim::CopyToBuffer(const void *video, void *dst, int width, in
const uint16_t *anim_line = this->ScreenToAnimOffset((const uint32_t *)video) + this->anim_buf; const uint16_t *anim_line = this->ScreenToAnimOffset((const uint32_t *)video) + this->anim_buf;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(udst, src, width * sizeof(uint32_t)); std::copy_n(src, width, udst);
src += _screen.pitch; src += _screen.pitch;
udst += width; udst += width;
/* Copy the anim-buffer */ /* Copy the anim-buffer */
memcpy(udst, anim_line, width * sizeof(uint16_t)); std::copy_n(anim_line, width, reinterpret_cast<uint16_t *>(udst));
udst = (uint32_t *)&((uint16_t *)udst)[width]; udst = (uint32_t *)&((uint16_t *)udst)[width];
anim_line += this->anim_buf_pitch; anim_line += this->anim_buf_pitch;
} }
@ -458,11 +458,7 @@ void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &widt
uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x); uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
uint th = height - scroll_y; uint th = height - scroll_y;
for (; th > 0; th--) { Blitter::MovePixels(src, dst, tw, th, -this->anim_buf_pitch);
memcpy(dst, src, tw * sizeof(uint16_t));
src -= this->anim_buf_pitch;
dst -= this->anim_buf_pitch;
}
} else { } else {
/* Calculate pointers */ /* Calculate pointers */
dst = this->anim_buf + left + top * this->anim_buf_pitch; dst = this->anim_buf + left + top * this->anim_buf_pitch;
@ -475,15 +471,9 @@ void Blitter_32bppAnim::ScrollBuffer(void *video, int &left, int &top, int &widt
src -= scroll_x; src -= scroll_x;
} }
/* the y-displacement may be 0 therefore we have to use memmove,
* because source and destination may overlap */
uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x); uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
uint th = height + scroll_y; uint th = height + scroll_y;
for (; th > 0; th--) { Blitter::MovePixels(src, dst, tw, th, this->anim_buf_pitch);
memmove(dst, src, tw * sizeof(uint16_t));
src += this->anim_buf_pitch;
dst += this->anim_buf_pitch;
}
} }
Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y); Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y);

View File

@ -190,8 +190,8 @@ bmno_full_transparency:
const uint m1 = (uint8_t) (mvX2 >> 16); const uint m1 = (uint8_t) (mvX2 >> 16);
const uint r1 = remap[m1]; const uint r1 = remap[m1];
if (mvX2 & 0x00FF00FF) { if (mvX2 & 0x00FF00FF) {
/* Written so the compiler uses CMOV. */
#define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \ #define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
/* Written so the compiler uses CMOV. */ \
Colour m_colour = m_colour_init; \ Colour m_colour = m_colour_init; \
{ \ { \
const Colour srcm = (Colour) (m_src); \ const Colour srcm = (Colour) (m_src); \

View File

@ -39,8 +39,10 @@ public:
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent, bool animated> template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent, bool animated>
void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override {
return Blitter_32bppSSE_Base::Encode(sprite, allocator); Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override
{
return Blitter_32bppSSE_Base::Encode(sprite_type, sprite, allocator);
} }
std::string_view GetName() override { return "32bpp-sse4-anim"; } std::string_view GetName() override { return "32bpp-sse4-anim"; }
using Blitter_32bppSSE2_Anim::LookupColourInPalette; using Blitter_32bppSSE2_Anim::LookupColourInPalette;

View File

@ -51,7 +51,7 @@ void Blitter_32bppBase::CopyFromBuffer(void *video, const void *src, int width,
const uint32_t *usrc = (const uint32_t *)src; const uint32_t *usrc = (const uint32_t *)src;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(dst, usrc, width * sizeof(uint32_t)); std::copy_n(usrc, width, dst);
usrc += width; usrc += width;
dst += _screen.pitch; dst += _screen.pitch;
} }
@ -63,7 +63,7 @@ void Blitter_32bppBase::CopyToBuffer(const void *video, void *dst, int width, in
const uint32_t *src = (const uint32_t *)video; const uint32_t *src = (const uint32_t *)video;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(udst, src, width * sizeof(uint32_t)); std::copy_n(src, width, udst);
src += _screen.pitch; src += _screen.pitch;
udst += width; udst += width;
} }
@ -75,7 +75,7 @@ void Blitter_32bppBase::CopyImageToBuffer(const void *video, void *dst, int widt
const uint32_t *src = (const uint32_t *)video; const uint32_t *src = (const uint32_t *)video;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(udst, src, width * sizeof(uint32_t)); std::copy_n(src, width, udst);
src += _screen.pitch; src += _screen.pitch;
udst += dst_pitch; udst += dst_pitch;
} }
@ -106,11 +106,7 @@ void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &widt
width += scroll_x; width += scroll_x;
} }
for (int h = height; h > 0; h--) { Blitter::MovePixels(src, dst, width, height, -_screen.pitch);
memcpy(dst, src, width * sizeof(uint32_t));
src -= _screen.pitch;
dst -= _screen.pitch;
}
} else { } else {
/* Calculate pointers */ /* Calculate pointers */
dst = (uint32_t *)video + left + top * _screen.pitch; dst = (uint32_t *)video + left + top * _screen.pitch;
@ -130,13 +126,7 @@ void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &widt
width += scroll_x; width += scroll_x;
} }
/* the y-displacement may be 0 therefore we have to use memmove, Blitter::MovePixels(src, dst, width, height, _screen.pitch);
* because source and destination may overlap */
for (int h = height; h > 0; h--) {
memmove(dst, src, width * sizeof(uint32_t));
src += _screen.pitch;
dst += _screen.pitch;
}
} }
} }

View File

@ -32,11 +32,11 @@ inline void Blitter_32bppOptimized::Draw(const Blitter::BlitterParams *bp, ZoomL
/* src_px : each line begins with uint32_t n = 'number of bytes in this line', /* src_px : each line begins with uint32_t n = 'number of bytes in this line',
* then n times is the Colour struct for this line */ * then n times is the Colour struct for this line */
const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]); const Colour *src_px = reinterpret_cast<const Colour *>(src->data + src->offset[0][zoom]);
/* src_n : each line begins with uint32_t n = 'number of bytes in this line', /* src_n : each line begins with uint32_t n = 'number of bytes in this line',
* then interleaved stream of 'm' and 'n' channels. 'm' is remap, * then interleaved stream of 'm' and 'n' channels. 'm' is remap,
* 'n' is number of bytes with the same alpha channel class */ * 'n' is number of bytes with the same alpha channel class */
const uint16_t *src_n = (const uint16_t *)(src->data + src->offset[zoom][1]); const uint16_t *src_n = reinterpret_cast<const uint16_t *>(src->data + src->offset[1][zoom]);
/* skip upper lines in src_px and src_n */ /* skip upper lines in src_px and src_n */
for (uint i = bp->skip_top; i != 0; i--) { for (uint i = bp->skip_top; i != 0; i--) {
@ -285,12 +285,13 @@ void Blitter_32bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode,
this->Draw<false>(bp, mode, zoom); this->Draw<false>(bp, mode, zoom);
} }
template <bool Tpal_to_rgb> Sprite *Blitter_32bppOptimized::EncodeInternal(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) template <bool Tpal_to_rgb>
Sprite *Blitter_32bppOptimized::EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
/* streams of pixels (a, r, g, b channels) /* streams of pixels (a, r, g, b channels)
* *
* stored in separated stream so data are always aligned on 4B boundary */ * stored in separated stream so data are always aligned on 4B boundary */
std::array<std::unique_ptr<Colour[]>, ZOOM_LVL_END> dst_px_orig; SpriteCollMap<std::unique_ptr<Colour[]>> dst_px_orig;
/* interleaved stream of 'm' channel and 'n' channel /* interleaved stream of 'm' channel and 'n' channel
* 'n' is number of following pixels with the same alpha channel class * 'n' is number of following pixels with the same alpha channel class
@ -298,21 +299,21 @@ template <bool Tpal_to_rgb> Sprite *Blitter_32bppOptimized::EncodeInternal(const
* *
* it has to be stored in one stream so fewer registers are used - * it has to be stored in one stream so fewer registers are used -
* x86 has problems with register allocation even with this solution */ * x86 has problems with register allocation even with this solution */
std::array<std::unique_ptr<uint16_t[]>, ZOOM_LVL_END> dst_n_orig; SpriteCollMap<std::unique_ptr<uint16_t[]>> dst_n_orig;
/* lengths of streams */ /* lengths of streams */
uint32_t lengths[ZOOM_LVL_END][2]; SpriteCollMap<uint32_t> lengths[2];
ZoomLevel zoom_min; ZoomLevel zoom_min;
ZoomLevel zoom_max; ZoomLevel zoom_max;
if (sprite[ZOOM_LVL_MIN].type == SpriteType::Font) { if (sprite_type == SpriteType::Font) {
zoom_min = ZOOM_LVL_MIN; zoom_min = ZoomLevel::Min;
zoom_max = ZOOM_LVL_MIN; zoom_max = ZoomLevel::Min;
} else { } else {
zoom_min = _settings_client.gui.zoom_min; zoom_min = _settings_client.gui.zoom_min;
zoom_max = _settings_client.gui.zoom_max; zoom_max = _settings_client.gui.zoom_max;
if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX; if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max;
} }
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
@ -405,40 +406,43 @@ template <bool Tpal_to_rgb> Sprite *Blitter_32bppOptimized::EncodeInternal(const
dst_n_ln = (uint32_t *)dst_n; dst_n_ln = (uint32_t *)dst_n;
} }
lengths[z][0] = reinterpret_cast<uint8_t *>(dst_px_ln) - reinterpret_cast<uint8_t *>(dst_px_orig[z].get()); // all are aligned to 4B boundary lengths[0][z] = reinterpret_cast<uint8_t *>(dst_px_ln) - reinterpret_cast<uint8_t *>(dst_px_orig[z].get()); // all are aligned to 4B boundary
lengths[z][1] = reinterpret_cast<uint8_t *>(dst_n_ln) - reinterpret_cast<uint8_t *>(dst_n_orig[z].get()); lengths[1][z] = reinterpret_cast<uint8_t *>(dst_n_ln) - reinterpret_cast<uint8_t *>(dst_n_orig[z].get());
} }
uint len = 0; // total length of data uint len = 0; // total length of data
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
len += lengths[z][0] + lengths[z][1]; len += lengths[0][z] + lengths[1][z];
} }
Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + sizeof(SpriteData) + len); Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + sizeof(SpriteData) + len);
dest_sprite->height = sprite[ZOOM_LVL_MIN].height; const auto &root_sprite = sprite.Root();
dest_sprite->width = sprite[ZOOM_LVL_MIN].width; dest_sprite->height = root_sprite.height;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs; dest_sprite->width = root_sprite.width;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = root_sprite.y_offs;
SpriteData *dst = (SpriteData *)dest_sprite->data; SpriteData *dst = (SpriteData *)dest_sprite->data;
memset(dst, 0, sizeof(*dst));
uint32_t offset = 0;
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
dst->offset[z][0] = z == zoom_min ? 0 : lengths[z - 1][1] + dst->offset[z - 1][1]; dst->offset[0][z] = offset;
dst->offset[z][1] = lengths[z][0] + dst->offset[z][0]; offset += lengths[0][z];
dst->offset[1][z] = offset;
offset += lengths[1][z];
memcpy(dst->data + dst->offset[z][0], dst_px_orig[z].get(), lengths[z][0]); std::copy_n(reinterpret_cast<uint8_t *>(dst_px_orig[z].get()), lengths[0][z], dst->data + dst->offset[0][z]);
memcpy(dst->data + dst->offset[z][1], dst_n_orig[z].get(), lengths[z][1]); std::copy_n(reinterpret_cast<uint8_t *>(dst_n_orig[z].get()), lengths[1][z], dst->data + dst->offset[1][z]);
} }
return dest_sprite; return dest_sprite;
} }
template Sprite *Blitter_32bppOptimized::EncodeInternal<true>(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); template Sprite *Blitter_32bppOptimized::EncodeInternal<true>(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator);
template Sprite *Blitter_32bppOptimized::EncodeInternal<false>(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); template Sprite *Blitter_32bppOptimized::EncodeInternal<false>(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator);
Sprite *Blitter_32bppOptimized::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) Sprite *Blitter_32bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
return this->EncodeInternal<true>(sprite, allocator); return this->EncodeInternal<true>(sprite_type, sprite, allocator);
} }

View File

@ -17,12 +17,12 @@ class Blitter_32bppOptimized : public Blitter_32bppSimple {
public: public:
/** Data stored about a (single) sprite. */ /** Data stored about a (single) sprite. */
struct SpriteData { struct SpriteData {
uint32_t offset[ZOOM_LVL_END][2]; ///< Offsets (from .data) to streams for different zoom levels, and the normal and remap image information. SpriteCollMap<uint32_t> offset[2]; ///< Offsets (from .data) to streams for different zoom levels, and the normal and remap image information.
uint8_t data[]; ///< Data, all zoomlevels. uint8_t data[]; ///< Data, all zoomlevels.
}; };
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
std::string_view GetName() override { return "32bpp-optimized"; } std::string_view GetName() override { return "32bpp-optimized"; }
@ -30,7 +30,7 @@ public:
protected: protected:
template <bool Tpal_to_rgb> void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom); template <bool Tpal_to_rgb> void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom);
template <bool Tpal_to_rgb> Sprite *EncodeInternal(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); template <bool Tpal_to_rgb> Sprite *EncodeInternal(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator);
}; };
/** Factory for the optimised 32 bpp blitter (without palette animation). */ /** Factory for the optimised 32 bpp blitter (without palette animation). */

View File

@ -115,20 +115,21 @@ void Blitter_32bppSimple::DrawColourMappingRect(void *dst, int width, int height
Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal); Debug(misc, 0, "32bpp blitter doesn't know how to draw this colour table ('{}')", pal);
} }
Sprite *Blitter_32bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) Sprite *Blitter_32bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
const auto &root_sprite = sprite.Root();
Blitter_32bppSimple::Pixel *dst; Blitter_32bppSimple::Pixel *dst;
Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(sprite[ZOOM_LVL_MIN].height) * static_cast<size_t>(sprite[ZOOM_LVL_MIN].width) * sizeof(*dst)); Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(root_sprite.height) * static_cast<size_t>(root_sprite.width) * sizeof(*dst));
dest_sprite->height = sprite[ZOOM_LVL_MIN].height; dest_sprite->height = root_sprite.height;
dest_sprite->width = sprite[ZOOM_LVL_MIN].width; dest_sprite->width = root_sprite.width;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs; dest_sprite->y_offs = root_sprite.y_offs;
dst = (Blitter_32bppSimple::Pixel *)dest_sprite->data; dst = reinterpret_cast<Blitter_32bppSimple::Pixel *>(dest_sprite->data);
SpriteLoader::CommonPixel *src = (SpriteLoader::CommonPixel *)sprite[ZOOM_LVL_MIN].data; SpriteLoader::CommonPixel *src = reinterpret_cast<SpriteLoader::CommonPixel *>(root_sprite.data);
for (int i = 0; i < sprite[ZOOM_LVL_MIN].height * sprite[ZOOM_LVL_MIN].width; i++) { for (int i = 0; i < root_sprite.height * root_sprite.width; i++) {
if (src->m == 0) { if (src->m == 0) {
dst[i].r = src->r; dst[i].r = src->r;
dst[i].g = src->g; dst[i].g = src->g;

View File

@ -26,7 +26,7 @@ class Blitter_32bppSimple : public Blitter_32bppBase {
public: public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
std::string_view GetName() override { return "32bpp-simple"; } std::string_view GetName() override { return "32bpp-simple"; }
}; };

View File

@ -20,18 +20,18 @@
/** Instantiation of the SSE2 32bpp blitter factory. */ /** Instantiation of the SSE2 32bpp blitter factory. */
static FBlitter_32bppSSE2 iFBlitter_32bppSSE2; static FBlitter_32bppSSE2 iFBlitter_32bppSSE2;
Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) Sprite *Blitter_32bppSSE_Base::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
/* First uint32_t of a line = the number of transparent pixels from the left. /* First uint32_t of a line = the number of transparent pixels from the left.
* Second uint32_t of a line = the number of transparent pixels from the right. * Second uint32_t of a line = the number of transparent pixels from the right.
* Then all RGBA then all MV. * Then all RGBA then all MV.
*/ */
ZoomLevel zoom_min = ZOOM_LVL_MIN; ZoomLevel zoom_min = ZoomLevel::Min;
ZoomLevel zoom_max = ZOOM_LVL_MIN; ZoomLevel zoom_max = ZoomLevel::Min;
if (sprite[ZOOM_LVL_MIN].type != SpriteType::Font) { if (sprite_type != SpriteType::Font) {
zoom_min = _settings_client.gui.zoom_min; zoom_min = _settings_client.gui.zoom_min;
zoom_max = _settings_client.gui.zoom_max; zoom_max = _settings_client.gui.zoom_max;
if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX; if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max;
} }
/* Calculate sizes and allocate. */ /* Calculate sizes and allocate. */
@ -39,23 +39,25 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri
uint all_sprites_size = 0; uint all_sprites_size = 0;
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
const SpriteLoader::Sprite *src_sprite = &sprite[z]; const SpriteLoader::Sprite *src_sprite = &sprite[z];
sd.infos[z].sprite_width = src_sprite->width; auto &info = sd.infos[z];
sd.infos[z].sprite_offset = all_sprites_size; info.sprite_width = src_sprite->width;
sd.infos[z].sprite_line_size = sizeof(Colour) * src_sprite->width + sizeof(uint32_t) * META_LENGTH; info.sprite_offset = all_sprites_size;
info.sprite_line_size = sizeof(Colour) * src_sprite->width + sizeof(uint32_t) * META_LENGTH;
const uint rgba_size = sd.infos[z].sprite_line_size * src_sprite->height; const uint rgba_size = info.sprite_line_size * src_sprite->height;
sd.infos[z].mv_offset = all_sprites_size + rgba_size; info.mv_offset = all_sprites_size + rgba_size;
const uint mv_size = sizeof(MapValue) * src_sprite->width * src_sprite->height; const uint mv_size = sizeof(MapValue) * src_sprite->width * src_sprite->height;
all_sprites_size += rgba_size + mv_size; all_sprites_size += rgba_size + mv_size;
} }
Sprite *dst_sprite = allocator.Allocate<Sprite>(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size); Sprite *dst_sprite = allocator.Allocate<Sprite>(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size);
dst_sprite->height = sprite[ZOOM_LVL_MIN].height; const auto &root_sprite = sprite.Root();
dst_sprite->width = sprite[ZOOM_LVL_MIN].width; dst_sprite->height = root_sprite.height;
dst_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs; dst_sprite->width = root_sprite.width;
dst_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs; dst_sprite->x_offs = root_sprite.x_offs;
memcpy(dst_sprite->data, &sd, sizeof(SpriteData)); dst_sprite->y_offs = root_sprite.y_offs;
std::copy_n(reinterpret_cast<std::byte *>(&sd), sizeof(SpriteData), dst_sprite->data);
/* Copy colours and determine flags. */ /* Copy colours and determine flags. */
bool has_remap = false; bool has_remap = false;
@ -64,8 +66,9 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) { for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
const SpriteLoader::Sprite *src_sprite = &sprite[z]; const SpriteLoader::Sprite *src_sprite = &sprite[z];
const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *) src_sprite->data; const SpriteLoader::CommonPixel *src = (const SpriteLoader::CommonPixel *) src_sprite->data;
Colour *dst_rgba_line = (Colour *) &dst_sprite->data[sizeof(SpriteData) + sd.infos[z].sprite_offset]; const auto &info = sd.infos[z];
MapValue *dst_mv = (MapValue *) &dst_sprite->data[sizeof(SpriteData) + sd.infos[z].mv_offset]; Colour *dst_rgba_line = reinterpret_cast<Colour *>(&dst_sprite->data[sizeof(SpriteData) + info.sprite_offset]);
MapValue *dst_mv = reinterpret_cast<MapValue *>(&dst_sprite->data[sizeof(SpriteData) + info.mv_offset]);
for (uint y = src_sprite->height; y != 0; y--) { for (uint y = src_sprite->height; y != 0; y--) {
Colour *dst_rgba = dst_rgba_line + META_LENGTH; Colour *dst_rgba = dst_rgba_line + META_LENGTH;
for (uint x = src_sprite->width; x != 0; x--) { for (uint x = src_sprite->width; x != 0; x--) {
@ -113,7 +116,7 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri
(*dst_rgba_line).data = nb_pix_transp; (*dst_rgba_line).data = nb_pix_transp;
Colour *nb_right = dst_rgba_line + 1; Colour *nb_right = dst_rgba_line + 1;
dst_rgba_line = (Colour*) ((uint8_t*) dst_rgba_line + sd.infos[z].sprite_line_size); dst_rgba_line = reinterpret_cast<Colour *>(reinterpret_cast<std::byte *>(dst_rgba_line) + info.sprite_line_size);
/* Count the number of transparent pixels from the right. */ /* Count the number of transparent pixels from the right. */
dst_rgba = dst_rgba_line - 1; dst_rgba = dst_rgba_line - 1;
@ -132,7 +135,7 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri
if (has_translucency) sd.flags.Set(SpriteFlag::Translucent); if (has_translucency) sd.flags.Set(SpriteFlag::Translucent);
if (!has_remap) sd.flags.Set(SpriteFlag::NoRemap); if (!has_remap) sd.flags.Set(SpriteFlag::NoRemap);
if (!has_anim) sd.flags.Set(SpriteFlag::NoAnim); if (!has_anim) sd.flags.Set(SpriteFlag::NoAnim);
memcpy(dst_sprite->data, &sd, sizeof(SpriteData)); std::copy_n(reinterpret_cast<std::byte *>(&sd), sizeof(SpriteData), dst_sprite->data);
return dst_sprite; return dst_sprite;
} }

View File

@ -73,11 +73,11 @@ public:
}; };
struct SpriteData { struct SpriteData {
SpriteFlags flags{}; SpriteFlags flags{};
std::array<SpriteInfo, ZOOM_LVL_END> infos{}; SpriteCollMap<SpriteInfo> infos{};
uint8_t data[]; ///< Data, all zoomlevels. uint8_t data[]; ///< Data, all zoomlevels.
}; };
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator); Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator);
}; };
/** The SSE2 32 bpp blitter (without palette animation). */ /** The SSE2 32 bpp blitter (without palette animation). */
@ -87,8 +87,9 @@ public:
template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent> template <BlitterMode mode, Blitter_32bppSSE_Base::ReadMode read_mode, Blitter_32bppSSE_Base::BlockType bt_last, bool translucent>
void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom); void Draw(const Blitter::BlitterParams *bp, ZoomLevel zoom);
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override { Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override
return Blitter_32bppSSE_Base::Encode(sprite, allocator); {
return Blitter_32bppSSE_Base::Encode(sprite_type, sprite, allocator);
} }
std::string_view GetName() override { return "32bpp-sse2"; } std::string_view GetName() override { return "32bpp-sse2"; }

View File

@ -309,8 +309,8 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel
/* Remap colours. */ /* Remap colours. */
if (mvX2 & 0x00FF00FF) { if (mvX2 & 0x00FF00FF) {
/* Written so the compiler uses CMOV. */
#define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \ #define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
/* Written so the compiler uses CMOV. */ \
Colour m_colour = m_colour_init; \ Colour m_colour = m_colour_init; \
{ \ { \
const Colour srcm = (Colour) (m_src); \ const Colour srcm = (Colour) (m_src); \

View File

@ -28,18 +28,10 @@
#endif #endif
#define META_LENGTH 2 ///< Number of uint32_t inserted before each line of pixels in a sprite. #define META_LENGTH 2 ///< Number of uint32_t inserted before each line of pixels in a sprite.
#define MARGIN_NORMAL_THRESHOLD (zoom == ZOOM_LVL_OUT_8X ? 8 : 4) ///< Minimum width to use margins with BlitterMode::Normal. #define MARGIN_NORMAL_THRESHOLD (zoom == ZoomLevel::Out8x ? 8 : 4) ///< Minimum width to use margins with BlitterMode::Normal.
#define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BlitterMode::ColourRemap. #define MARGIN_REMAP_THRESHOLD 4 ///< Minimum width to use margins with BlitterMode::ColourRemap.
#undef ALIGN typedef union alignas(16) um128i {
#ifdef _MSC_VER
#define ALIGN(n) __declspec(align(n))
#else
#define ALIGN(n) __attribute__ ((aligned (n)))
#endif
typedef union ALIGN(16) um128i {
__m128i m128i; __m128i m128i;
uint8_t m128i_u8[16]; uint8_t m128i_u8[16];
uint16_t m128i_u16[8]; uint16_t m128i_u16[8];

View File

@ -96,11 +96,11 @@ inline void Blitter_40bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
/* src_px : each line begins with uint32_t n = 'number of bytes in this line', /* src_px : each line begins with uint32_t n = 'number of bytes in this line',
* then n times is the Colour struct for this line */ * then n times is the Colour struct for this line */
const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]); const Colour *src_px = reinterpret_cast<const Colour *>(src->data + src->offset[0][zoom]);
/* src_n : each line begins with uint32_t n = 'number of bytes in this line', /* src_n : each line begins with uint32_t n = 'number of bytes in this line',
* then interleaved stream of 'm' and 'n' channels. 'm' is remap, * then interleaved stream of 'm' and 'n' channels. 'm' is remap,
* 'n' is number of bytes with the same alpha channel class */ * 'n' is number of bytes with the same alpha channel class */
const uint16_t *src_n = (const uint16_t *)(src->data + src->offset[zoom][1]); const uint16_t *src_n = reinterpret_cast<const uint16_t *>(src->data + src->offset[1][zoom]);
/* skip upper lines in src_px and src_n */ /* skip upper lines in src_px and src_n */
for (uint i = bp->skip_top; i != 0; i--) { for (uint i = bp->skip_top; i != 0; i--) {
@ -377,8 +377,11 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height,
const uint8_t *remap = GetNonSprite(pal, SpriteType::Recolour) + 1; const uint8_t *remap = GetNonSprite(pal, SpriteType::Recolour) + 1;
do { do {
for (int i = 0; i != width; i++) { for (int i = 0; i != width; i++) {
if (*anim == 0) *udst = MakeGrey(*udst); if (*anim == 0) {
*udst = MakeGrey(*udst);
} else {
*anim = remap[*anim]; *anim = remap[*anim];
}
udst++; udst++;
anim++; anim++;
} }
@ -389,7 +392,7 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height,
const uint8_t *remap = GetNonSprite(pal, SpriteType::Recolour) + 1; const uint8_t *remap = GetNonSprite(pal, SpriteType::Recolour) + 1;
do { do {
for (int i = 0; i != width; i++) { for (int i = 0; i != width; i++) {
*anim = remap[*anim]; if (*anim != 0) *anim = remap[*anim];
anim++; anim++;
} }
anim = anim - width + _screen.pitch; anim = anim - width + _screen.pitch;
@ -397,9 +400,9 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height,
} }
} }
Sprite *Blitter_40bppAnim::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) Sprite *Blitter_40bppAnim::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
return this->EncodeInternal<false>(sprite, allocator); return this->EncodeInternal<false>(sprite_type, sprite, allocator);
} }
@ -415,11 +418,11 @@ void Blitter_40bppAnim::CopyFromBuffer(void *video, const void *src, int width,
uint8_t *anim_line = ((uint32_t *)video - (uint32_t *)_screen.dst_ptr) + anim_buf; uint8_t *anim_line = ((uint32_t *)video - (uint32_t *)_screen.dst_ptr) + anim_buf;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(dst, usrc, width * sizeof(uint32_t)); std::copy_n(usrc, width, dst);
usrc += width; usrc += width;
dst += _screen.pitch; dst += _screen.pitch;
/* Copy back the anim-buffer */ /* Copy back the anim-buffer */
memcpy(anim_line, usrc, width * sizeof(uint8_t)); std::copy_n(reinterpret_cast<const uint8_t *>(usrc), width, anim_line);
usrc = (const uint32_t *)((const uint8_t *)usrc + width); usrc = (const uint32_t *)((const uint8_t *)usrc + width);
anim_line += _screen.pitch; anim_line += _screen.pitch;
} }
@ -437,11 +440,11 @@ void Blitter_40bppAnim::CopyToBuffer(const void *video, void *dst, int width, in
const uint8_t *anim_line = ((const uint32_t *)video - (uint32_t *)_screen.dst_ptr) + anim_buf; const uint8_t *anim_line = ((const uint32_t *)video - (uint32_t *)_screen.dst_ptr) + anim_buf;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(udst, src, width * sizeof(uint32_t)); std::copy_n(src, width, udst);
src += _screen.pitch; src += _screen.pitch;
udst += width; udst += width;
/* Copy the anim-buffer */ /* Copy the anim-buffer */
memcpy(udst, anim_line, width * sizeof(uint8_t)); std::copy_n(anim_line, width, reinterpret_cast<uint8_t *>(udst));
udst = (uint32_t *)((uint8_t *)udst + width); udst = (uint32_t *)((uint8_t *)udst + width);
anim_line += _screen.pitch; anim_line += _screen.pitch;
} }
@ -490,11 +493,7 @@ void Blitter_40bppAnim::ScrollBuffer(void *video, int &left, int &top, int &widt
uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x); uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
uint th = height - scroll_y; uint th = height - scroll_y;
for (; th > 0; th--) { Blitter::MovePixels(src, dst, tw, th, -_screen.pitch);
memcpy(dst, src, tw * sizeof(uint8_t));
src -= _screen.pitch;
dst -= _screen.pitch;
}
} else { } else {
/* Calculate pointers */ /* Calculate pointers */
dst = anim_buf + left + top * _screen.pitch; dst = anim_buf + left + top * _screen.pitch;
@ -507,15 +506,9 @@ void Blitter_40bppAnim::ScrollBuffer(void *video, int &left, int &top, int &widt
src -= scroll_x; src -= scroll_x;
} }
/* the y-displacement may be 0 therefore we have to use memmove,
* because source and destination may overlap */
uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x); uint tw = width + (scroll_x >= 0 ? -scroll_x : scroll_x);
uint th = height + scroll_y; uint th = height + scroll_y;
for (; th > 0; th--) { Blitter::MovePixels(src, dst, tw, th, _screen.pitch);
memmove(dst, src, tw * sizeof(uint8_t));
src += _screen.pitch;
dst += _screen.pitch;
}
} }
Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y); Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y);

View File

@ -27,7 +27,7 @@ public:
void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override; void ScrollBuffer(void *video, int &left, int &top, int &width, int &height, int scroll_x, int scroll_y) override;
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override; void DrawColourMappingRect(void *dst, int width, int height, PaletteID pal) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
size_t BufferSize(uint width, uint height) override; size_t BufferSize(uint width, uint height) override;
Blitter::PaletteAnimation UsePaletteAnimation() override; Blitter::PaletteAnimation UsePaletteAnimation() override;
bool NeedsAnimationBuffer() override; bool NeedsAnimationBuffer() override;

View File

@ -43,9 +43,10 @@ void Blitter_8bppBase::DrawLine(void *video, int x, int y, int x2, int y2, int s
void Blitter_8bppBase::DrawRect(void *video, int width, int height, uint8_t colour) void Blitter_8bppBase::DrawRect(void *video, int width, int height, uint8_t colour)
{ {
std::byte *p = static_cast<std::byte *>(video);
do { do {
memset(video, colour, width); std::fill_n(p, width, static_cast<std::byte>(colour));
video = (uint8_t *)video + _screen.pitch; p += _screen.pitch;
} while (--height); } while (--height);
} }
@ -55,7 +56,7 @@ void Blitter_8bppBase::CopyFromBuffer(void *video, const void *src, int width, i
const uint8_t *usrc = (const uint8_t *)src; const uint8_t *usrc = (const uint8_t *)src;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(dst, usrc, width * sizeof(uint8_t)); std::copy_n(usrc, width, dst);
usrc += width; usrc += width;
dst += _screen.pitch; dst += _screen.pitch;
} }
@ -67,7 +68,7 @@ void Blitter_8bppBase::CopyToBuffer(const void *video, void *dst, int width, int
const uint8_t *src = (const uint8_t *)video; const uint8_t *src = (const uint8_t *)video;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(udst, src, width * sizeof(uint8_t)); std::copy_n(src, width, udst);
src += _screen.pitch; src += _screen.pitch;
udst += width; udst += width;
} }
@ -79,7 +80,7 @@ void Blitter_8bppBase::CopyImageToBuffer(const void *video, void *dst, int width
const uint8_t *src = (const uint8_t *)video; const uint8_t *src = (const uint8_t *)video;
for (; height > 0; height--) { for (; height > 0; height--) {
memcpy(udst, src, width * sizeof(uint8_t)); std::copy_n(src, width, udst);
src += _screen.pitch; src += _screen.pitch;
udst += dst_pitch; udst += dst_pitch;
} }
@ -110,11 +111,7 @@ void Blitter_8bppBase::ScrollBuffer(void *video, int &left, int &top, int &width
width += scroll_x; width += scroll_x;
} }
for (int h = height; h > 0; h--) { Blitter::MovePixels(src, dst, width, height, -_screen.pitch);
memcpy(dst, src, width * sizeof(uint8_t));
src -= _screen.pitch;
dst -= _screen.pitch;
}
} else { } else {
/* Calculate pointers */ /* Calculate pointers */
dst = (uint8_t *)video + left + top * _screen.pitch; dst = (uint8_t *)video + left + top * _screen.pitch;
@ -134,13 +131,7 @@ void Blitter_8bppBase::ScrollBuffer(void *video, int &left, int &top, int &width
width += scroll_x; width += scroll_x;
} }
/* the y-displacement may be 0 therefore we have to use memmove, Blitter::MovePixels(src, dst, width, height, _screen.pitch);
* because source and destination may overlap */
for (int h = height; h > 0; h--) {
memmove(dst, src, width * sizeof(uint8_t));
src += _screen.pitch;
dst += _screen.pitch;
}
} }
} }

View File

@ -11,7 +11,6 @@
#include "../zoom_func.h" #include "../zoom_func.h"
#include "../settings_type.h" #include "../settings_type.h"
#include "../core/math_func.hpp" #include "../core/math_func.hpp"
#include "../core/mem_func.hpp"
#include "8bpp_optimized.hpp" #include "8bpp_optimized.hpp"
#include "../safeguards.h" #include "../safeguards.h"
@ -96,7 +95,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z
} }
case BlitterMode::BlackRemap: case BlitterMode::BlackRemap:
MemSetT(dst, 0, pixels); std::fill_n(dst, pixels, 0);
dst += pixels; dst += pixels;
break; break;
@ -112,7 +111,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z
} }
default: default:
MemCpyT(dst, src, pixels); std::copy_n(src, pixels, dst);
dst += pixels; src += pixels; dst += pixels; src += pixels;
break; break;
} }
@ -120,7 +119,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z
} }
} }
Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) Sprite *Blitter_8bppOptimized::Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
/* Make memory for all zoom-levels */ /* Make memory for all zoom-levels */
uint memory = sizeof(SpriteData); uint memory = sizeof(SpriteData);
@ -128,13 +127,13 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
ZoomLevel zoom_min; ZoomLevel zoom_min;
ZoomLevel zoom_max; ZoomLevel zoom_max;
if (sprite[ZOOM_LVL_MIN].type == SpriteType::Font) { if (sprite_type == SpriteType::Font) {
zoom_min = ZOOM_LVL_MIN; zoom_min = ZoomLevel::Min;
zoom_max = ZOOM_LVL_MIN; zoom_max = ZoomLevel::Min;
} else { } else {
zoom_min = _settings_client.gui.zoom_min; zoom_min = _settings_client.gui.zoom_min;
zoom_max = _settings_client.gui.zoom_max; zoom_max = _settings_client.gui.zoom_max;
if (zoom_max == zoom_min) zoom_max = ZOOM_LVL_MAX; if (zoom_max == zoom_min) zoom_max = ZoomLevel::Max;
} }
for (ZoomLevel i = zoom_min; i <= zoom_max; i++) { for (ZoomLevel i = zoom_min; i <= zoom_max; i++) {
@ -153,13 +152,14 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
/* Make the sprites per zoom-level */ /* Make the sprites per zoom-level */
for (ZoomLevel i = zoom_min; i <= zoom_max; i++) { for (ZoomLevel i = zoom_min; i <= zoom_max; i++) {
const SpriteLoader::Sprite &src_orig = sprite[i];
/* Store the index table */ /* Store the index table */
uint offset = dst - temp_dst->data; uint offset = dst - temp_dst->data;
temp_dst->offset[i] = offset; temp_dst->offset[i] = offset;
/* cache values, because compiler can't cache it */ /* cache values, because compiler can't cache it */
int scaled_height = sprite[i].height; int scaled_height = src_orig.height;
int scaled_width = sprite[i].width; int scaled_width = src_orig.width;
for (int y = 0; y < scaled_height; y++) { for (int y = 0; y < scaled_height; y++) {
uint trans = 0; uint trans = 0;
@ -168,7 +168,7 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
uint8_t *count_dst = nullptr; uint8_t *count_dst = nullptr;
/* Store the scaled image */ /* Store the scaled image */
const SpriteLoader::CommonPixel *src = &sprite[i].data[y * sprite[i].width]; const SpriteLoader::CommonPixel *src = &src_orig.data[y * src_orig.width];
for (int x = 0; x < scaled_width; x++) { for (int x = 0; x < scaled_width; x++) {
uint colour = src++->m; uint colour = src++->m;
@ -220,11 +220,12 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
/* Allocate the exact amount of memory we need */ /* Allocate the exact amount of memory we need */
Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + size); Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + size);
dest_sprite->height = sprite[ZOOM_LVL_MIN].height; const auto &root_sprite = sprite.Root();
dest_sprite->width = sprite[ZOOM_LVL_MIN].width; dest_sprite->height = root_sprite.height;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs; dest_sprite->width = root_sprite.width;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs; dest_sprite->x_offs = root_sprite.x_offs;
memcpy(dest_sprite->data, temp_dst, size); dest_sprite->y_offs = root_sprite.y_offs;
std::copy_n(reinterpret_cast<std::byte *>(temp_dst), size, dest_sprite->data);
return dest_sprite; return dest_sprite;
} }

View File

@ -18,12 +18,12 @@ class Blitter_8bppOptimized final : public Blitter_8bppBase {
public: public:
/** Data stored about a (single) sprite. */ /** Data stored about a (single) sprite. */
struct SpriteData { struct SpriteData {
uint32_t offset[ZOOM_LVL_END]; ///< Offsets (from .data) to streams for different zoom levels. SpriteCollMap<uint32_t> offset; ///< Offsets (from .data) to streams for different zoom levels.
uint8_t data[]; ///< Data, all zoomlevels. uint8_t data[]; ///< Data, all zoomlevels.
}; };
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
std::string_view GetName() override { return "8bpp-optimized"; } std::string_view GetName() override { return "8bpp-optimized"; }
}; };

View File

@ -61,19 +61,21 @@ void Blitter_8bppSimple::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Zoom
} }
} }
Sprite *Blitter_8bppSimple::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) Sprite *Blitter_8bppSimple::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
const auto &root_sprite = sprite.Root();
Sprite *dest_sprite; Sprite *dest_sprite;
dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(sprite[ZOOM_LVL_MIN].height) * static_cast<size_t>(sprite[ZOOM_LVL_MIN].width)); dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + static_cast<size_t>(root_sprite.height) * static_cast<size_t>(root_sprite.width));
dest_sprite->height = sprite[ZOOM_LVL_MIN].height; dest_sprite->height = root_sprite.height;
dest_sprite->width = sprite[ZOOM_LVL_MIN].width; dest_sprite->width = root_sprite.width;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs; dest_sprite->y_offs = root_sprite.y_offs;
/* Copy over only the 'remap' channel, as that is what we care about in 8bpp */ /* Copy over only the 'remap' channel, as that is what we care about in 8bpp */
for (int i = 0; i < sprite[ZOOM_LVL_MIN].height * sprite[ZOOM_LVL_MIN].width; i++) { uint8_t *dst = reinterpret_cast<uint8_t *>(dest_sprite->data);
dest_sprite->data[i] = sprite[ZOOM_LVL_MIN].data[i].m; for (int i = 0; i < root_sprite.height * root_sprite.width; i++) {
dst[i] = root_sprite.data[i].m;
} }
return dest_sprite; return dest_sprite;

View File

@ -17,7 +17,7 @@
class Blitter_8bppSimple final : public Blitter_8bppBase { class Blitter_8bppSimple final : public Blitter_8bppBase {
public: public:
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override; void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) override;
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
std::string_view GetName() override { return "8bpp-simple"; } std::string_view GetName() override { return "8bpp-simple"; }
}; };

View File

@ -207,6 +207,8 @@ public:
virtual ~Blitter() = default; virtual ~Blitter() = default;
template <typename SetPixelT> void DrawLineGeneric(int x, int y, int x2, int y2, int screen_width, int screen_height, int width, int dash, SetPixelT set_pixel); template <typename SetPixelT> void DrawLineGeneric(int x, int y, int x2, int y2, int screen_width, int screen_height, int width, int dash, SetPixelT set_pixel);
template <typename T> static void MovePixels(const T *src, T *dst, size_t width, size_t height, ptrdiff_t pitch);
}; };
#endif /* BLITTER_BASE_HPP */ #endif /* BLITTER_BASE_HPP */

View File

@ -192,4 +192,24 @@ void Blitter::DrawLineGeneric(int x1, int y1, int x2, int y2, int screen_width,
} }
} }
template <typename T>
/* static */ void Blitter::MovePixels(const T *src, T *dst, size_t width, size_t height, ptrdiff_t pitch)
{
if (src == dst) return;
if (src < dst) {
for (size_t i = 0; i < height; ++i) {
std::move_backward(src, src + width, dst + width);
src += pitch;
dst += pitch;
}
} else {
for (size_t i = 0; i < height; ++i) {
std::move(src, src + width, dst);
src += pitch;
dst += pitch;
}
}
}
#endif /* BLITTER_COMMON_HPP */ #endif /* BLITTER_COMMON_HPP */

View File

@ -55,7 +55,7 @@ protected:
* @pre description != nullptr. * @pre description != nullptr.
* @pre There is no blitter registered with this name. * @pre There is no blitter registered with this name.
*/ */
BlitterFactory(const char *name, const char *description, bool usable = true) : BlitterFactory(std::string_view name, std::string_view description, bool usable = true) :
name(name), description(description) name(name), description(description)
{ {
if (usable) { if (usable) {
@ -93,7 +93,7 @@ public:
* @param name the blitter to select. * @param name the blitter to select.
* @post Sets the blitter so GetCurrentBlitter() returns it too. * @post Sets the blitter so GetCurrentBlitter() returns it too.
*/ */
static Blitter *SelectBlitter(const std::string_view name) static Blitter *SelectBlitter(std::string_view name)
{ {
BlitterFactory *b = GetBlitterFactory(name); BlitterFactory *b = GetBlitterFactory(name);
if (b == nullptr) return nullptr; if (b == nullptr) return nullptr;
@ -109,17 +109,17 @@ public:
* @param name the blitter factory to select. * @param name the blitter factory to select.
* @return The blitter factory, or nullptr when there isn't one with the wanted name. * @return The blitter factory, or nullptr when there isn't one with the wanted name.
*/ */
static BlitterFactory *GetBlitterFactory(const std::string_view name) static BlitterFactory *GetBlitterFactory(std::string_view name)
{ {
#if defined(DEDICATED) #if defined(DEDICATED)
const std::string_view default_blitter = "null"; static const std::string_view default_blitter = "null";
#elif defined(WITH_COCOA) #elif defined(WITH_COCOA)
const std::string_view default_blitter = "32bpp-anim"; static const std::string_view default_blitter = "32bpp-anim";
#else #else
const std::string_view default_blitter = "8bpp-optimized"; static const std::string_view default_blitter = "8bpp-optimized";
#endif #endif
if (GetBlitters().empty()) return nullptr; if (GetBlitters().empty()) return nullptr;
const std::string_view bname = name.empty() ? default_blitter : name; std::string_view bname = name.empty() ? default_blitter : name;
for (auto &it : GetBlitters()) { for (auto &it : GetBlitters()) {
BlitterFactory *b = it.second; BlitterFactory *b = it.second;

View File

@ -15,15 +15,16 @@
/** Instantiation of the null blitter factory. */ /** Instantiation of the null blitter factory. */
static FBlitter_Null iFBlitter_Null; static FBlitter_Null iFBlitter_Null;
Sprite *Blitter_Null::Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) Sprite *Blitter_Null::Encode(SpriteType, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator)
{ {
Sprite *dest_sprite; Sprite *dest_sprite;
dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite)); dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite));
dest_sprite->height = sprite[ZOOM_LVL_MIN].height; const auto &root_sprite = sprite.Root();
dest_sprite->width = sprite[ZOOM_LVL_MIN].width; dest_sprite->height = root_sprite.height;
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs; dest_sprite->width = root_sprite.width;
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs; dest_sprite->x_offs = root_sprite.x_offs;
dest_sprite->y_offs = root_sprite.y_offs;
return dest_sprite; return dest_sprite;
} }

View File

@ -18,7 +18,7 @@ public:
uint8_t GetScreenDepth() override { return 0; } uint8_t GetScreenDepth() override { return 0; }
void Draw(Blitter::BlitterParams *, BlitterMode, ZoomLevel) override {}; void Draw(Blitter::BlitterParams *, BlitterMode, ZoomLevel) override {};
void DrawColourMappingRect(void *, int, int, PaletteID) override {}; void DrawColourMappingRect(void *, int, int, PaletteID) override {};
Sprite *Encode(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override; Sprite *Encode(SpriteType sprite_type, const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator) override;
void *MoveTo(void *, int, int) override { return nullptr; }; void *MoveTo(void *, int, int) override { return nullptr; };
void SetPixel(void *, int, int, uint8_t) override {}; void SetPixel(void *, int, int, uint8_t) override {};
void DrawRect(void *, int, int, uint8_t) override {}; void DrawRect(void *, int, int, uint8_t) override {};

View File

@ -42,7 +42,7 @@ static constexpr NWidgetPart _background_widgets[] = {
* Window description for the background window to prevent smearing. * Window description for the background window to prevent smearing.
*/ */
static WindowDesc _background_desc( static WindowDesc _background_desc(
WDP_MANUAL, nullptr, 0, 0, WDP_MANUAL, {}, 0, 0,
WC_BOOTSTRAP, WC_NONE, WC_BOOTSTRAP, WC_NONE,
WindowDefaultFlag::NoClose, WindowDefaultFlag::NoClose,
_background_widgets _background_widgets
@ -78,7 +78,7 @@ static constexpr NWidgetPart _nested_bootstrap_errmsg_widgets[] = {
/** Window description for the error window. */ /** Window description for the error window. */
static WindowDesc _bootstrap_errmsg_desc( static WindowDesc _bootstrap_errmsg_desc(
WDP_CENTER, nullptr, 0, 0, WDP_CENTER, {}, 0, 0,
WC_BOOTSTRAP, WC_NONE, WC_BOOTSTRAP, WC_NONE,
{WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose}, {WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose},
_nested_bootstrap_errmsg_widgets _nested_bootstrap_errmsg_widgets
@ -135,7 +135,7 @@ static constexpr NWidgetPart _nested_bootstrap_download_status_window_widgets[]
/** Window description for the download window */ /** Window description for the download window */
static WindowDesc _bootstrap_download_status_window_desc( static WindowDesc _bootstrap_download_status_window_desc(
WDP_CENTER, nullptr, 0, 0, WDP_CENTER, {}, 0, 0,
WC_NETWORK_STATUS_WINDOW, WC_NONE, WC_NETWORK_STATUS_WINDOW, WC_NONE,
{WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose}, {WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose},
_nested_bootstrap_download_status_window_widgets _nested_bootstrap_download_status_window_widgets
@ -187,7 +187,7 @@ static constexpr NWidgetPart _bootstrap_query_widgets[] = {
/** The window description for the query. */ /** The window description for the query. */
static WindowDesc _bootstrap_query_desc( static WindowDesc _bootstrap_query_desc(
WDP_CENTER, nullptr, 0, 0, WDP_CENTER, {}, 0, 0,
WC_CONFIRM_POPUP_QUERY, WC_NONE, WC_CONFIRM_POPUP_QUERY, WC_NONE,
WindowDefaultFlag::NoClose, WindowDefaultFlag::NoClose,
_bootstrap_query_widgets _bootstrap_query_widgets
@ -273,10 +273,10 @@ public:
_network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS); _network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
} }
void OnReceiveContentInfo(const ContentInfo *ci) override void OnReceiveContentInfo(const ContentInfo &ci) override
{ {
/* And once the meta data is received, start downloading it. */ /* And once the meta data is received, start downloading it. */
_network_content_client.Select(ci->id); _network_content_client.Select(ci.id);
new BootstrapContentDownloadStatusWindow(); new BootstrapContentDownloadStatusWindow();
this->Close(); this->Close();
} }
@ -320,19 +320,19 @@ public:
_network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS); _network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
} }
void OnReceiveContentInfo(const ContentInfo *ci) override void OnReceiveContentInfo(const ContentInfo &ci) override
{ {
if (this->downloading) return; if (this->downloading) return;
/* And once the metadata is received, start downloading it. */ /* And once the metadata is received, start downloading it. */
_network_content_client.Select(ci->id); _network_content_client.Select(ci.id);
_network_content_client.DownloadSelectedContent(this->total_files, this->total_bytes); _network_content_client.DownloadSelectedContent(this->total_files, this->total_bytes);
this->downloading = true; this->downloading = true;
EM_ASM({ if (window["openttd_bootstrap"]) openttd_bootstrap($0, $1); }, this->downloaded_bytes, this->total_bytes); EM_ASM({ if (window["openttd_bootstrap"]) openttd_bootstrap($0, $1); }, this->downloaded_bytes, this->total_bytes);
} }
void OnDownloadProgress(const ContentInfo *, int bytes) override void OnDownloadProgress(const ContentInfo &, int bytes) override
{ {
/* A negative value means we are resetting; for example, when retrying or using a fallback. */ /* A negative value means we are resetting; for example, when retrying or using a fallback. */
if (bytes < 0) { if (bytes < 0) {

View File

@ -205,7 +205,7 @@ public:
sprite_dim = maxdim(sprite_dim, GetScaledSpriteSize(bridge_data.spec->sprite)); sprite_dim = maxdim(sprite_dim, GetScaledSpriteSize(bridge_data.spec->sprite));
text_dim = maxdim(text_dim, GetStringBoundingBox(GetBridgeSelectString(bridge_data))); text_dim = maxdim(text_dim, GetStringBoundingBox(GetBridgeSelectString(bridge_data)));
} }
resize.height = std::max(sprite_dim.height, text_dim.height) + padding.height; // Max of both sizes + account for matrix edges. fill.height = resize.height = std::max(sprite_dim.height, text_dim.height) + padding.height; // Max of both sizes + account for matrix edges.
this->icon_width = sprite_dim.width; // Width of bridge icon. this->icon_width = sprite_dim.width; // Width of bridge icon.
size.width = this->icon_width + WidgetDimensions::scaled.hsep_normal + text_dim.width + padding.width; size.width = this->icon_width + WidgetDimensions::scaled.hsep_normal + text_dim.width + padding.width;
@ -239,7 +239,7 @@ public:
for (auto it = first; it != last; ++it) { for (auto it = first; it != last; ++it) {
const BridgeSpec *b = it->spec; const BridgeSpec *b = it->spec;
DrawSpriteIgnorePadding(b->sprite, b->pal, tr.WithWidth(this->icon_width, rtl), SA_HOR_CENTER | SA_BOTTOM); DrawSpriteIgnorePadding(b->sprite, b->pal, tr.WithWidth(this->icon_width, rtl), SA_HOR_CENTER | SA_BOTTOM);
DrawStringMultiLine(tr.Indent(this->icon_width + WidgetDimensions::scaled.hsep_normal, rtl), GetBridgeSelectString(*it)); DrawStringMultiLineWithClipping(tr.Indent(this->icon_width + WidgetDimensions::scaled.hsep_normal, rtl), GetBridgeSelectString(*it));
tr = tr.Translate(0, this->resize.step_height); tr = tr.Translate(0, this->resize.step_height);
} }
break; break;
@ -283,7 +283,7 @@ public:
} }
} }
void OnDropdownSelect(WidgetID widget, int index) override void OnDropdownSelect(WidgetID widget, int index, int) override
{ {
if (widget == WID_BBS_DROPDOWN_CRITERIA && this->bridges.SortType() != index) { if (widget == WID_BBS_DROPDOWN_CRITERIA && this->bridges.SortType() != index) {
this->bridges.SetSortType(index); this->bridges.SetSortType(index);

View File

@ -18,6 +18,8 @@
#include "company_func.h" #include "company_func.h"
#include "vehicle_gui.h" #include "vehicle_gui.h"
#include "newgrf_badge.h" #include "newgrf_badge.h"
#include "newgrf_badge_config.h"
#include "newgrf_badge_gui.h"
#include "newgrf_engine.h" #include "newgrf_engine.h"
#include "newgrf_text.h" #include "newgrf_text.h"
#include "group.h" #include "group.h"
@ -72,10 +74,13 @@ static constexpr NWidgetPart _nested_build_vehicle_widgets[] = {
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDDEN_ENGINES), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BV_SHOW_HIDDEN_ENGINES),
NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA), NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_BV_CARGO_FILTER_DROPDOWN), SetResize(1, 0), SetFill(1, 0), SetToolTip(STR_TOOLTIP_FILTER_CRITERIA),
NWidget(WWT_IMGBTN, COLOUR_GREY, WID_BV_CONFIGURE_BADGES), SetAspect(WidgetDimensions::ASPECT_UP_DOWN_BUTTON), SetResize(0, 0), SetFill(0, 1), SetSpriteTip(SPR_EXTRA_MENU, STR_BADGE_CONFIG_MENU_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(WWT_PANEL, COLOUR_GREY), NWidget(WWT_PANEL, COLOUR_GREY),
NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BV_FILTER), SetResize(1, 0), SetFill(1, 0), SetPadding(2), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), NWidget(WWT_EDITBOX, COLOUR_GREY, WID_BV_FILTER), SetResize(1, 0), SetFill(1, 0), SetPadding(2), SetStringTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP),
EndContainer(), EndContainer(),
NWidget(NWID_VERTICAL, NWidContainerFlag{}, WID_BV_BADGE_FILTER),
EndContainer(),
EndContainer(), EndContainer(),
/* Vehicle list. */ /* Vehicle list. */
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),
@ -795,16 +800,20 @@ static int DrawAircraftPurchaseInfo(int left, int right, int y, EngineID engine_
*/ */
static std::optional<std::string> GetNewGRFAdditionalText(EngineID engine) static std::optional<std::string> GetNewGRFAdditionalText(EngineID engine)
{ {
uint16_t callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, nullptr); std::array<int32_t, 16> regs100;
uint16_t callback = GetVehicleCallback(CBID_VEHICLE_ADDITIONAL_TEXT, 0, 0, engine, nullptr, regs100);
if (callback == CALLBACK_FAILED || callback == 0x400) return std::nullopt; if (callback == CALLBACK_FAILED || callback == 0x400) return std::nullopt;
const GRFFile *grffile = Engine::Get(engine)->GetGRF(); const GRFFile *grffile = Engine::Get(engine)->GetGRF();
assert(grffile != nullptr); assert(grffile != nullptr);
if (callback == 0x40F) {
return GetGRFStringWithTextStack(grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
}
if (callback > 0x400) { if (callback > 0x400) {
ErrorUnknownCallbackResult(grffile->grfid, CBID_VEHICLE_ADDITIONAL_TEXT, callback); ErrorUnknownCallbackResult(grffile->grfid, CBID_VEHICLE_ADDITIONAL_TEXT, callback);
return std::nullopt; return std::nullopt;
} }
return GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, 6); return GetGRFStringWithTextStack(grffile, GRFSTR_MISC_GRF_TEXT + callback, regs100);
} }
/** /**
@ -995,7 +1004,7 @@ void DrawEngineList(VehicleType type, const Rect &r, const GUIEngineList &eng_li
if (lvl < item.indent) tx += level_width; if (lvl < item.indent) tx += level_width;
} }
/* Draw our node in the tree. */ /* Draw our node in the tree. */
int ycentre = CenterBounds(textr.top, textr.bottom, WidgetDimensions::scaled.fullbevel.top); int ycentre = CentreBounds(textr.top, textr.bottom, WidgetDimensions::scaled.fullbevel.top);
if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ir.top, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top); if (!HasBit(item.level_mask, item.indent)) GfxDrawLine(tx, ir.top, tx, ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top); GfxDrawLine(tx, ycentre, tx + offset - (rtl ? -1 : 1), ycentre, linecolour, WidgetDimensions::scaled.fullbevel.top);
} }
@ -1122,11 +1131,6 @@ void GUIEngineListAddChildren(GUIEngineList &dst, const GUIEngineList &src, Engi
} }
} }
/** Enum referring to the Hotkeys in the build vehicle window */
enum BuildVehicleHotkeys : int32_t {
BVHK_FOCUS_FILTER_BOX, ///< Focus the edit box for editing the filter string
};
/** GUI for building vehicles. */ /** GUI for building vehicles. */
struct BuildVehicleWindow : Window { struct BuildVehicleWindow : Window {
VehicleType vehicle_type = VEH_INVALID; ///< Type of vehicles shown in the window. VehicleType vehicle_type = VEH_INVALID; ///< Type of vehicles shown in the window.
@ -1147,9 +1151,14 @@ struct BuildVehicleWindow : Window {
TestedEngineDetails te{}; ///< Tested cost and capacity after refit. TestedEngineDetails te{}; ///< Tested cost and capacity after refit.
GUIBadgeClasses badge_classes{}; GUIBadgeClasses badge_classes{};
static constexpr int BADGE_COLUMNS = 3; ///< Number of columns available for badges (0 = left of image, 1 = between image and name, 2 = after name)
StringFilter string_filter{}; ///< Filter for vehicle name StringFilter string_filter{}; ///< Filter for vehicle name
QueryString vehicle_editbox; ///< Filter editbox QueryString vehicle_editbox; ///< Filter editbox
std::pair<WidgetID, WidgetID> badge_filters{}; ///< First and last widgets IDs of badge filters.
BadgeFilterChoices badge_filter_choices{};
void SetBuyVehicleText() void SetBuyVehicleText()
{ {
NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_BUILD); NWidgetCore *widget = this->GetWidget<NWidgetCore>(WID_BV_BUILD);
@ -1305,6 +1314,12 @@ struct BuildVehicleWindow : Window {
{ {
this->badge_classes = GUIBadgeClasses(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type)); this->badge_classes = GUIBadgeClasses(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type));
this->SetCargoFilterArray(); this->SetCargoFilterArray();
auto container = this->GetWidget<NWidgetContainer>(WID_BV_BADGE_FILTER);
this->badge_filters = AddBadgeDropdownFilters(*container, WID_BV_BADGE_FILTER, COLOUR_GREY, static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type));
this->widget_lookup.clear();
this->nested_root->FillWidgetLookup(this->widget_lookup);
} }
/** Filter the engine list against the currently selected cargo filter */ /** Filter the engine list against the currently selected cargo filter */
@ -1352,6 +1367,7 @@ struct BuildVehicleWindow : Window {
list.clear(); list.clear();
BadgeTextFilter btf(this->string_filter, GSF_TRAINS); BadgeTextFilter btf(this->string_filter, GSF_TRAINS);
BadgeDropdownFilter bdf(this->badge_filter_choices);
/* Make list of all available train engines and wagons. /* Make list of all available train engines and wagons.
* Also check to see if the previously selected engine is still available, * Also check to see if the previously selected engine is still available,
@ -1368,6 +1384,8 @@ struct BuildVehicleWindow : Window {
/* Filter now! So num_engines and num_wagons is valid */ /* Filter now! So num_engines and num_wagons is valid */
if (!FilterSingleEngine(eid)) continue; if (!FilterSingleEngine(eid)) continue;
if (!bdf.Filter(e->badges)) continue;
/* Filter by name or NewGRF extra text */ /* Filter by name or NewGRF extra text */
if (!FilterByText(e) && !btf.Filter(e->badges)) continue; if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
@ -1573,6 +1591,12 @@ struct BuildVehicleWindow : Window {
return list; return list;
} }
DropDownList BuildBadgeConfigurationList() const
{
static const auto separators = {STR_BADGE_CONFIG_PREVIEW, STR_BADGE_CONFIG_NAME};
return BuildBadgeClassConfigurationList(this->badge_classes, BADGE_COLUMNS, separators);
}
void BuildVehicle() void BuildVehicle()
{ {
EngineID sel_eng = this->sel_engine; EngineID sel_eng = this->sel_engine;
@ -1656,6 +1680,11 @@ struct BuildVehicleWindow : Window {
ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria, widget); ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria, widget);
break; break;
case WID_BV_CONFIGURE_BADGES:
if (this->badge_classes.GetClasses().empty()) break;
ShowDropDownList(this, this->BuildBadgeConfigurationList(), -1, widget, 0, false, true);
break;
case WID_BV_SHOW_HIDE: { case WID_BV_SHOW_HIDE: {
const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine); const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine);
if (e != nullptr) { if (e != nullptr) {
@ -1676,6 +1705,12 @@ struct BuildVehicleWindow : Window {
} }
break; break;
} }
default:
if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
ShowDropDownList(this, this->GetWidget<NWidgetBadgeFilter>(widget)->GetDropDownList(), -1, widget, 0, false);
}
break;
} }
} }
@ -1726,6 +1761,10 @@ struct BuildVehicleWindow : Window {
} }
default: default:
if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
return this->GetWidget<NWidgetBadgeFilter>(widget)->GetStringParameter(this->badge_filter_choices);
}
return this->Window::GetWidgetString(widget, stringid); return this->Window::GetWidgetString(widget, stringid);
} }
} }
@ -1734,7 +1773,7 @@ struct BuildVehicleWindow : Window {
{ {
switch (widget) { switch (widget) {
case WID_BV_LIST: case WID_BV_LIST:
resize.height = GetEngineListHeight(this->vehicle_type); fill.height = resize.height = GetEngineListHeight(this->vehicle_type);
size.height = 3 * resize.height; size.height = 3 * resize.height;
size.width = std::max(size.width, this->badge_classes.GetTotalColumnsWidth() + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width; size.width = std::max(size.width, this->badge_classes.GetTotalColumnsWidth() + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_left + GetVehicleImageCellSize(this->vehicle_type, EIT_PURCHASE).extend_right + 165) + padding.width;
break; break;
@ -1755,6 +1794,11 @@ struct BuildVehicleWindow : Window {
size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width); size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width);
break; break;
case WID_BV_CONFIGURE_BADGES:
/* Hide the configuration button if no configurable badges are present. */
if (this->badge_classes.GetClasses().empty()) size = {0, 0};
break;
case WID_BV_BUILD: case WID_BV_BUILD:
size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type); size = GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_VEHICLE_BUTTON + this->vehicle_type);
size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type)); size = maxdim(size, GetStringBoundingBox(STR_BUY_VEHICLE_TRAIN_BUY_REFIT_VEHICLE_BUTTON + this->vehicle_type));
@ -1829,7 +1873,7 @@ struct BuildVehicleWindow : Window {
Command<CMD_RENAME_ENGINE>::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, *str); Command<CMD_RENAME_ENGINE>::Post(STR_ERROR_CAN_T_RENAME_TRAIN_TYPE + this->vehicle_type, this->rename_engine, *str);
} }
void OnDropdownSelect(WidgetID widget, int index) override void OnDropdownSelect(WidgetID widget, int index, int click_result) override
{ {
switch (widget) { switch (widget) {
case WID_BV_SORT_DROPDOWN: case WID_BV_SORT_DROPDOWN:
@ -1850,6 +1894,31 @@ struct BuildVehicleWindow : Window {
this->SelectEngine(this->sel_engine); this->SelectEngine(this->sel_engine);
} }
break; break;
case WID_BV_CONFIGURE_BADGES: {
bool reopen = HandleBadgeConfigurationDropDownClick(static_cast<GrfSpecFeature>(GSF_TRAINS + this->vehicle_type), BADGE_COLUMNS, index, click_result);
this->ReInit();
if (reopen) {
ReplaceDropDownList(this, this->BuildBadgeConfigurationList(), -1);
} else {
this->CloseChildWindows(WC_DROPDOWN_MENU);
}
break;
}
default:
if (IsInsideMM(widget, this->badge_filters.first, this->badge_filters.second)) {
if (index < 0) {
ResetBadgeFilter(this->badge_filter_choices, this->GetWidget<NWidgetBadgeFilter>(widget)->GetBadgeClassID());
} else {
SetBadgeFilter(this->badge_filter_choices, BadgeID(index));
}
this->eng_list.ForceRebuild();
this->SetDirty();
}
break;
} }
this->SetDirty(); this->SetDirty();
} }
@ -1867,23 +1936,8 @@ struct BuildVehicleWindow : Window {
} }
} }
EventState OnHotkey(int hotkey) override
{
switch (hotkey) {
case BVHK_FOCUS_FILTER_BOX:
this->SetFocusedWidget(WID_BV_FILTER);
SetFocusedWindow(this); // The user has asked to give focus to the text box, so make sure this window is focused.
return ES_HANDLED;
default:
return ES_NOT_HANDLED;
}
return ES_HANDLED;
}
static inline HotkeyList hotkeys{"buildvehicle", { static inline HotkeyList hotkeys{"buildvehicle", {
Hotkey('F', "focus_filter_box", BVHK_FOCUS_FILTER_BOX), Hotkey('F', "focus_filter_box", WID_BV_FILTER),
}}; }};
}; };

View File

@ -74,9 +74,8 @@ void CheckCaches()
for (const RoadStop *rs : RoadStop::Iterate()) { for (const RoadStop *rs : RoadStop::Iterate()) {
if (IsBayRoadStopTile(rs->xy)) continue; if (IsBayRoadStopTile(rs->xy)) continue;
assert(rs->GetEntry(DIAGDIR_NE) != rs->GetEntry(DIAGDIR_NW)); rs->GetEntry(DIAGDIR_NE).CheckIntegrity(rs);
rs->GetEntry(DIAGDIR_NE)->CheckIntegrity(rs); rs->GetEntry(DIAGDIR_NW).CheckIntegrity(rs);
rs->GetEntry(DIAGDIR_NW)->CheckIntegrity(rs);
} }
std::vector<NewGRFCache> grf_cache; std::vector<NewGRFCache> grf_cache;

View File

@ -18,5 +18,5 @@ Cheats _cheats;
/** Reinitialise all the cheats. */ /** Reinitialise all the cheats. */
void InitializeCheats() void InitializeCheats()
{ {
memset(&_cheats, 0, sizeof(Cheats)); _cheats = {};
} }

View File

@ -12,8 +12,6 @@
#include "cheat_type.h" #include "cheat_type.h"
extern Cheats _cheats;
void ShowCheatWindow(); void ShowCheatWindow();

View File

@ -35,6 +35,7 @@
#include "timer/timer.h" #include "timer/timer.h"
#include "timer/timer_game_calendar.h" #include "timer/timer_game_calendar.h"
#include "timer/timer_game_economy.h" #include "timer/timer_game_economy.h"
#include "core/string_consumer.hpp"
#include "widgets/cheat_widget.h" #include "widgets/cheat_widget.h"
@ -286,7 +287,7 @@ struct CheatWindow : Window {
case SLE_BOOL: { case SLE_BOOL: {
bool on = (*(bool*)ce->variable); bool on = (*(bool*)ce->variable);
DrawBoolButton(button_left, y + button_y_offset, on, true); DrawBoolButton(button_left, y + button_y_offset, COLOUR_YELLOW, COLOUR_GREY, on, true);
str = GetString(ce->str, on ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); str = GetString(ce->str, on ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
break; break;
} }
@ -354,7 +355,7 @@ struct CheatWindow : Window {
int32_t value = sd->Read(&GetGameSettings()); int32_t value = sd->Read(&GetGameSettings());
if (sd->IsBoolSetting()) { if (sd->IsBoolSetting()) {
/* Draw checkbox for boolean-value either on/off */ /* Draw checkbox for boolean-value either on/off */
DrawBoolButton(buttons.left, buttons.top, value != 0, editable); DrawBoolButton(buttons.left, buttons.top, COLOUR_YELLOW, COLOUR_GREY, value != 0, editable);
} else if (sd->flags.Test(SettingFlag::GuiDropdown)) { } else if (sd->flags.Test(SettingFlag::GuiDropdown)) {
/* Draw [v] button for settings of an enum-type */ /* Draw [v] button for settings of an enum-type */
DrawDropDownButton(buttons.left, buttons.top, COLOUR_YELLOW, state != 0, editable); DrawDropDownButton(buttons.left, buttons.top, COLOUR_YELLOW, state != 0, editable);
@ -607,12 +608,13 @@ struct CheatWindow : Window {
int32_t value; int32_t value;
if (!str->empty()) { if (!str->empty()) {
long long llvalue = atoll(str->c_str()); auto llvalue = ParseInteger<int64_t>(*str, 10, true);
if (!llvalue.has_value()) return;
/* Save the correct currency-translated value */ /* Save the correct currency-translated value */
if (sd->flags.Test(SettingFlag::GuiCurrency)) llvalue /= GetCurrency().rate; if (sd->flags.Test(SettingFlag::GuiCurrency)) llvalue = *llvalue / GetCurrency().rate;
value = ClampTo<int32_t>(llvalue); value = ClampTo<int32_t>(*llvalue);
} else { } else {
value = sd->GetDefaultValue(); value = sd->GetDefaultValue();
} }
@ -621,18 +623,19 @@ struct CheatWindow : Window {
} else { } else {
const CheatEntry *ce = &_cheats_ui[clicked_cheat]; const CheatEntry *ce = &_cheats_ui[clicked_cheat];
int oldvalue = static_cast<int32_t>(ReadValue(ce->variable, ce->type)); int oldvalue = static_cast<int32_t>(ReadValue(ce->variable, ce->type));
int value = atoi(str->c_str()); auto value = ParseInteger<int32_t>(*str, 10, true);
if (!value.has_value()) return;
*ce->been_used = true; *ce->been_used = true;
value = ce->proc(value, value - oldvalue); value = ce->proc(*value, *value - oldvalue);
if (value != oldvalue) WriteValue(ce->variable, ce->type, static_cast<int64_t>(value)); if (*value != oldvalue) WriteValue(ce->variable, ce->type, static_cast<int64_t>(*value));
} }
this->valuewindow_entry = nullptr; this->valuewindow_entry = nullptr;
this->SetDirty(); this->SetDirty();
} }
IntervalTimer<TimerGameCalendar> daily_interval = {{TimerGameCalendar::MONTH, TimerGameCalendar::Priority::NONE}, [this](auto) { const IntervalTimer<TimerGameCalendar> daily_interval = {{TimerGameCalendar::MONTH, TimerGameCalendar::Priority::NONE}, [this](auto) {
this->SetDirty(); this->SetDirty();
}}; }};
}; };

View File

@ -14,8 +14,8 @@
* Info about each of the cheats. * Info about each of the cheats.
*/ */
struct Cheat { struct Cheat {
bool been_used; ///< has this cheat been used before? bool been_used = false; ///< has this cheat been used before?
bool value; ///< tells if the bool cheat is active or not bool value = false; ///< tells if the bool cheat is active or not
}; };
/** /**
@ -24,15 +24,15 @@ struct Cheat {
* Only add new entries at the end of the struct! * Only add new entries at the end of the struct!
*/ */
struct Cheats { struct Cheats {
Cheat magic_bulldozer; ///< dynamite industries, objects Cheat magic_bulldozer{}; ///< dynamite industries, objects
Cheat switch_company; ///< change to another company Cheat switch_company{}; ///< change to another company
Cheat money; ///< get rich or poor Cheat money{}; ///< get rich or poor
Cheat crossing_tunnels; ///< allow tunnels that cross each other Cheat crossing_tunnels{}; ///< allow tunnels that cross each other
Cheat no_jetcrash; ///< no jet will crash on small airports anymore Cheat no_jetcrash{}; ///< no jet will crash on small airports anymore
Cheat change_date; ///< changes date ingame Cheat change_date{}; ///< changes date ingame
Cheat setup_prod; ///< setup raw-material production in game Cheat setup_prod{}; ///< setup raw-material production in game
Cheat edit_max_hl; ///< edit the maximum heightlevel; this is a cheat because of the fact that it needs to reset NewGRF game state and doing so as a simple configuration breaks the expectation of many Cheat edit_max_hl{}; ///< edit the maximum heightlevel; this is a cheat because of the fact that it needs to reset NewGRF game state and doing so as a simple configuration breaks the expectation of many
Cheat station_rating; ///< Fix station ratings at 100% Cheat station_rating{}; ///< Fix station ratings at 100%
}; };
extern Cheats _cheats; extern Cheats _cheats;

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