mirror of https://github.com/OpenTTD/OpenTTD
Merge branch 'master' into timetable-clear-all-fix
commit
7212fece8b
|
@ -50,7 +50,6 @@ Describe here
|
|||
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')
|
||||
* 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')
|
||||
* ai_changelog.hpp, game_changelog.hpp need updating.
|
||||
* The compatibility wrappers (compat_*.nut) need updating.
|
||||
|
|
|
@ -158,7 +158,7 @@ def scan_source_files(path, strings_found):
|
|||
# Most files we can just open, but some use magic, that requires the
|
||||
# G++ preprocessor before we can make sense out of it.
|
||||
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()
|
||||
else:
|
||||
with open(new_path) as fp:
|
||||
|
|
16
README.md
16
README.md
|
@ -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.
|
||||
|
||||
|
||||
## 1.2) OpenTTD gameplay manual
|
||||
|
||||
OpenTTD has a [community-maintained wiki](https://wiki.openttd.org/), including a gameplay manual and tips.
|
||||
|
||||
|
||||
## 1.3) Supported platforms
|
||||
|
||||
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.
|
||||
|
||||
### 1.3.1) Legacy support
|
||||
|
||||
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.
|
||||
|
||||
|
@ -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).
|
||||
|
||||
|
||||
### 1.4.1) Free graphics and sound files
|
||||
|
||||
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.
|
||||
The Windows installer can optionally download and install these packages.
|
||||
|
||||
|
||||
### 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.
|
||||
|
@ -100,7 +97,6 @@ You need to copy the following files:
|
|||
- trgir.grf or TRGI.GRF
|
||||
- trgtr.grf or TRGT.GRF
|
||||
|
||||
|
||||
### 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.
|
||||
|
@ -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
|
||||
- Transport Tycoon Original: The GM.CAT file, but rename it to GM-TTO.CAT
|
||||
|
||||
|
||||
## 1.5) Add-on content / mods
|
||||
|
||||
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).
|
||||
|
||||
|
||||
### 1.5.1) Social Integration
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### 1.6) OpenTTD directories
|
||||
|
||||
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).
|
||||
|
||||
|
||||
## 2.0) Contact and Community
|
||||
|
||||
'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.
|
||||
|
||||
|
||||
### 2.2) Contributing to OpenTTD
|
||||
|
||||
We welcome contributors to OpenTTD. More information for contributors can be found in [CONTRIBUTING.md](./CONTRIBUTING.md)
|
||||
|
||||
|
||||
### 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.
|
||||
|
@ -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).
|
||||
Instructions can be found in [debugging and reporting desyncs](./docs/debugging_desyncs.md).
|
||||
|
||||
|
||||
### 2.4) Translating
|
||||
|
||||
OpenTTD is translated into many languages. Translations are added and updated via the [online translation tool](https://translator.openttd.org).
|
||||
|
||||
|
||||
## 3.0) Licensing
|
||||
|
||||
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.
|
||||
See `cmake/3rdparty/llvm/LICENSE.txt` for the complete license text.
|
||||
|
||||
## 4.0 Credits
|
||||
## 4.0) Credits
|
||||
|
||||
See [CREDITS.md](./CREDITS.md)
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
GSBridge.GetBridgeID <- GSBridge.GetBridgeType;
|
||||
|
||||
/* Emulate old GSText parameter padding behaviour */
|
||||
GSText.SCRIPT_TEXT_MAX_PARAMETERS <- 20;
|
||||
|
||||
class GSCompat14 {
|
||||
function Text(text)
|
||||
{
|
||||
|
|
103
changelog.md
103
changelog.md
|
@ -1,5 +1,108 @@
|
|||
## 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)
|
||||
|
||||
- Feature: Town, industry and vehicle window zoom with mouse wheel (#12810, #12809, #12797)
|
||||
|
|
|
@ -17,41 +17,48 @@ if(NOT APILC)
|
|||
endif()
|
||||
|
||||
macro(dump_fileheader)
|
||||
get_filename_component(SCRIPT_API_FILE_NAME "${SCRIPT_API_FILE}" NAME)
|
||||
string(APPEND SQUIRREL_EXPORT "\n#include \"../${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}.hpp\"")
|
||||
if(NOT "${APIUC}" STREQUAL "Template")
|
||||
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()
|
||||
endmacro()
|
||||
|
||||
macro(dump_class_templates 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 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 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 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<${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\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\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")
|
||||
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")
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
string(APPEND SQUIRREL_EXPORT "\n template <> struct Param<Text *> {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n static inline Text *Get(HSQUIRRELVM vm, int index) {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_INSTANCE) {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n return Param<ScriptText *>::Get(vm, index);")
|
||||
string(APPEND SQUIRREL_EXPORT "\n }")
|
||||
string(APPEND SQUIRREL_EXPORT "\n if (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 }")
|
||||
string(APPEND SQUIRREL_EXPORT "\n if (sq_gettype(vm, index) == OT_NULL) {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n return nullptr;")
|
||||
string(APPEND SQUIRREL_EXPORT "\n }")
|
||||
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 }")
|
||||
string(APPEND SQUIRREL_EXPORT "\n };")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\ttemplate <> struct Param<Text *> {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\tstatic inline Text *Get(HSQUIRRELVM vm, int index) {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\tif (sq_gettype(vm, index) == OT_INSTANCE) {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\t\treturn Param<ScriptText *>::Get(vm, index);")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\t}")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\tif (sq_gettype(vm, index) == OT_STRING) {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\t\treturn new RawText(Param<const std::string &>::Get(vm, index));")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\t}")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\tif (sq_gettype(vm, index) == OT_NULL) {")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\t\treturn nullptr;")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t\t\t}")
|
||||
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\t\t}")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t};")
|
||||
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()
|
||||
endmacro()
|
||||
|
||||
|
@ -66,7 +73,6 @@ macro(reset_reader)
|
|||
unset(STATIC_METHODS)
|
||||
unset(CLS)
|
||||
unset(START_SQUIRREL_DEFINE_ON_NEXT_LINE)
|
||||
set(CLS_LEVEL 0)
|
||||
unset(CLS_IN_API)
|
||||
endmacro()
|
||||
|
||||
|
@ -75,6 +81,9 @@ reset_reader()
|
|||
file(STRINGS "${SCRIPT_API_FILE}" SOURCE_LINES)
|
||||
|
||||
set(NUM_LINE 0)
|
||||
set(CLS_LEVEL 0)
|
||||
set(BRACE_LEVEL 0)
|
||||
|
||||
macro(doxygen_check)
|
||||
if(NOT "${DOXYGEN_SKIP}" STREQUAL "")
|
||||
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()
|
||||
endif()
|
||||
|
||||
if("${LINE}" MATCHES "^([ ]*)\\* @api (.*)$")
|
||||
if("${LINE}" MATCHES "^([\t ]*)\\* @api (.*)$")
|
||||
set(LINE ${CMAKE_MATCH_2})
|
||||
# By default, classes are not selected
|
||||
if(NOT CLS_LEVEL)
|
||||
|
@ -148,22 +157,28 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
continue()
|
||||
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
|
||||
if("${LINE}" MATCHES "^( *)class(.*);")
|
||||
if("${LINE}" MATCHES "^(\t*)class(.*);")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
# 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 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)
|
||||
endif()
|
||||
unset(IS_PUBLIC)
|
||||
unset(CLS_PARAM_0)
|
||||
set(CLS_PARAM_1 1)
|
||||
set(CLS_PARAM_2 "x")
|
||||
unset(CLS_PARAMS)
|
||||
set(CLS_TYPES "x")
|
||||
set(CLS_IN_API ${API_SELECTED})
|
||||
unset(API_SELECTED)
|
||||
set(CLS "${CMAKE_MATCH_2}")
|
||||
|
@ -181,19 +196,19 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1")
|
||||
continue()
|
||||
endif()
|
||||
if("${LINE}" MATCHES "^( *)public")
|
||||
if("${LINE}" MATCHES "^(\t*)public")
|
||||
if(CLS_LEVEL EQUAL 1)
|
||||
set(IS_PUBLIC TRUE)
|
||||
endif()
|
||||
continue()
|
||||
endif()
|
||||
if("${LINE}" MATCHES "^( *)protected")
|
||||
if("${LINE}" MATCHES "^(\t*)protected")
|
||||
if(CLS_LEVEL EQUAL 1)
|
||||
unset(IS_PUBLIC)
|
||||
endif()
|
||||
continue()
|
||||
endif()
|
||||
if("${LINE}" MATCHES "^( *)private")
|
||||
if("${LINE}" MATCHES "^(\t*)private")
|
||||
if(CLS_LEVEL EQUAL 1)
|
||||
unset(IS_PUBLIC)
|
||||
endif()
|
||||
|
@ -221,7 +236,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
endif()
|
||||
|
||||
# We need to make specialized conversions for structs
|
||||
if("${LINE}" MATCHES "^( *)struct ([^ ]*)")
|
||||
if("${LINE}" MATCHES "^(\t*)struct ([^ ]*)")
|
||||
math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1")
|
||||
|
||||
# Check if we want to publish this struct
|
||||
|
@ -243,7 +258,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
endif()
|
||||
|
||||
# We need to make specialized conversions for enums
|
||||
if("${LINE}" MATCHES "^( *)enum ([^ ]*)")
|
||||
if("${LINE}" MATCHES "^(\t*)enum ([^ ]*)")
|
||||
math(EXPR CLS_LEVEL "${CLS_LEVEL} + 1")
|
||||
|
||||
# Check if we want to publish this enum
|
||||
|
@ -266,7 +281,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
endif()
|
||||
|
||||
# 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")
|
||||
if(CLS_LEVEL)
|
||||
unset(IN_ENUM)
|
||||
|
@ -280,7 +295,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
endif()
|
||||
|
||||
# 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)
|
||||
continue()
|
||||
endif()
|
||||
|
@ -304,33 +319,17 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
|
||||
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
|
||||
if(DEFINED STRUCTS)
|
||||
if(NOT NAMESPACE_OPENED)
|
||||
string(APPEND SQUIRREL_EXPORT "\nnamespace SQConvert {")
|
||||
set(NAMESPACE_OPENED TRUE)
|
||||
endif()
|
||||
string(APPEND SQUIRREL_EXPORT "\n /* Allow inner classes/structs to be used as Squirrel parameters */")
|
||||
open_namespace()
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t/* Allow inner classes/structs to be used as Squirrel parameters */")
|
||||
foreach(STRUCT IN LISTS STRUCTS)
|
||||
dump_class_templates(${STRUCT})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
if(NOT NAMESPACE_OPENED)
|
||||
string(APPEND SQUIRREL_EXPORT "\nnamespace SQConvert {")
|
||||
set(NAMESPACE_OPENED TRUE)
|
||||
else()
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
endif()
|
||||
string(APPEND SQUIRREL_EXPORT "\n /* Allow ${CLS} to be used as Squirrel parameter */")
|
||||
open_namespace()
|
||||
string(APPEND SQUIRREL_EXPORT "\n\t/* Allow ${CLS} to be used as Squirrel parameter */")
|
||||
dump_class_templates(${CLS})
|
||||
|
||||
string(APPEND SQUIRREL_EXPORT "\n} // namespace SQConvert")
|
||||
|
@ -340,23 +339,23 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
endif()
|
||||
|
||||
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")
|
||||
|
||||
# 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 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")
|
||||
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.PreRegister(engine);")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.PreRegister(engine);")
|
||||
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()
|
||||
if(NOT "${SUPER_CLS}" MATCHES "^ScriptEvent")
|
||||
if("${CLS_PARAM_2}" STREQUAL "v")
|
||||
string(APPEND SQUIRREL_EXPORT "\n SQ${API_CLS}.AddSQAdvancedConstructor(engine);")
|
||||
if((DEFINED CLS_PARAMS OR DEFINED METHODS) AND NOT "${SUPER_CLS}" MATCHES "^ScriptEvent" AND NOT "${CLS}" STREQUAL "ScriptEvent")
|
||||
if("${CLS_TYPES}" STREQUAL "v")
|
||||
string(APPEND SQUIRREL_EXPORT "\n\tSQ${API_CLS}.AddSQAdvancedConstructor(engine);")
|
||||
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()
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
|
@ -376,7 +375,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
foreach(i RANGE ${LEN})
|
||||
string(APPEND SPACES " ")
|
||||
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()
|
||||
if(MLEN)
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
|
@ -397,7 +396,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
foreach(i RANGE ${LEN})
|
||||
string(APPEND SPACES " ")
|
||||
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()
|
||||
if(MLEN)
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
|
@ -423,7 +422,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
foreach(i RANGE ${LEN})
|
||||
string(APPEND SPACES " ")
|
||||
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()
|
||||
if(MLEN)
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
|
@ -444,7 +443,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
foreach(i RANGE ${LEN})
|
||||
string(APPEND SPACES " ")
|
||||
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()
|
||||
if(MLEN)
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
|
@ -463,8 +462,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
foreach(STATIC_METHOD IN LISTS STATIC_METHODS)
|
||||
string(REPLACE ":" ";" STATIC_METHOD "${STATIC_METHOD}")
|
||||
list(GET STATIC_METHOD 0 FUNCNAME)
|
||||
list(GET STATIC_METHOD 1 ARGC)
|
||||
list(GET STATIC_METHOD 2 TYPES)
|
||||
list(GET STATIC_METHOD 1 TYPES)
|
||||
string(LENGTH "${FUNCNAME}" LEN)
|
||||
math(EXPR LEN "${MLEN} - ${LEN}")
|
||||
if("${TYPES}" STREQUAL "v")
|
||||
|
@ -479,9 +477,9 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
string(APPEND SPACES " ")
|
||||
endforeach()
|
||||
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()
|
||||
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()
|
||||
endforeach()
|
||||
if(MLEN)
|
||||
|
@ -501,8 +499,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
foreach(METHOD IN LISTS METHODS)
|
||||
string(REPLACE ":" ";" METHOD "${METHOD}")
|
||||
list(GET METHOD 0 FUNCNAME)
|
||||
list(GET METHOD 1 ARGC)
|
||||
list(GET METHOD 2 TYPES)
|
||||
list(GET METHOD 1 TYPES)
|
||||
string(LENGTH "${FUNCNAME}" LEN)
|
||||
math(EXPR LEN "${MLEN} - ${LEN}")
|
||||
if("${TYPES}" STREQUAL "v")
|
||||
|
@ -517,16 +514,16 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
string(APPEND SPACES " ")
|
||||
endforeach()
|
||||
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()
|
||||
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()
|
||||
endforeach()
|
||||
if(MLEN)
|
||||
string(APPEND SQUIRREL_EXPORT "\n")
|
||||
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}")
|
||||
|
||||
reset_reader()
|
||||
|
@ -539,9 +536,13 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
continue()
|
||||
endif()
|
||||
|
||||
if(NOT BRACE_LEVEL EQUAL CLS_LEVEL)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
# Add enums
|
||||
if(IN_ENUM)
|
||||
string(REGEX MATCH "([^, ]+)" ENUM_VALUE "${LINE}")
|
||||
string(REGEX MATCH "([^,\t ]+)" ENUM_VALUE "${LINE}")
|
||||
list(APPEND ENUM_VALUES "${ENUM_VALUE}")
|
||||
|
||||
# Check if this a special error enum
|
||||
|
@ -549,12 +550,12 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
if("${ENUM}" MATCHES ".*::ErrorMessages")
|
||||
# syntax:
|
||||
# enum ErrorMessages {
|
||||
# ERR_SOME_ERROR, // [STR_ITEM1, STR_ITEM2, ...]
|
||||
#\tERR_SOME_ERROR,\t// [STR_ITEM1, STR_ITEM2, ...]
|
||||
# }
|
||||
|
||||
# Set the mappings
|
||||
if("${LINE}" MATCHES "\\[(.*)\\]")
|
||||
string(REGEX REPLACE "[ ]" "" MAPPINGS "${CMAKE_MATCH_1}")
|
||||
string(REGEX REPLACE "[ \t]" "" MAPPINGS "${CMAKE_MATCH_1}")
|
||||
string(REPLACE "," ";" MAPPINGS "${MAPPINGS}")
|
||||
|
||||
foreach(MAPPING IN LISTS MAPPINGS)
|
||||
|
@ -568,11 +569,11 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
endif()
|
||||
|
||||
# Add a const (non-enum) value
|
||||
if("${LINE}" MATCHES "^[ ]*static const [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;")
|
||||
if("${LINE}" MATCHES "^[ \t]*static const [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;")
|
||||
list(APPEND CONST_VALUES "${CMAKE_MATCH_1}")
|
||||
continue()
|
||||
endif()
|
||||
if("${LINE}" MATCHES "^[ ]*static constexpr [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;")
|
||||
if("${LINE}" MATCHES "^[ \t]*static constexpr [^ ]+ ([^ ]+) = -?\\(?[^ ]*\\)?[^ ]+;")
|
||||
list(APPEND CONST_VALUES "${CMAKE_MATCH_1}")
|
||||
continue()
|
||||
endif()
|
||||
|
@ -584,41 +585,43 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
endif()
|
||||
if("${LINE}" MATCHES "~")
|
||||
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)
|
||||
endif()
|
||||
continue()
|
||||
endif()
|
||||
|
||||
unset(IS_STATIC)
|
||||
if("${LINE}" MATCHES "static")
|
||||
if("${LINE}" MATCHES "static ")
|
||||
set(IS_STATIC TRUE)
|
||||
endif()
|
||||
|
||||
string(REGEX REPLACE "(virtual|static|const)[ ]+" "" LINE "${LINE}")
|
||||
string(REGEX REPLACE "(virtual|static|const)[ \t]+" "" LINE "${LINE}")
|
||||
string(REGEX REPLACE "{.*" "" LINE "${LINE}")
|
||||
set(PARAM_S "${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 MATCH "([^ ]+)( ([^ ]+))?" RESULT "${LINE}")
|
||||
string(REGEX MATCH "([^ \t]+)( ([^ ]+))?" RESULT "${LINE}")
|
||||
set(FUNCTYPE "${CMAKE_MATCH_1}")
|
||||
set(FUNCNAME "${CMAKE_MATCH_3}")
|
||||
if("${FUNCTYPE}" STREQUAL "${CLS}" AND NOT FUNCNAME)
|
||||
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)
|
||||
endif()
|
||||
set(CLS_PARAM_0 "${PARAM_S}")
|
||||
set(CLS_PARAMS "${PARAM_S}")
|
||||
if(NOT PARAM_S)
|
||||
continue()
|
||||
endif()
|
||||
elseif(NOT FUNCNAME)
|
||||
continue()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(REPLACE "," ";" PARAMS "${PARAM_S}")
|
||||
if(IS_STATIC)
|
||||
|
@ -627,9 +630,7 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
set(TYPES "x")
|
||||
endif()
|
||||
|
||||
set(LEN 1)
|
||||
foreach(PARAM IN LISTS PARAMS)
|
||||
math(EXPR LEN "${LEN} + 1")
|
||||
string(STRIP "${PARAM}" PARAM)
|
||||
if("${PARAM}" MATCHES "\\*|&")
|
||||
if("${PARAM}" MATCHES "^char")
|
||||
|
@ -668,13 +669,12 @@ foreach(LINE IN LISTS SOURCE_LINES)
|
|||
unset(API_SELECTED)
|
||||
|
||||
if("${FUNCTYPE}" STREQUAL "${CLS}" AND NOT FUNCNAME)
|
||||
set(CLS_PARAM_1 ${LEN})
|
||||
set(CLS_PARAM_2 "${TYPES}")
|
||||
set(CLS_TYPES "${TYPES}")
|
||||
elseif("${FUNCNAME}" MATCHES "^_" AND NOT "${TYPES}" STREQUAL "v")
|
||||
elseif(IS_STATIC)
|
||||
list(APPEND STATIC_METHODS "${FUNCNAME}:${LEN}:${TYPES}")
|
||||
list(APPEND STATIC_METHODS "${FUNCNAME}:${TYPES}")
|
||||
else()
|
||||
list(APPEND METHODS "${FUNCNAME}:${LEN}:${TYPES}")
|
||||
list(APPEND METHODS "${FUNCNAME}:${TYPES}")
|
||||
endif()
|
||||
continue()
|
||||
endif()
|
||||
|
|
|
@ -19,7 +19,7 @@ endif()
|
|||
file(READ "${API_FILES}" 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)
|
||||
string(REGEX REPLACE ".*api/${APILC}/(.*)" "#include \"\\1\"" FILE "${FILE}")
|
||||
list(APPEND SQUIRREL_INCLUDES "${FILE}")
|
||||
|
@ -28,7 +28,7 @@ foreach(FILE IN LISTS SCRIPT_API_BINARY_FILES)
|
|||
continue()
|
||||
endif()
|
||||
string(REGEX REPLACE "^.*void " " " LINE "${LINE}")
|
||||
string(REGEX REPLACE "Squirrel \\*" "" LINE "${LINE}")
|
||||
string(REGEX REPLACE "Squirrel &" "" LINE "${LINE}")
|
||||
list(APPEND SQUIRREL_REGISTER "${LINE}")
|
||||
endforeach()
|
||||
endif()
|
||||
|
|
|
@ -16,8 +16,11 @@
|
|||
; - `openttd -I <name>` starts OpenTTD with the given set (case sensitive)
|
||||
; - adding `graphicsset = <name>` to the misc section of openttd.cfg makes
|
||||
; OpenTTD start with that graphics set by default
|
||||
; - there is a command line tool for all platforms called md5sum that can
|
||||
; create the MD5 checksum you need.
|
||||
; - `grfid -m` can give the GRF file MD5 checksums that 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
|
||||
; 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
|
||||
|
@ -44,6 +47,8 @@ description.en_US = howdie
|
|||
palette = DOS
|
||||
; preferred blitter, optional; either 8bpp (default) or 32bpp.
|
||||
blitter = 8bpp
|
||||
; url, optional
|
||||
url = https://github.com/my/baseset
|
||||
|
||||
; The files section lists the files that replace sprites.
|
||||
; The file names are case sensitive.
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
## Table of contents
|
||||
|
||||
- 1.0) About
|
||||
- 2.0) Known bugs
|
||||
- 1.0) [About](#10-about)
|
||||
- 2.0) [Known bugs](#20-known-bugs)
|
||||
|
||||
## 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!
|
||||
|
||||
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
|
||||
might have fixed the bug in the mean time.
|
||||
|
||||
|
|
Binary file not shown.
|
@ -1 +1 @@
|
|||
019dba4830a64ee4345d3d647633e1da
|
||||
a4a727b03a7cd07ee0499231f7f233f4
|
||||
|
|
|
@ -840,3 +840,7 @@
|
|||
-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 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 |
|
@ -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/>.
|
||||
//
|
||||
-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 146 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 581 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 |
|
@ -24,7 +24,7 @@ if (!$Env:AZURE_CODESIGN_ENDPOINT -or !$Env:AZURE_CODESIGN_ACCOUNT_NAME -or !$En
|
|||
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 = @{}
|
||||
|
||||
|
@ -37,4 +37,4 @@ $params["FileDigest"] = "SHA256"
|
|||
$params["TimestampRfc3161"] = "http://timestamp.acs.microsoft.com"
|
||||
$params["TimestampDigest"] = "SHA256"
|
||||
|
||||
Invoke-AzureCodeSigning @params
|
||||
Invoke-TrustedSigning @params
|
||||
|
|
|
@ -17,6 +17,10 @@ function Regression::TestInit()
|
|||
print(" IsValid(vehicle.plane_speed): " + AIGameSettings.IsValid("vehicle.plane_speed"));
|
||||
print(" vehicle.plane_speed: " + AIGameSettings.GetValue("vehicle.plane_speed"));
|
||||
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(3, 6): " + min(3, 6));
|
||||
print(" max(6, 3): " + max(6, 3));
|
||||
|
@ -824,6 +828,13 @@ function Regression::List()
|
|||
print(" []:");
|
||||
print(" 4000 => " + list[4000]);
|
||||
|
||||
print(" clone:");
|
||||
local list3 = clone list;
|
||||
print(" Clone ListDump:");
|
||||
foreach (idx, val in list3) {
|
||||
print(" " + idx + " => " + val);
|
||||
}
|
||||
|
||||
list.Clear();
|
||||
print(" IsEmpty(): " + list.IsEmpty());
|
||||
|
||||
|
@ -856,6 +867,12 @@ function Regression::List()
|
|||
it = list.Next();
|
||||
print(" " + it + " => " + list.GetValue(it));
|
||||
}
|
||||
|
||||
print(" Clone ListDump:");
|
||||
foreach (idx, val in list3) {
|
||||
print(" " + idx + " => " + val);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function Regression::Map()
|
||||
|
@ -1017,6 +1034,28 @@ function Regression::Order()
|
|||
foreach (idx, val in list) {
|
||||
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()
|
||||
|
@ -1675,13 +1714,22 @@ function Regression::TownList()
|
|||
}
|
||||
|
||||
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(" 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(" 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()
|
||||
|
@ -1975,6 +2023,33 @@ function Regression::Math()
|
|||
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()
|
||||
{
|
||||
this.TestInit();
|
||||
|
@ -2049,6 +2124,20 @@ function Regression::Start()
|
|||
print(" PresidentName: " + c.GetNewName());
|
||||
} 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:
|
||||
print(" Unknown Event");
|
||||
break;
|
||||
|
@ -2057,12 +2146,18 @@ function Regression::Start()
|
|||
print(" IsEventWaiting: false");
|
||||
|
||||
this.Math();
|
||||
this.PriorityQueue();
|
||||
|
||||
/* Check Valuate() is actually limited, MUST BE THE LAST TEST. */
|
||||
print("--Valuate() with excessive CPU usage--")
|
||||
local list = AIList();
|
||||
list.AddItem(0, 0);
|
||||
local Infinite = function(id) { while(true); }
|
||||
try {
|
||||
list = AIIndustryList(Infinite);
|
||||
} catch (e) {
|
||||
print("constructor failed with: " + e);
|
||||
}
|
||||
list.Valuate(Infinite);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,9 @@
|
|||
print(" Required this file");
|
||||
|
||||
const test_constant = 1;
|
||||
|
||||
enum TestEnum {
|
||||
value0,
|
||||
value1,
|
||||
value2
|
||||
};
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
IsValid(vehicle.plane_speed): true
|
||||
vehicle.plane_speed: 2
|
||||
Required this file
|
||||
TestEnum.value1: 1
|
||||
test_constant: 1
|
||||
TestEnum.value2: 2
|
||||
test_constant: 1
|
||||
min(6, 3): 3
|
||||
min(3, 6): 3
|
||||
max(6, 3): 6
|
||||
|
@ -81,7 +85,7 @@
|
|||
20
|
||||
30
|
||||
40
|
||||
Ops: 8673
|
||||
Ops: 8649
|
||||
|
||||
--Std--
|
||||
abs(-21): 21
|
||||
|
@ -571,6 +575,13 @@
|
|||
4006 => 12
|
||||
[]:
|
||||
4000 => 50
|
||||
clone:
|
||||
Clone ListDump:
|
||||
1005 => 1005
|
||||
4000 => 50
|
||||
4001 => 8002
|
||||
4002 => 8004
|
||||
4006 => 12
|
||||
IsEmpty(): true
|
||||
0 => 5 (true)
|
||||
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)
|
||||
3 => 6 (true)
|
||||
9 => 0 (false)
|
||||
Clone ListDump:
|
||||
1005 => 1005
|
||||
4000 => 50
|
||||
4001 => 8002
|
||||
4002 => 8004
|
||||
4006 => 12
|
||||
|
||||
--Company--
|
||||
SetName(): true
|
||||
|
@ -9423,13 +9440,22 @@ ERROR: IsEnd() is invalid as Begin() is never called
|
|||
23 => 652
|
||||
25 => 563
|
||||
HasStatue(): false
|
||||
GetRoadReworkDuration(): 0
|
||||
GetExclusiveRightsCompany(): -1
|
||||
GetExclusiveRightsDuration(): 0
|
||||
IsActionAvailable(BUILD_STATUE): true
|
||||
PerformTownAction(BUILD_STATUE): true
|
||||
IsActionAvailable(BUILD_STATUE): false
|
||||
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--
|
||||
IsTunnelTile(): false
|
||||
|
@ -9694,6 +9720,14 @@ ERROR: IsEnd() is invalid as Begin() is never called
|
|||
GetStopLocation(): 1
|
||||
|
||||
--VehicleList_Station--
|
||||
Count(): 1
|
||||
Location ListDump:
|
||||
20 => 23596
|
||||
foreach():
|
||||
20 => 23596
|
||||
Count(): 0
|
||||
Location ListDump:
|
||||
foreach():
|
||||
Count(): 1
|
||||
Location ListDump:
|
||||
20 => 23596
|
||||
|
@ -9726,6 +9760,16 @@ ERROR: IsEnd() is invalid as Begin() is never called
|
|||
EventName: CompanyRenamed
|
||||
CompanyID: 1
|
||||
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
|
||||
|
||||
--Math--
|
||||
|
@ -9760,21 +9804,38 @@ ERROR: IsEnd() is invalid as Begin() is never called
|
|||
-1 > 2147483647: false
|
||||
-2147483648 > 2147483647: false
|
||||
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--
|
||||
constructor failed with: excessive CPU usage in list filter function
|
||||
Your script made an error: excessive CPU usage in valuator function
|
||||
|
||||
*FUNCTION [unknown()] regression/main.nut line [2065]
|
||||
*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]
|
||||
*FUNCTION [Start()] regression/main.nut line [2161]
|
||||
|
||||
[Infinite] CLOSURE
|
||||
[list] INSTANCE
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <unicode/utypes.h>
|
||||
#include <unicode/uobject.h>
|
||||
#include <unicode/uscript.h>
|
||||
#include <array>
|
||||
|
||||
U_NAMESPACE_BEGIN
|
||||
|
||||
|
|
|
@ -281,7 +281,7 @@ void Md5::Append(const void *data, const size_t nbytes)
|
|||
if (offset) {
|
||||
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;
|
||||
|
||||
|
@ -294,7 +294,7 @@ void Md5::Append(const void *data, const size_t nbytes)
|
|||
for (; left >= 64; p += 64, left -= 64) this->Process(p);
|
||||
|
||||
/* 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)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
add_files(
|
||||
sqstdaux.h
|
||||
sqstdmath.h
|
||||
sqstdstring.h
|
||||
squirrel.h
|
||||
)
|
||||
|
|
|
@ -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_*/
|
|
@ -68,7 +68,6 @@ struct SQClass;
|
|||
struct SQInstance;
|
||||
struct SQDelegable;
|
||||
|
||||
typedef char SQChar;
|
||||
#define MAX_CHAR 0xFFFF
|
||||
|
||||
#define SQUIRREL_VERSION "Squirrel 2.2.5 stable - With custom OpenTTD modifications"
|
||||
|
@ -164,17 +163,17 @@ typedef struct tagSQObject
|
|||
}SQObject;
|
||||
|
||||
typedef struct tagSQStackInfos{
|
||||
const SQChar* funcname;
|
||||
const SQChar* source;
|
||||
SQInteger line;
|
||||
std::string_view funcname;
|
||||
std::string_view source;
|
||||
SQInteger line = -1;
|
||||
}SQStackInfos;
|
||||
|
||||
typedef struct SQVM* HSQUIRRELVM;
|
||||
typedef SQObject HSQOBJECT;
|
||||
typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM);
|
||||
typedef SQInteger (*SQRELEASEHOOK)(SQUserPointer,SQInteger size);
|
||||
typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,const SQChar * /*desc*/,const SQChar * /*source*/,SQInteger /*line*/,SQInteger /*column*/);
|
||||
typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,const std::string &);
|
||||
typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,std::string_view /*desc*/,std::string_view /*source*/,SQInteger /*line*/,SQInteger /*column*/);
|
||||
typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,std::string_view);
|
||||
|
||||
typedef SQInteger (*SQWRITEFUNC)(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 struct tagSQRegFunction{
|
||||
const SQChar *name;
|
||||
std::string_view name;
|
||||
SQFUNCTION f;
|
||||
SQInteger nparamscheck;
|
||||
const SQChar *typemask;
|
||||
std::optional<std::string_view> typemask;
|
||||
}SQRegFunction;
|
||||
|
||||
typedef struct tagSQFunctionInfo {
|
||||
SQUserPointer funcid;
|
||||
const SQChar *name;
|
||||
const SQChar *source;
|
||||
std::string_view name;
|
||||
std::string_view source;
|
||||
}SQFunctionInfo;
|
||||
|
||||
|
||||
|
@ -213,8 +212,8 @@ SQInteger sq_getvmstate(HSQUIRRELVM v);
|
|||
void sq_decreaseops(HSQUIRRELVM v, int amount);
|
||||
|
||||
/*compiler*/
|
||||
SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror);
|
||||
SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,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,std::string_view buffer,std::string_view sourcename,SQBool raiseerror);
|
||||
void sq_enabledebuginfo(HSQUIRRELVM v, SQBool enable);
|
||||
void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable);
|
||||
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_newarray(HSQUIRRELVM v,SQInteger size);
|
||||
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);
|
||||
void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len);
|
||||
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_pushstring(HSQUIRRELVM v, std::string_view str);
|
||||
void sq_pushfloat(HSQUIRRELVM v,SQFloat f);
|
||||
void sq_pushinteger(HSQUIRRELVM v,SQInteger n);
|
||||
void sq_pushbool(HSQUIRRELVM v,SQBool b);
|
||||
|
@ -250,7 +248,7 @@ SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx);
|
|||
SQBool sq_instanceof(HSQUIRRELVM v);
|
||||
void sq_tostring(HSQUIRRELVM v,SQInteger idx);
|
||||
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_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f);
|
||||
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_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag);
|
||||
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_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_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag);
|
||||
SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize);
|
||||
|
@ -305,10 +303,9 @@ SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx);
|
|||
/*calls*/
|
||||
SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror, int suspend = -1);
|
||||
SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool raiseerror);
|
||||
const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx);
|
||||
const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
|
||||
SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err, SQInteger len = -1);
|
||||
inline SQRESULT sq_throwerror(HSQUIRRELVM v, const std::string_view err) { return sq_throwerror(v, err.data(), err.size()); }
|
||||
std::optional<std::string_view> sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx);
|
||||
std::optional<std::string_view> sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval);
|
||||
SQRESULT sq_throwerror(HSQUIRRELVM v,std::string_view err);
|
||||
void sq_reseterror(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);
|
||||
SQBool sq_release(HSQUIRRELVM v,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);
|
||||
SQInteger sq_objtointeger(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 */
|
||||
struct SQOpsLimiter {
|
||||
SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, const char *label);
|
||||
SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, std::string_view label);
|
||||
~SQOpsLimiter();
|
||||
private:
|
||||
HSQUIRRELVM _v;
|
||||
|
|
|
@ -16,28 +16,22 @@ void sqstd_printcallstack(HSQUIRRELVM v)
|
|||
SQInteger i;
|
||||
SQBool b;
|
||||
SQFloat f;
|
||||
const SQChar *s;
|
||||
SQInteger level=1; //1 is to skip this function that is level 0
|
||||
const SQChar *name=nullptr;
|
||||
SQInteger seq=0;
|
||||
pf(v,"\nCALLSTACK\n");
|
||||
while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si)))
|
||||
{
|
||||
const SQChar *fn="unknown";
|
||||
const SQChar *src="unknown";
|
||||
if(si.funcname)fn=si.funcname;
|
||||
if(si.source) {
|
||||
std::string_view fn="unknown";
|
||||
std::string_view src="unknown";
|
||||
if(!si.funcname.empty())fn=si.funcname;
|
||||
if(!si.source.empty()) {
|
||||
/* 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
|
||||
* to strip it here. Let's hope nobody installs openttd in a subdirectory
|
||||
* of a directory named /ai/. */
|
||||
src = strstr(si.source, "\\ai\\");
|
||||
if (!src) src = strstr(si.source, "/ai/");
|
||||
if (src) {
|
||||
src += 4;
|
||||
} else {
|
||||
src = si.source;
|
||||
}
|
||||
auto p = si.source.find("\\ai\\");
|
||||
if (p == std::string_view::npos) p = si.source.find("/ai/");
|
||||
src = (p == std::string_view::npos) ? si.source : si.source.substr(p + 4);
|
||||
}
|
||||
pf(v,fmt::format("*FUNCTION [{}()] {} line [{}]\n",fn,src,si.line));
|
||||
level++;
|
||||
|
@ -47,8 +41,9 @@ void sqstd_printcallstack(HSQUIRRELVM v)
|
|||
|
||||
for(level=0;level<10;level++){
|
||||
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++;
|
||||
switch(sq_gettype(v,-1))
|
||||
{
|
||||
|
@ -66,10 +61,12 @@ void sqstd_printcallstack(HSQUIRRELVM v)
|
|||
case OT_USERPOINTER:
|
||||
pf(v,fmt::format("[{}] USERPOINTER\n",name));
|
||||
break;
|
||||
case OT_STRING:
|
||||
sq_getstring(v,-1,&s);
|
||||
pf(v,fmt::format("[{}] \"{}\"\n",name,s));
|
||||
case OT_STRING: {
|
||||
std::string_view view;
|
||||
sq_getstring(v,-1,view);
|
||||
pf(v,fmt::format("[{}] \"{}\"\n",name,view));
|
||||
break;
|
||||
}
|
||||
case OT_TABLE:
|
||||
pf(v,fmt::format("[{}] TABLE\n",name));
|
||||
break;
|
||||
|
@ -117,10 +114,10 @@ static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
|
|||
{
|
||||
SQPRINTFUNCTION pf = sq_getprintfunc(v);
|
||||
if(pf) {
|
||||
const SQChar *sErr = nullptr;
|
||||
std::string_view error;
|
||||
if(sq_gettop(v)>=1) {
|
||||
if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) {
|
||||
pf(v,fmt::format("\nAN ERROR HAS OCCURRED [{}]\n",sErr));
|
||||
if(SQ_SUCCEEDED(sq_getstring(v,2,error))) {
|
||||
pf(v,fmt::format("\nAN ERROR HAS OCCURRED [{}]\n",error));
|
||||
}
|
||||
else{
|
||||
pf(v,"\nAN ERROR HAS OCCURRED [unknown]\n");
|
||||
|
@ -131,7 +128,7 @@ static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v)
|
|||
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);
|
||||
if(pf) {
|
||||
|
|
|
@ -67,7 +67,7 @@ SINGLE_ARG_FUNC(ceil, 1)
|
|||
SINGLE_ARG_FUNC(exp, 100)
|
||||
|
||||
#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(sin,2,".n"),
|
||||
_DECL_FUNC(cos,2,".n"),
|
||||
|
@ -84,11 +84,10 @@ static SQRegFunction mathlib_funcs[] = {
|
|||
_DECL_FUNC(exp,2,".n"),
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
_DECL_FUNC(srand,2,".n"),
|
||||
_DECL_FUNC(rand,1,nullptr),
|
||||
_DECL_FUNC(rand,1,std::nullopt),
|
||||
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
|
||||
_DECL_FUNC(fabs,2,".n"),
|
||||
_DECL_FUNC(abs,2,".n"),
|
||||
{nullptr,nullptr,0,nullptr},
|
||||
};
|
||||
|
||||
#ifndef M_PI
|
||||
|
@ -97,21 +96,19 @@ static SQRegFunction mathlib_funcs[] = {
|
|||
|
||||
SQRESULT sqstd_register_mathlib(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger i=0;
|
||||
while(mathlib_funcs[i].name!=nullptr) {
|
||||
sq_pushstring(v,mathlib_funcs[i].name,-1);
|
||||
sq_newclosure(v,mathlib_funcs[i].f,0);
|
||||
sq_setparamscheck(v,mathlib_funcs[i].nparamscheck,mathlib_funcs[i].typemask);
|
||||
sq_setnativeclosurename(v,-1,mathlib_funcs[i].name);
|
||||
for(auto &func : mathlib_funcs) {
|
||||
sq_pushstring(v,func.name);
|
||||
sq_newclosure(v,func.f,0);
|
||||
sq_setparamscheck(v,func.nparamscheck,func.typemask);
|
||||
sq_setnativeclosurename(v,-1,func.name);
|
||||
sq_createslot(v,-3);
|
||||
i++;
|
||||
}
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
sq_pushstring(v,"RAND_MAX",-1);
|
||||
sq_pushstring(v,"RAND_MAX");
|
||||
sq_pushinteger(v,RAND_MAX);
|
||||
sq_createslot(v,-3);
|
||||
#endif /* EXPORT_DEFAULT_SQUIRREL_FUNCTIONS */
|
||||
sq_pushstring(v,"PI",-1);
|
||||
sq_pushstring(v,"PI");
|
||||
sq_pushfloat(v,(SQFloat)M_PI);
|
||||
sq_createslot(v,-3);
|
||||
return SQ_OK;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
#include "sqfuncstate.h"
|
||||
#include "sqclass.h"
|
||||
|
||||
#include "../../../core/string_consumer.hpp"
|
||||
#include "../../../string_func.h"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
@ -131,7 +132,7 @@ void sq_close(HSQUIRRELVM v)
|
|||
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;
|
||||
if(Compile(v, read, p, sourcename, o, raiseerror != 0, _ss(v)->_debuginfo)) {
|
||||
|
@ -172,12 +173,12 @@ SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po)
|
|||
#endif
|
||||
}
|
||||
|
||||
const SQChar *sq_objtostring(HSQOBJECT *o)
|
||||
std::optional<std::string_view> sq_objtostring(HSQOBJECT *o)
|
||||
{
|
||||
if(sq_type(*o) == OT_STRING) {
|
||||
return _stringval(*o);
|
||||
}
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
SQInteger sq_objtointeger(HSQOBJECT *o)
|
||||
|
@ -209,11 +210,9 @@ void sq_pushnull(HSQUIRRELVM v)
|
|||
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, len)));
|
||||
else v->Push(_null_);
|
||||
v->Push(SQObjectPtr(SQString::Create(_ss(v), s)));
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
if(!sq_isnativeclosure(o))
|
||||
return sq_throwerror(v, "native closure expected");
|
||||
SQNativeClosure *nc = _nativeclosure(o);
|
||||
nc->_nparamscheck = nparamscheck;
|
||||
if(typemask) {
|
||||
if(typemask.has_value()) {
|
||||
SQIntVec res;
|
||||
if(!CompileTypemask(res, typemask))
|
||||
if(!CompileTypemask(res, *typemask))
|
||||
return sq_throwerror(v, "invalid typemask");
|
||||
nc->_typecheck.copy(res);
|
||||
}
|
||||
|
@ -552,11 +551,11 @@ SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b)
|
|||
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;
|
||||
_GETSAFE_OBJ(v, idx, OT_STRING,o);
|
||||
*c = _stringval(*o);
|
||||
str = _stringval(*o);
|
||||
return SQ_OK;
|
||||
}
|
||||
|
||||
|
@ -584,7 +583,7 @@ SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx)
|
|||
SQObjectPtr &o = stack_get(v, idx);
|
||||
SQObjectType type = type(o);
|
||||
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_ARRAY: return _array(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;
|
||||
}
|
||||
|
||||
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 lvl=(cstksize-level)-1;
|
||||
|
@ -908,7 +907,7 @@ const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedIntege
|
|||
}
|
||||
SQVM::CallInfo &ci=v->_callsstack[lvl];
|
||||
if(type(ci._closure)!=OT_CLOSURE)
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
SQClosure *c=_closure(ci._closure);
|
||||
SQFunctionProto *func=_funcproto(c->_function);
|
||||
if(func->_noutervalues > (SQInteger)idx) {
|
||||
|
@ -918,7 +917,7 @@ const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedIntege
|
|||
idx -= func->_noutervalues;
|
||||
return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1);
|
||||
}
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj)
|
||||
|
@ -931,9 +930,9 @@ void sq_resetobject(HSQOBJECT *po)
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -978,6 +977,10 @@ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror,
|
|||
if(!v->_suspended) {
|
||||
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){
|
||||
v->Push(res); return SQ_OK;
|
||||
}
|
||||
|
@ -1075,7 +1078,7 @@ SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up)
|
|||
return SQ_OK;
|
||||
}
|
||||
|
||||
SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize)
|
||||
std::span<char> sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize)
|
||||
{
|
||||
return _ss(v)->GetScratchPad(minsize);
|
||||
}
|
||||
|
@ -1089,19 +1092,18 @@ SQInteger sq_collectgarbage(HSQUIRRELVM v)
|
|||
#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);
|
||||
const SQChar *name = nullptr;
|
||||
if(type(self) == OT_CLOSURE) {
|
||||
if(_closure(self)->_outervalues.size()>nval) {
|
||||
v->Push(_closure(self)->_outervalues[nval]);
|
||||
SQFunctionProto *fp = _funcproto(_closure(self)->_function);
|
||||
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)
|
||||
|
@ -1253,44 +1255,16 @@ SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx)
|
|||
return SQ_ERROR;
|
||||
}
|
||||
|
||||
struct BufState{
|
||||
const SQChar *buf;
|
||||
SQInteger ptr;
|
||||
SQInteger size;
|
||||
};
|
||||
|
||||
char32_t buf_lexfeed(SQUserPointer file)
|
||||
{
|
||||
/* Convert an UTF-8 character into a char32_t */
|
||||
BufState *buf = (BufState *)file;
|
||||
const char *p = &buf->buf[buf->ptr];
|
||||
|
||||
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. */
|
||||
if (buf->size < buf->ptr + len) return 0;
|
||||
buf->ptr += len;
|
||||
|
||||
/* Convert the character, and when definitely invalid, bail out as well. */
|
||||
char32_t c;
|
||||
if (Utf8Decode(&c, p) != len) return -1;
|
||||
|
||||
return c;
|
||||
StringConsumer &consumer = *reinterpret_cast<StringConsumer *>(file);
|
||||
return consumer.AnyBytesLeft() ? consumer.ReadUtf8(-1) : 0;
|
||||
}
|
||||
|
||||
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);
|
||||
SQRESULT sq_compilebuffer(HSQUIRRELVM v,std::string_view buffer,std::string_view sourcename,SQBool raiseerror) {
|
||||
StringConsumer consumer{buffer};
|
||||
return sq_compile(v, buf_lexfeed, &consumer, sourcename, raiseerror);
|
||||
}
|
||||
|
||||
void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx)
|
||||
|
@ -1323,7 +1297,7 @@ void sq_free(void *p,SQUnsignedInteger 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;
|
||||
if (this->_ops == INT64_MIN) {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../../stdafx.h"
|
||||
#include "../../fmt/format.h"
|
||||
|
||||
#include "../../../core/string_consumer.hpp"
|
||||
#include "sqpcheader.h"
|
||||
#include "sqvm.h"
|
||||
#include "sqstring.h"
|
||||
|
@ -17,19 +18,20 @@
|
|||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
bool str2num(const SQChar *s,SQObjectPtr &res)
|
||||
bool str2num(std::string_view s,SQObjectPtr &res)
|
||||
{
|
||||
SQChar *end;
|
||||
if(strstr(s,".")){
|
||||
SQFloat r = SQFloat(strtod(s,&end));
|
||||
if(s == end) return false;
|
||||
if(s.find('.') != std::string_view::npos){
|
||||
char *end;
|
||||
std::string str{s};
|
||||
SQFloat r = SQFloat(strtod(str.c_str(),&end));
|
||||
if(str.c_str() == end) return false;
|
||||
res = r;
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
SQInteger r = SQInteger(strtol(s,&end,10));
|
||||
if(s == end) return false;
|
||||
res = r;
|
||||
auto val = ParseInteger<int64_t>(s);
|
||||
if (!val.has_value()) return false;
|
||||
res = *val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -101,29 +103,29 @@ static SQInteger base_getstackinfos(HSQUIRRELVM v)
|
|||
SQInteger level;
|
||||
SQStackInfos si;
|
||||
SQInteger seq = 0;
|
||||
const SQChar *name = nullptr;
|
||||
sq_getinteger(v, -1, &level);
|
||||
if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si)))
|
||||
{
|
||||
const SQChar *fn = "unknown";
|
||||
const SQChar *src = "unknown";
|
||||
std::string_view fn = "unknown";
|
||||
std::string_view src = "unknown";
|
||||
if(si.funcname)fn = si.funcname;
|
||||
if(si.source)src = si.source;
|
||||
sq_newtable(v);
|
||||
sq_pushstring(v, "func", -1);
|
||||
sq_pushstring(v, fn, -1);
|
||||
sq_pushstring(v, "func");
|
||||
sq_pushstring(v, fn);
|
||||
sq_createslot(v, -3);
|
||||
sq_pushstring(v, "src", -1);
|
||||
sq_pushstring(v, src, -1);
|
||||
sq_pushstring(v, "src");
|
||||
sq_pushstring(v, src);
|
||||
sq_createslot(v, -3);
|
||||
sq_pushstring(v, "line", -1);
|
||||
sq_pushstring(v, "line");
|
||||
sq_pushinteger(v, si.line);
|
||||
sq_createslot(v, -3);
|
||||
sq_pushstring(v, "locals", -1);
|
||||
sq_pushstring(v, "locals");
|
||||
sq_newtable(v);
|
||||
seq=0;
|
||||
while ((name = sq_getlocal(v, level, seq))) {
|
||||
sq_pushstring(v, name, -1);
|
||||
std::optional<std::string_view> name;
|
||||
while ((name = sq_getlocal(v, level, seq)).has_value()) {
|
||||
sq_pushstring(v, *name);
|
||||
sq_push(v, -2);
|
||||
sq_createslot(v, -4);
|
||||
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)
|
||||
{
|
||||
const SQChar *str;
|
||||
std::string_view str;
|
||||
sq_tostring(v,2);
|
||||
sq_getstring(v,-1,&str);
|
||||
sq_getstring(v,-1,str);
|
||||
if(_ss(v)->_printfunc) _ss(v)->_printfunc(v,str);
|
||||
return 0;
|
||||
}
|
||||
|
@ -180,14 +182,14 @@ static SQInteger base_print(HSQUIRRELVM v)
|
|||
static SQInteger base_compilestring(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger nargs=sq_gettop(v);
|
||||
const SQChar *src=nullptr,*name="unnamedbuffer";
|
||||
std::string_view src, name="unnamedbuffer";
|
||||
SQInteger size;
|
||||
sq_getstring(v,2,&src);
|
||||
sq_getstring(v,2,src);
|
||||
size=sq_getsize(v,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;
|
||||
else
|
||||
return SQ_ERROR;
|
||||
|
@ -232,62 +234,59 @@ static SQInteger base_array(HSQUIRRELVM v)
|
|||
static SQInteger base_type(HSQUIRRELVM v)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
static SQRegFunction base_funcs[]={
|
||||
static const std::initializer_list<SQRegFunction> base_funcs={
|
||||
//generic
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
{"seterrorhandler",base_seterrorhandler,2, nullptr},
|
||||
{"setdebughook",base_setdebughook,2, nullptr},
|
||||
{"enabledebuginfo",base_enabledebuginfo,2, nullptr},
|
||||
{"seterrorhandler",base_seterrorhandler,2, std::nullopt},
|
||||
{"setdebughook",base_setdebughook,2, std::nullopt},
|
||||
{"enabledebuginfo",base_enabledebuginfo,2, std::nullopt},
|
||||
{"getstackinfos",base_getstackinfos,2, ".n"},
|
||||
{"getroottable",base_getroottable,1, nullptr},
|
||||
{"setroottable",base_setroottable,2, nullptr},
|
||||
{"getconsttable",base_getconsttable,1, nullptr},
|
||||
{"setconsttable",base_setconsttable,2, nullptr},
|
||||
{"getroottable",base_getroottable,1, std::nullopt},
|
||||
{"setroottable",base_setroottable,2, std::nullopt},
|
||||
{"getconsttable",base_getconsttable,1, std::nullopt},
|
||||
{"setconsttable",base_setconsttable,2, std::nullopt},
|
||||
#endif
|
||||
{"assert",base_assert,2, nullptr},
|
||||
{"print",base_print,2, nullptr},
|
||||
{"assert",base_assert,2, std::nullopt},
|
||||
{"print",base_print,2, std::nullopt},
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
{"compilestring",base_compilestring,-2, ".ss"},
|
||||
{"newthread",base_newthread,2, ".c"},
|
||||
{"suspend",base_suspend,-1, nullptr},
|
||||
{"suspend",base_suspend,-1, std::nullopt},
|
||||
#endif
|
||||
{"array",base_array,-2, ".n"},
|
||||
{"type",base_type,2, nullptr},
|
||||
{"type",base_type,2, std::nullopt},
|
||||
#ifdef EXPORT_DEFAULT_SQUIRREL_FUNCTIONS
|
||||
{"dummy",base_dummy,0,nullptr},
|
||||
{"dummy",base_dummy,0,std::nullopt},
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
{"collectgarbage",base_collectgarbage,1, "t"},
|
||||
#endif
|
||||
#endif
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
void sq_base_register(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger i=0;
|
||||
sq_pushroottable(v);
|
||||
while(base_funcs[i].name!=nullptr) {
|
||||
sq_pushstring(v,base_funcs[i].name,-1);
|
||||
sq_newclosure(v,base_funcs[i].f,0);
|
||||
sq_setnativeclosurename(v,-1,base_funcs[i].name);
|
||||
sq_setparamscheck(v,base_funcs[i].nparamscheck,base_funcs[i].typemask);
|
||||
for(auto &func : base_funcs) {
|
||||
sq_pushstring(v,func.name);
|
||||
sq_newclosure(v,func.f,0);
|
||||
sq_setnativeclosurename(v,-1,func.name);
|
||||
sq_setparamscheck(v,func.nparamscheck,func.typemask);
|
||||
sq_createslot(v,-3);
|
||||
i++;
|
||||
}
|
||||
sq_pushstring(v,"_version_",-1);
|
||||
sq_pushstring(v,SQUIRREL_VERSION,-1);
|
||||
sq_pushstring(v,"_version_");
|
||||
sq_pushstring(v,SQUIRREL_VERSION);
|
||||
sq_createslot(v,-3);
|
||||
sq_pushstring(v,"_charsize_",-1);
|
||||
sq_pushinteger(v,sizeof(SQChar));
|
||||
sq_pushstring(v,"_charsize_");
|
||||
sq_pushinteger(v,sizeof(char));
|
||||
sq_createslot(v,-3);
|
||||
sq_pushstring(v,"_intsize_",-1);
|
||||
sq_pushstring(v,"_intsize_");
|
||||
sq_pushinteger(v,sizeof(SQInteger));
|
||||
sq_createslot(v,-3);
|
||||
sq_pushstring(v,"_floatsize_",-1);
|
||||
sq_pushstring(v,"_floatsize_");
|
||||
sq_pushinteger(v,sizeof(SQFloat));
|
||||
sq_createslot(v,-3);
|
||||
sq_pop(v,1);
|
||||
|
@ -370,8 +369,8 @@ static SQInteger obj_clear(HSQUIRRELVM v)
|
|||
static SQInteger number_delegate_tochar(HSQUIRRELVM v)
|
||||
{
|
||||
SQObject &o=stack_get(v,1);
|
||||
SQChar c = (SQChar)tointeger(o);
|
||||
v->Push(SQString::Create(_ss(v),(const SQChar *)&c,1));
|
||||
char c = static_cast<char>(tointeger(o));
|
||||
v->Push(SQString::Create(_ss(v),std::string_view(&c, 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"},
|
||||
{"rawget",table_rawget,2, "t"},
|
||||
{"rawset",table_rawset,3, "t"},
|
||||
{"rawdelete",table_rawdelete,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, "."},
|
||||
{"clear",obj_clear,1, "."},
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
//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"},
|
||||
{"append",array_append,2, "a"},
|
||||
{"extend",array_extend,2, "aa"},
|
||||
|
@ -624,10 +622,9 @@ SQRegFunction SQSharedState::_array_default_delegate_funcz[]={
|
|||
{"reverse",array_reverse,1, "a"},
|
||||
{"sort",array_sort,-1, "ac"},
|
||||
{"slice",array_slice,-1, "ann"},
|
||||
{"weakref",obj_delegate_weakref,1, nullptr },
|
||||
{"weakref",obj_delegate_weakref,1, std::nullopt },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"clear",obj_clear,1, "."},
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
//STRING DEFAULT DELEGATE//////////////////////////
|
||||
|
@ -636,25 +633,25 @@ static SQInteger string_slice(HSQUIRRELVM v)
|
|||
SQInteger sidx,eidx;
|
||||
SQObjectPtr o;
|
||||
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(eidx < 0)eidx = slen + eidx;
|
||||
if(eidx < sidx) return sq_throwerror(v,"wrong indexes");
|
||||
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;
|
||||
}
|
||||
|
||||
static SQInteger string_find(HSQUIRRELVM v)
|
||||
{
|
||||
SQInteger top,start_idx=0;
|
||||
const SQChar *str,*substr,*ret;
|
||||
if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){
|
||||
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>2)sq_getinteger(v,3,&start_idx);
|
||||
if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){
|
||||
ret=strstr(&str[start_idx],substr);
|
||||
if(ret){
|
||||
sq_pushinteger(v,(SQInteger)(ret-str));
|
||||
auto ret = str.find(substr, start_idx);
|
||||
if(ret != std::string_view::npos){
|
||||
sq_pushinteger(v,static_cast<SQInteger>(ret));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -666,11 +663,11 @@ static SQInteger string_find(HSQUIRRELVM v)
|
|||
#define STRING_TOFUNCZ(func) static SQInteger string_##func(HSQUIRRELVM v) \
|
||||
{ \
|
||||
SQObject str=stack_get(v,1); \
|
||||
SQInteger len=_string(str)->_len; \
|
||||
const SQChar *sThis=_stringval(str); \
|
||||
SQChar *sNew=(_ss(v)->GetScratchPad(len)); \
|
||||
for(SQInteger i=0;i<len;i++) sNew[i]=func(sThis[i]); \
|
||||
v->Push(SQString::Create(_ss(v),sNew,len)); \
|
||||
std::string_view sThis=_stringval(str); \
|
||||
size_t len=sThis.size(); \
|
||||
std::span<char> sNew=(_ss(v)->GetScratchPad(len)); \
|
||||
for(size_t i=0;i<len;i++) sNew[i]=func(sThis[i]); \
|
||||
v->Push(SQString::Create(_ss(v),std::string_view(sNew.data(), len))); \
|
||||
return 1; \
|
||||
}
|
||||
|
||||
|
@ -678,7 +675,7 @@ static SQInteger string_find(HSQUIRRELVM v)
|
|||
STRING_TOFUNCZ(tolower)
|
||||
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"},
|
||||
{"tointeger",default_delegate_tointeger,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 "},
|
||||
{"tolower",string_tolower,1, "s"},
|
||||
{"toupper",string_toupper,1, "s"},
|
||||
{"weakref",obj_delegate_weakref,1, nullptr },
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
{"weakref",obj_delegate_weakref,1, std::nullopt },
|
||||
};
|
||||
|
||||
//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"},
|
||||
{"tofloat",default_delegate_tofloat,1, "n|b"},
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"tochar",number_delegate_tochar,1, "n|b"},
|
||||
{"weakref",obj_delegate_weakref,1, nullptr },
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
{"weakref",obj_delegate_weakref,1, std::nullopt },
|
||||
};
|
||||
|
||||
//CLOSURE DEFAULT DELEGATE//////////////////////////
|
||||
|
@ -749,19 +744,19 @@ static SQInteger closure_getinfos(HSQUIRRELVM v) {
|
|||
_array(params)->Set((SQInteger)n,f->_parameters[n]);
|
||||
}
|
||||
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),"name",-1),f->_name);
|
||||
res->NewSlot(SQString::Create(_ss(v),"src",-1),f->_sourcename);
|
||||
res->NewSlot(SQString::Create(_ss(v),"parameters",-1),params);
|
||||
res->NewSlot(SQString::Create(_ss(v),"varargs",-1),f->_varparams);
|
||||
res->NewSlot(SQString::Create(_ss(v),"native"),false);
|
||||
res->NewSlot(SQString::Create(_ss(v),"name"),f->_name);
|
||||
res->NewSlot(SQString::Create(_ss(v),"src"),f->_sourcename);
|
||||
res->NewSlot(SQString::Create(_ss(v),"parameters"),params);
|
||||
res->NewSlot(SQString::Create(_ss(v),"varargs"),f->_varparams);
|
||||
}
|
||||
else { //OT_NATIVECLOSURE
|
||||
SQNativeClosure *nc = _nativeclosure(o);
|
||||
res->NewSlot(SQString::Create(_ss(v),"native",-1),true);
|
||||
res->NewSlot(SQString::Create(_ss(v),"name",-1),nc->_name);
|
||||
res->NewSlot(SQString::Create(_ss(v),"paramscheck",-1),nc->_nparamscheck);
|
||||
res->NewSlot(SQString::Create(_ss(v),"native"),true);
|
||||
res->NewSlot(SQString::Create(_ss(v),"name"),nc->_name);
|
||||
res->NewSlot(SQString::Create(_ss(v),"paramscheck"),nc->_nparamscheck);
|
||||
SQObjectPtr typecheck;
|
||||
if(!nc->_typecheck.empty()) {
|
||||
typecheck =
|
||||
|
@ -770,23 +765,22 @@ static SQInteger closure_getinfos(HSQUIRRELVM v) {
|
|||
_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);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
SQRegFunction SQSharedState::_closure_default_delegate_funcz[]={
|
||||
/* static */ const std::initializer_list<SQRegFunction> SQSharedState::_closure_default_delegate_funcz={
|
||||
{"call",closure_call,-1, "c"},
|
||||
{"pcall",closure_pcall,-1, "c"},
|
||||
{"acall",closure_acall,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, "."},
|
||||
{"bindenv",closure_bindenv,2, "c x|y|t"},
|
||||
{"getinfos",closure_getinfos,1, "c"},
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
//GENERATOR DEFAULT DELEGATE
|
||||
|
@ -801,11 +795,10 @@ static SQInteger generator_getstatus(HSQUIRRELVM v)
|
|||
return 1;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_generator_default_delegate_funcz[]={
|
||||
/* static */ const std::initializer_list<SQRegFunction> SQSharedState::_generator_default_delegate_funcz={
|
||||
{"getstatus",generator_getstatus,1, "g"},
|
||||
{"weakref",obj_delegate_weakref,1, nullptr },
|
||||
{"weakref",obj_delegate_weakref,1, std::nullopt },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
//THREAD DEFAULT DELEGATE
|
||||
|
@ -871,13 +864,13 @@ static SQInteger thread_getstatus(HSQUIRRELVM v)
|
|||
SQObjectPtr &o = stack_get(v,1);
|
||||
switch(sq_getvmstate(_thread(o))) {
|
||||
case SQ_VMSTATE_IDLE:
|
||||
sq_pushstring(v,"idle",-1);
|
||||
sq_pushstring(v,"idle");
|
||||
break;
|
||||
case SQ_VMSTATE_RUNNING:
|
||||
sq_pushstring(v,"running",-1);
|
||||
sq_pushstring(v,"running");
|
||||
break;
|
||||
case SQ_VMSTATE_SUSPENDED:
|
||||
sq_pushstring(v,"suspended",-1);
|
||||
sq_pushstring(v,"suspended");
|
||||
break;
|
||||
default:
|
||||
return sq_throwerror(v,"internal VM error");
|
||||
|
@ -885,13 +878,12 @@ static SQInteger thread_getstatus(HSQUIRRELVM v)
|
|||
return 1;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_thread_default_delegate_funcz[] = {
|
||||
/* static */ const std::initializer_list<SQRegFunction> SQSharedState::_thread_default_delegate_funcz = {
|
||||
{"call", thread_call, -1, "v"},
|
||||
{"wakeup", thread_wakeup, -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, "."},
|
||||
{nullptr,nullptr,0,nullptr},
|
||||
};
|
||||
|
||||
static SQInteger class_getattributes(HSQUIRRELVM v)
|
||||
|
@ -915,14 +907,13 @@ static SQInteger class_instance(HSQUIRRELVM v)
|
|||
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."},
|
||||
{"setattributes", class_setattributes, 3, "y.."},
|
||||
{"rawin",container_rawexists,2, "y"},
|
||||
{"weakref",obj_delegate_weakref,1, nullptr },
|
||||
{"weakref",obj_delegate_weakref,1, std::nullopt },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{"instance",class_instance,1, "y"},
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
static SQInteger instance_getclass(HSQUIRRELVM v)
|
||||
|
@ -932,12 +923,11 @@ static SQInteger instance_getclass(HSQUIRRELVM v)
|
|||
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"},
|
||||
{"rawin",container_rawexists,2, "x"},
|
||||
{"weakref",obj_delegate_weakref,1, nullptr },
|
||||
{"weakref",obj_delegate_weakref,1, std::nullopt },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
static SQInteger weakref_ref(HSQUIRRELVM v)
|
||||
|
@ -947,11 +937,10 @@ static SQInteger weakref_ref(HSQUIRRELVM v)
|
|||
return 1;
|
||||
}
|
||||
|
||||
SQRegFunction SQSharedState::_weakref_default_delegate_funcz[] = {
|
||||
/* static */ const std::initializer_list<SQRegFunction> SQSharedState::_weakref_default_delegate_funcz = {
|
||||
{"ref",weakref_ref,1, "r"},
|
||||
{"weakref",obj_delegate_weakref,1, nullptr },
|
||||
{"weakref",obj_delegate_weakref,1, std::nullopt },
|
||||
{"tostring",default_delegate_tostring,1, "."},
|
||||
{nullptr,nullptr,0,nullptr}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -57,16 +57,12 @@ typedef sqvector<ExpState> ExpStateVec;
|
|||
class SQCompiler
|
||||
{
|
||||
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;
|
||||
_sourcename = SQString::Create(_ss(v), sourcename);
|
||||
_lineinfo = lineinfo;_raiseerror = raiseerror;
|
||||
}
|
||||
[[noreturn]] void Error(const std::string &msg)
|
||||
{
|
||||
throw CompileException(msg);
|
||||
}
|
||||
void Lex(){ _token = _lex.Lex();}
|
||||
void PushExpState(){ _expstates.push_back(ExpState()); }
|
||||
bool IsDerefToken(SQInteger tok)
|
||||
|
@ -92,7 +88,7 @@ public:
|
|||
//do nothing
|
||||
}
|
||||
else {
|
||||
const SQChar *etypename;
|
||||
std::string_view etypename;
|
||||
if(tok > 255) {
|
||||
switch(tok)
|
||||
{
|
||||
|
@ -109,21 +105,21 @@ public:
|
|||
etypename = "FLOAT";
|
||||
break;
|
||||
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;
|
||||
switch(tok)
|
||||
{
|
||||
case TK_IDENTIFIER:
|
||||
ret = _fs->CreateString(_lex._svalue);
|
||||
ret = _fs->CreateString(_lex.View());
|
||||
break;
|
||||
case TK_STRING_LITERAL:
|
||||
ret = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1);
|
||||
ret = _fs->CreateString(_lex.View());
|
||||
break;
|
||||
case TK_INTEGER:
|
||||
ret = SQObjectPtr(_lex._nvalue);
|
||||
|
@ -140,7 +136,7 @@ public:
|
|||
{
|
||||
if(_token == ';') { Lex(); return; }
|
||||
if(!IsEndOfStatement()) {
|
||||
Error("end of statement expected (; or lf)");
|
||||
throw CompileException("end of statement expected (; or lf)");
|
||||
}
|
||||
}
|
||||
void MoveIfCurrentTargetIsLocal() {
|
||||
|
@ -233,7 +229,7 @@ public:
|
|||
}
|
||||
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){
|
||||
_fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0);
|
||||
}
|
||||
|
@ -243,7 +239,7 @@ public:
|
|||
Lex();
|
||||
break;
|
||||
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) {
|
||||
_fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0);
|
||||
}
|
||||
|
@ -356,19 +352,19 @@ public:
|
|||
SQInteger op = _token;
|
||||
SQInteger ds = _exst._deref;
|
||||
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();
|
||||
|
||||
switch(op){
|
||||
case TK_NEWSLOT:
|
||||
if(freevar) Error("free variables cannot be modified");
|
||||
if(freevar) throw CompileException("free variables cannot be modified");
|
||||
if(ds == DEREF_FIELD)
|
||||
EmitDerefOp(_OP_NEWSLOT);
|
||||
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;
|
||||
case '=': //ASSIGN
|
||||
if(freevar) Error("free variables cannot be modified");
|
||||
if(freevar) throw CompileException("free variables cannot be modified");
|
||||
if(ds == DEREF_FIELD)
|
||||
EmitDerefOp(_OP_SET);
|
||||
else {//if _derefstate != DEREF_NO_DEREF && DEREF_FIELD so is the index of a local
|
||||
|
@ -533,7 +529,7 @@ public:
|
|||
if(_token == TK_PARENT) {
|
||||
Lex();
|
||||
if(!NeedGet())
|
||||
Error("parent cannot be set");
|
||||
throw CompileException("parent cannot be set");
|
||||
SQInteger src = _fs->PopTarget();
|
||||
_fs->AddInstruction(_OP_GETPARENT, _fs->PushTarget(), src);
|
||||
}
|
||||
|
@ -546,7 +542,7 @@ public:
|
|||
}
|
||||
break;
|
||||
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(']');
|
||||
pos = -1;
|
||||
if(NeedGet()) Emit2ArgsOP(_OP_GET);
|
||||
|
@ -598,7 +594,7 @@ public:
|
|||
switch(_token)
|
||||
{
|
||||
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();
|
||||
}
|
||||
break;
|
||||
|
@ -618,7 +614,7 @@ public:
|
|||
SQObject id;
|
||||
SQObject constant;
|
||||
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_CONSTRUCTOR: id = _fs->CreateString("constructor"); break;
|
||||
}
|
||||
|
@ -638,7 +634,7 @@ public:
|
|||
Expect('.'); constid = Expect(TK_IDENTIFIER);
|
||||
if(!_table(constant)->Get(constid,constval)) {
|
||||
constval.Null();
|
||||
Error(fmt::format("invalid constant [{}.{}]", _stringval(id),_stringval(constid)));
|
||||
throw CompileException(fmt::format("invalid constant [{}.{}]", _stringval(id),_stringval(constid)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -742,7 +738,7 @@ public:
|
|||
case TK_DELEGATE : DelegateExpr(); break;
|
||||
case '(': Lex(); CommaExpr(); Expect(')');
|
||||
break;
|
||||
default: Error("expression expected");
|
||||
default: throw CompileException("expression expected");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -771,7 +767,7 @@ public:
|
|||
nargs++;
|
||||
if(_token == ','){
|
||||
Lex();
|
||||
if(_token == ')') Error("expression expected, found ')'");
|
||||
if(_token == ')') throw CompileException("expression expected, found ')'");
|
||||
}
|
||||
}
|
||||
Lex();
|
||||
|
@ -1082,13 +1078,13 @@ public:
|
|||
_exst._funcarg = false;
|
||||
PrefixedExpr();
|
||||
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) {
|
||||
ClassExp();
|
||||
EmitDerefOp(_OP_NEWSLOT);
|
||||
_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()
|
||||
{
|
||||
|
@ -1103,7 +1099,7 @@ public:
|
|||
val._unVal.fFloat = _lex._fvalue;
|
||||
break;
|
||||
case TK_STRING_LITERAL:
|
||||
val = _fs->CreateString(_lex._svalue,_lex._longstr.size()-1);
|
||||
val = _fs->CreateString(_lex.View());
|
||||
break;
|
||||
case '-':
|
||||
Lex();
|
||||
|
@ -1118,12 +1114,12 @@ public:
|
|||
val._unVal.fFloat = -_lex._fvalue;
|
||||
break;
|
||||
default:
|
||||
Error("scalar expected : integer,float");
|
||||
throw CompileException("scalar expected : integer,float");
|
||||
val._type = OT_NULL; // Silent compile-warning
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Error("scalar expected : integer,float or string");
|
||||
throw CompileException("scalar expected : integer,float or string");
|
||||
val._type = OT_NULL; // Silent compile-warning
|
||||
}
|
||||
Lex();
|
||||
|
@ -1226,9 +1222,9 @@ public:
|
|||
_exst._funcarg = false;
|
||||
PrefixedExpr();
|
||||
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);
|
||||
else Error("cannot delete a local");
|
||||
else throw CompileException("cannot delete a local");
|
||||
}
|
||||
void PrefixIncDec(SQInteger token)
|
||||
{
|
||||
|
@ -1255,10 +1251,10 @@ public:
|
|||
SQInteger defparams = 0;
|
||||
while(_token!=')') {
|
||||
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;
|
||||
Lex();
|
||||
if(_token != ')') Error("expected ')'");
|
||||
if(_token != ')') throw CompileException("expected ')'");
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
@ -1271,10 +1267,10 @@ public:
|
|||
defparams++;
|
||||
}
|
||||
else {
|
||||
if(defparams > 0) Error("expected '='");
|
||||
if(defparams > 0) throw CompileException("expected '='");
|
||||
}
|
||||
if(_token == ',') Lex();
|
||||
else if(_token != ')') Error("expected ')' or ','");
|
||||
else if(_token != ')') throw CompileException("expected ')' or ','");
|
||||
}
|
||||
}
|
||||
Expect(')');
|
||||
|
@ -1289,7 +1285,7 @@ public:
|
|||
//outers are treated as implicit local variables
|
||||
funcstate->AddOuterValue(paramname);
|
||||
if(_token == ',') Lex();
|
||||
else if(_token != ')') Error("expected ')' or ','");
|
||||
else if(_token != ')') throw CompileException("expected ')' or ','");
|
||||
}
|
||||
Lex();
|
||||
}
|
||||
|
@ -1346,7 +1342,7 @@ private:
|
|||
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);
|
||||
return p.Compile(out);
|
||||
|
|
|
@ -73,5 +73,5 @@ struct SQVM;
|
|||
|
||||
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_
|
||||
|
|
|
@ -37,7 +37,7 @@ SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si)
|
|||
{
|
||||
SQInteger cssize = v->_callsstacksize;
|
||||
if (cssize > level) {
|
||||
memset(si, 0, sizeof(SQStackInfos));
|
||||
*si = {};
|
||||
SQVM::CallInfo &ci = v->_callsstack[cssize-level-1];
|
||||
switch (type(ci._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)
|
||||
{
|
||||
SQObjectPtr exptypes = SQString::Create(_ss(this), "", -1);
|
||||
SQObjectPtr exptypes = SQString::Create(_ss(this), "");
|
||||
SQInteger found = 0;
|
||||
for(SQInteger i=0; i<16; i++)
|
||||
{
|
||||
SQInteger mask = 0x00000001LL << i;
|
||||
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 ++;
|
||||
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)));
|
||||
|
|
|
@ -114,7 +114,7 @@ public:
|
|||
this->~SQFunctionProto();
|
||||
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);
|
||||
bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write);
|
||||
static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret);
|
||||
|
|
|
@ -110,11 +110,6 @@ SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent)
|
|||
|
||||
}
|
||||
|
||||
void SQFuncState::Error(const SQChar *err)
|
||||
{
|
||||
throw CompileException(err);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG_DUMP
|
||||
void SQFuncState::Dump(SQFunctionProto *func)
|
||||
{
|
||||
|
@ -234,7 +229,7 @@ SQInteger SQFuncState::GetConstant(const SQObject &cons)
|
|||
_nliterals++;
|
||||
if(_nliterals > MAX_LITERALS) {
|
||||
val.Null();
|
||||
Error("internal compiler error: too many literals");
|
||||
throw CompileException("internal compiler error: too many literals");
|
||||
}
|
||||
}
|
||||
return _integer(val);
|
||||
|
@ -264,7 +259,7 @@ SQInteger SQFuncState::AllocStackPos()
|
|||
SQInteger npos=_vlocals.size();
|
||||
_vlocals.push_back(SQLocalVarInfo());
|
||||
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();
|
||||
}
|
||||
return npos;
|
||||
|
@ -499,9 +494,9 @@ void SQFuncState::AddInstruction(SQInstruction &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);
|
||||
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 < _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;
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ struct SQFuncState
|
|||
#ifdef _DEBUG_DUMP
|
||||
void Dump(SQFunctionProto *func);
|
||||
#endif
|
||||
[[noreturn]] void Error(const SQChar *err);
|
||||
SQFuncState *PushChildState(SQSharedState *ss);
|
||||
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);}
|
||||
|
@ -43,7 +42,7 @@ struct SQFuncState
|
|||
SQInteger TopTarget();
|
||||
SQInteger GetUpTarget(SQInteger n);
|
||||
bool IsLocal(SQUnsignedInteger stkpos);
|
||||
SQObject CreateString(const SQChar *s,SQInteger len = -1);
|
||||
SQObject CreateString(std::string_view s);
|
||||
SQObject CreateTable();
|
||||
bool IsConstant(const SQObject &name,SQObject &e);
|
||||
SQInteger _returnexp;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "sqlexer.h"
|
||||
|
||||
#include "../../../core/utf8.hpp"
|
||||
#include "../../../core/string_consumer.hpp"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
|
@ -84,22 +85,16 @@ SQLexer::SQLexer(SQSharedState *ss, SQLEXREADFUNC rg, SQUserPointer up)
|
|||
_prevtoken = -1;
|
||||
_curtoken = -1;
|
||||
|
||||
_svalue = nullptr;
|
||||
_nvalue = 0;
|
||||
_fvalue = 0;
|
||||
|
||||
Next();
|
||||
}
|
||||
|
||||
[[noreturn]] void SQLexer::Error(const SQChar *err)
|
||||
{
|
||||
throw CompileException(err);
|
||||
}
|
||||
|
||||
void SQLexer::Next()
|
||||
{
|
||||
char32_t t = _readf(_up);
|
||||
if(t > MAX_CHAR) Error("Invalid character");
|
||||
if(t > MAX_CHAR) throw CompileException("Invalid character");
|
||||
if(t != 0) {
|
||||
_currdata = t;
|
||||
return;
|
||||
|
@ -107,7 +102,7 @@ void SQLexer::Next()
|
|||
_currdata = SQUIRREL_EOB;
|
||||
}
|
||||
|
||||
const SQChar *SQLexer::Tok2Str(SQInteger tok)
|
||||
std::optional<std::string_view> SQLexer::Tok2Str(SQInteger tok)
|
||||
{
|
||||
SQObjectPtr itr, key, val;
|
||||
SQInteger nitr;
|
||||
|
@ -116,7 +111,7 @@ const SQChar *SQLexer::Tok2Str(SQInteger tok)
|
|||
if(((SQInteger)_integer(val)) == tok)
|
||||
return _stringval(key);
|
||||
}
|
||||
return nullptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SQLexer::LexBlockComment()
|
||||
|
@ -126,7 +121,7 @@ void SQLexer::LexBlockComment()
|
|||
switch(CUR_CHAR) {
|
||||
case '*': { NEXT(); if(CUR_CHAR == '/') { done = true; NEXT(); }}; continue;
|
||||
case '\n': _currentline++; NEXT(); continue;
|
||||
case SQUIRREL_EOB: Error("missing \"*/\" in comment");
|
||||
case SQUIRREL_EOB: throw CompileException("missing \"*/\" in comment");
|
||||
default: NEXT();
|
||||
}
|
||||
}
|
||||
|
@ -195,11 +190,11 @@ SQInteger SQLexer::Lex()
|
|||
SQInteger stype;
|
||||
NEXT();
|
||||
if(CUR_CHAR != '"')
|
||||
Error("string expected");
|
||||
throw CompileException("string expected");
|
||||
if((stype=ReadString('"',true))!=-1) {
|
||||
RETURN_TOKEN(stype);
|
||||
}
|
||||
Error("error parsing the string");
|
||||
throw CompileException("error parsing the string");
|
||||
}
|
||||
case '"':
|
||||
case '\'': {
|
||||
|
@ -207,7 +202,7 @@ SQInteger SQLexer::Lex()
|
|||
if((stype=ReadString(CUR_CHAR,false))!=-1){
|
||||
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 '~':
|
||||
|
@ -217,7 +212,7 @@ SQInteger SQLexer::Lex()
|
|||
NEXT();
|
||||
if (CUR_CHAR != '.'){ RETURN_TOKEN('.') }
|
||||
NEXT();
|
||||
if (CUR_CHAR != '.'){ Error("invalid token '..'"); }
|
||||
if (CUR_CHAR != '.'){ throw CompileException("invalid token '..'"); }
|
||||
NEXT();
|
||||
RETURN_TOKEN(TK_VARPARAMS);
|
||||
case '&':
|
||||
|
@ -263,7 +258,7 @@ SQInteger SQLexer::Lex()
|
|||
}
|
||||
else {
|
||||
SQInteger c = CUR_CHAR;
|
||||
if (iscntrl((int)c)) Error("unexpected character(control)");
|
||||
if (iscntrl((int)c)) throw CompileException("unexpected character(control)");
|
||||
NEXT();
|
||||
RETURN_TOKEN(c);
|
||||
}
|
||||
|
@ -274,7 +269,7 @@ SQInteger SQLexer::Lex()
|
|||
return 0;
|
||||
}
|
||||
|
||||
SQInteger SQLexer::GetIDType(SQChar *s)
|
||||
SQInteger SQLexer::GetIDType(std::string_view s)
|
||||
{
|
||||
SQObjectPtr 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) {
|
||||
switch(CUR_CHAR) {
|
||||
case SQUIRREL_EOB:
|
||||
Error("unfinished string");
|
||||
throw CompileException("unfinished string");
|
||||
return -1;
|
||||
case '\n':
|
||||
if(!verbatim) Error("newline in a constant");
|
||||
if(!verbatim) throw CompileException("newline in a constant");
|
||||
APPEND_CHAR(CUR_CHAR); NEXT();
|
||||
_currentline++;
|
||||
break;
|
||||
|
@ -308,18 +303,18 @@ SQInteger SQLexer::ReadString(char32_t ndelim,bool verbatim)
|
|||
NEXT();
|
||||
switch(CUR_CHAR) {
|
||||
case 'x': NEXT(); {
|
||||
if(!isxdigit(CUR_CHAR)) Error("hexadecimal number expected");
|
||||
if(!isxdigit(CUR_CHAR)) throw CompileException("hexadecimal number expected");
|
||||
const SQInteger maxdigits = 4;
|
||||
SQChar temp[maxdigits+1];
|
||||
SQInteger n = 0;
|
||||
char temp[maxdigits];
|
||||
size_t n = 0;
|
||||
while(isxdigit(CUR_CHAR) && n < maxdigits) {
|
||||
temp[n] = CUR_CHAR;
|
||||
n++;
|
||||
NEXT();
|
||||
}
|
||||
temp[n] = 0;
|
||||
SQChar *sTemp;
|
||||
APPEND_CHAR((SQChar)strtoul(temp,&sTemp,16));
|
||||
auto val = ParseInteger(std::string_view{temp, n}, 16);
|
||||
if (!val.has_value()) throw CompileException("hexadecimal number expected");
|
||||
APPEND_CHAR(static_cast<char>(*val));
|
||||
}
|
||||
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;
|
||||
default:
|
||||
Error("unrecognised escaper char");
|
||||
throw CompileException("unrecognised escaper char");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -353,49 +348,16 @@ SQInteger SQLexer::ReadString(char32_t ndelim,bool verbatim)
|
|||
break;
|
||||
}
|
||||
}
|
||||
TERMINATE_BUFFER();
|
||||
SQInteger len = _longstr.size()-1;
|
||||
if(ndelim == '\'') {
|
||||
if(len == 0) Error("empty constant");
|
||||
if(len > 1) Error("constant too long");
|
||||
if(_longstr.empty()) throw CompileException("empty constant");
|
||||
if(_longstr.size() > 1) throw CompileException("constant too long");
|
||||
_nvalue = _longstr[0];
|
||||
return TK_INTEGER;
|
||||
}
|
||||
_svalue = &_longstr[0];
|
||||
return TK_STRING_LITERAL;
|
||||
}
|
||||
|
||||
void LexHexadecimal(const SQChar *s,SQUnsignedInteger *res)
|
||||
{
|
||||
*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 scisodigit(char c) { return c >= '0' && c <= '7'; }
|
||||
|
||||
SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; }
|
||||
|
||||
|
@ -409,7 +371,6 @@ SQInteger SQLexer::ReadNumber()
|
|||
#define TSCIENTIFIC 4
|
||||
#define TOCTAL 5
|
||||
SQInteger type = TINT, firstchar = CUR_CHAR;
|
||||
SQChar *sTemp;
|
||||
INIT_TEMP_STRING();
|
||||
NEXT();
|
||||
if(firstchar == '0' && (toupper(CUR_CHAR) == 'X' || scisodigit(CUR_CHAR)) ) {
|
||||
|
@ -419,7 +380,7 @@ SQInteger SQLexer::ReadNumber()
|
|||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
if(isdigit(CUR_CHAR)) Error("invalid octal number");
|
||||
if(isdigit(CUR_CHAR)) throw CompileException("invalid octal number");
|
||||
}
|
||||
else {
|
||||
NEXT();
|
||||
|
@ -428,7 +389,7 @@ SQInteger SQLexer::ReadNumber()
|
|||
APPEND_CHAR(CUR_CHAR);
|
||||
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 {
|
||||
|
@ -436,7 +397,7 @@ SQInteger SQLexer::ReadNumber()
|
|||
while (CUR_CHAR == '.' || isdigit(CUR_CHAR) || isexponent(CUR_CHAR)) {
|
||||
if(CUR_CHAR == '.' || isexponent(CUR_CHAR)) type = TFLOAT;
|
||||
if(isexponent(CUR_CHAR)) {
|
||||
if(type != TFLOAT) Error("invalid numeric format");
|
||||
if(type != TFLOAT) throw CompileException("invalid numeric format");
|
||||
type = TSCIENTIFIC;
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
|
@ -444,27 +405,29 @@ SQInteger SQLexer::ReadNumber()
|
|||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
if(!isdigit(CUR_CHAR)) Error("exponent expected");
|
||||
if(!isdigit(CUR_CHAR)) throw CompileException("exponent expected");
|
||||
}
|
||||
|
||||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
}
|
||||
}
|
||||
TERMINATE_BUFFER();
|
||||
switch(type) {
|
||||
case TSCIENTIFIC:
|
||||
case TFLOAT:
|
||||
_fvalue = (SQFloat)strtod(&_longstr[0],&sTemp);
|
||||
case TFLOAT: {
|
||||
std::string str{View()};
|
||||
char *sTemp;
|
||||
_fvalue = (SQFloat)strtod(str.c_str(),&sTemp);
|
||||
return TK_FLOAT;
|
||||
}
|
||||
case TINT:
|
||||
LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
|
||||
_nvalue = ParseInteger<SQUnsignedInteger>(View(), 10).value_or(0);
|
||||
return TK_INTEGER;
|
||||
case THEX:
|
||||
LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
|
||||
_nvalue = ParseInteger<SQUnsignedInteger>(View(), 16).value_or(0);
|
||||
return TK_INTEGER;
|
||||
case TOCTAL:
|
||||
LexOctal(&_longstr[0],(SQUnsignedInteger *)&_nvalue);
|
||||
_nvalue = ParseInteger<SQUnsignedInteger>(View(), 8).value_or(0);
|
||||
return TK_INTEGER;
|
||||
}
|
||||
return 0;
|
||||
|
@ -478,10 +441,6 @@ SQInteger SQLexer::ReadID()
|
|||
APPEND_CHAR(CUR_CHAR);
|
||||
NEXT();
|
||||
} while(isalnum(CUR_CHAR) || CUR_CHAR == '_');
|
||||
TERMINATE_BUFFER();
|
||||
res = GetIDType(&_longstr[0]);
|
||||
if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) {
|
||||
_svalue = &_longstr[0];
|
||||
}
|
||||
res = GetIDType(View());
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,10 @@ struct SQLexer
|
|||
{
|
||||
~SQLexer();
|
||||
SQLexer(SQSharedState *ss,SQLEXREADFUNC rg,SQUserPointer up);
|
||||
[[noreturn]] void Error(const SQChar *err);
|
||||
SQInteger Lex();
|
||||
const SQChar *Tok2Str(SQInteger tok);
|
||||
std::optional<std::string_view> Tok2Str(SQInteger tok);
|
||||
private:
|
||||
SQInteger GetIDType(SQChar *s);
|
||||
SQInteger GetIDType(std::string_view s);
|
||||
SQInteger ReadString(char32_t ndelim,bool verbatim);
|
||||
SQInteger ReadNumber();
|
||||
void LexBlockComment();
|
||||
|
@ -20,21 +19,21 @@ private:
|
|||
SQTable *_keywords;
|
||||
void INIT_TEMP_STRING() { _longstr.resize(0); }
|
||||
void APPEND_CHAR(char32_t c);
|
||||
void TERMINATE_BUFFER() { _longstr.push_back('\0'); }
|
||||
|
||||
public:
|
||||
SQInteger _prevtoken;
|
||||
SQInteger _currentline;
|
||||
SQInteger _lasttokenline;
|
||||
SQInteger _currentcolumn;
|
||||
const SQChar *_svalue;
|
||||
SQInteger _nvalue;
|
||||
SQFloat _fvalue;
|
||||
SQLEXREADFUNC _readf;
|
||||
SQUserPointer _up;
|
||||
char32_t _currdata;
|
||||
SQSharedState *_sharedstate;
|
||||
sqvector<SQChar> _longstr;
|
||||
sqvector<char> _longstr;
|
||||
|
||||
std::string_view View() const { return std::string_view(_longstr._vals, _longstr.size()); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#include "../../../safeguards.h"
|
||||
|
||||
|
||||
const SQChar *IdType2Name(SQObjectType type)
|
||||
std::string_view IdType2Name(SQObjectType type)
|
||||
{
|
||||
switch(_RAW_TYPE(type))
|
||||
{
|
||||
|
@ -42,25 +42,25 @@ const SQChar *IdType2Name(SQObjectType type)
|
|||
case _RT_INSTANCE: return "instance";
|
||||
case _RT_WEAKREF: return "weakref";
|
||||
default:
|
||||
return nullptr;
|
||||
NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
const SQChar *GetTypeName(const SQObjectPtr &obj1)
|
||||
std::string_view GetTypeName(const SQObjectPtr &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;
|
||||
return str;
|
||||
}
|
||||
|
||||
void SQString::Release()
|
||||
{
|
||||
REMOVE_STRING(_sharedstate,this);
|
||||
_sharedstate->_stringtable->Remove(this);
|
||||
}
|
||||
|
||||
SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval)
|
||||
|
@ -202,24 +202,21 @@ void SQArray::Extend(const SQArray *a){
|
|||
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;
|
||||
const SQChar *res=nullptr;
|
||||
if(nvars>=nseq){
|
||||
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){
|
||||
vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]);
|
||||
res=_stringval(_localvarinfos[i]._name);
|
||||
break;
|
||||
return _stringval(_localvarinfos[i]._name);
|
||||
}
|
||||
nseq--;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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)));
|
||||
switch(type(o)){
|
||||
case OT_STRING:
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,_stringval(o),_string(o)->_len));
|
||||
case OT_STRING: {
|
||||
auto str = _string(o)->Span();
|
||||
SQInteger len = str.size();
|
||||
_CHECK_IO(SafeWrite(v,write,up,&len,sizeof(len)));
|
||||
_CHECK_IO(SafeWrite(v,write,up,str.data(),len));
|
||||
break;
|
||||
}
|
||||
case OT_INTEGER:
|
||||
_CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break;
|
||||
case OT_FLOAT:
|
||||
|
@ -297,11 +297,11 @@ bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o)
|
|||
switch(t){
|
||||
case OT_STRING:{
|
||||
SQInteger len;
|
||||
_CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger)));
|
||||
_CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(len),len));
|
||||
o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len);
|
||||
}
|
||||
_CHECK_IO(SafeRead(v,read,up,&len,sizeof(len)));
|
||||
_CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(len).data(),len));
|
||||
o=SQString::Create(_ss(v),std::string_view(_ss(v)->GetScratchPad(-1).data(),len));
|
||||
break;
|
||||
}
|
||||
case OT_INTEGER:{
|
||||
SQInteger i;
|
||||
_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)
|
||||
{
|
||||
_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(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL));
|
||||
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)
|
||||
{
|
||||
_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;
|
||||
_CHECK_IO(SQFunctionProto::Load(v,up,read,func));
|
||||
_CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL));
|
||||
|
|
|
@ -138,7 +138,7 @@ struct SQObjectPtr;
|
|||
#define _refcounted(obj) ((obj)._unVal.pRefCounted)
|
||||
#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 tofloat(num) ((type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num))
|
||||
|
@ -357,7 +357,7 @@ struct SQObjectPtr : public SQObject
|
|||
return *this;
|
||||
}
|
||||
private:
|
||||
SQObjectPtr(const SQChar *){} //safety
|
||||
SQObjectPtr(const char *) = delete; //safety
|
||||
};
|
||||
|
||||
inline void _Swap(SQObject &a,SQObject &b)
|
||||
|
@ -449,8 +449,8 @@ struct SQDelegable : public CHAINABLE_OBJ {
|
|||
SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx);
|
||||
typedef sqvector<SQObjectPtr> SQObjectPtrVec;
|
||||
typedef sqvector<SQInteger> SQIntVec;
|
||||
const SQChar *GetTypeName(const SQObjectPtr &obj1);
|
||||
const SQChar *IdType2Name(SQObjectType type);
|
||||
std::string_view GetTypeName(const SQObjectPtr &obj1);
|
||||
std::string_view IdType2Name(SQObjectType type);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ enum SQOpcode
|
|||
};
|
||||
|
||||
struct SQInstructionDesc {
|
||||
const SQChar *name;
|
||||
std::string_view name;
|
||||
};
|
||||
|
||||
struct SQInstruction
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "sqarray.h"
|
||||
#include "squserdata.h"
|
||||
#include "sqclass.h"
|
||||
#include "../../../core/string_consumer.hpp"
|
||||
|
||||
#include "../../../safeguards.h"
|
||||
|
||||
|
@ -32,14 +33,12 @@ SQObjectPtr _minusone_((SQInteger)-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;
|
||||
while(typemask[i] != 0) {
|
||||
|
||||
switch(typemask[i]){
|
||||
StringConsumer consumer{typemask};
|
||||
while (consumer.AnyBytesLeft()) {
|
||||
switch(consumer.ReadChar()){
|
||||
case 'o': mask |= _RT_NULL; break;
|
||||
case 'i': mask |= _RT_INTEGER; 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 'y': mask |= _RT_CLASS; break;
|
||||
case 'r': mask |= _RT_WEAKREF; break;
|
||||
case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue;
|
||||
case ' ': i++; continue; //ignores spaces
|
||||
case '.': mask = -1; res.push_back(mask); mask = 0; continue;
|
||||
case ' ': continue; //ignores spaces
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
if(typemask[i] == '|') {
|
||||
i++;
|
||||
if(typemask[i] == 0)
|
||||
return false;
|
||||
|
||||
if(consumer.ReadCharIf('|')) {
|
||||
if(!consumer.AnyBytesLeft()) return false;
|
||||
continue;
|
||||
}
|
||||
res.push_back(mask);
|
||||
mask = 0;
|
||||
|
||||
}
|
||||
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);
|
||||
while(funcz[i].name!=nullptr){
|
||||
SQNativeClosure *nc = SQNativeClosure::Create(ss,funcz[i].f);
|
||||
nc->_nparamscheck = funcz[i].nparamscheck;
|
||||
nc->_name = SQString::Create(ss,funcz[i].name);
|
||||
if(funcz[i].typemask && !CompileTypemask(nc->_typecheck,funcz[i].typemask))
|
||||
for (auto &func : funcz) {
|
||||
SQNativeClosure *nc = SQNativeClosure::Create(ss,func.f);
|
||||
nc->_nparamscheck = func.nparamscheck;
|
||||
nc->_name = SQString::Create(ss,func.name);
|
||||
if(func.typemask.has_value() && !CompileTypemask(nc->_typecheck,*func.typemask))
|
||||
return nullptr;
|
||||
t->NewSlot(SQString::Create(ss,funcz[i].name),nc);
|
||||
i++;
|
||||
t->NewSlot(SQString::Create(ss,func.name),nc);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
@ -97,8 +91,6 @@ SQSharedState::SQSharedState()
|
|||
_printfunc = nullptr;
|
||||
_debuginfo = false;
|
||||
_notifyallexceptions = false;
|
||||
_scratchpad=nullptr;
|
||||
_scratchpadsize=0;
|
||||
_collectable_free_processing = false;
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
_gc_chain=nullptr;
|
||||
|
@ -212,7 +204,6 @@ SQSharedState::~SQSharedState()
|
|||
sq_delete(_systemstrings,SQObjectPtrVec);
|
||||
sq_delete(_metamethods,SQObjectPtrVec);
|
||||
sq_delete(_stringtable,SQStringTable);
|
||||
if(_scratchpad)SQ_FREE(_scratchpad,_scratchpadsize);
|
||||
}
|
||||
|
||||
|
||||
|
@ -357,19 +348,16 @@ void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c)
|
|||
}
|
||||
#endif
|
||||
|
||||
SQChar* SQSharedState::GetScratchPad(SQInteger size)
|
||||
std::span<char> SQSharedState::GetScratchPad(SQInteger size)
|
||||
{
|
||||
SQInteger newsize;
|
||||
if(size>0) {
|
||||
if(_scratchpadsize < size) {
|
||||
if(_scratchpad.size() < static_cast<size_t>(size)) {
|
||||
newsize = size + (size>>1);
|
||||
_scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
|
||||
_scratchpadsize = newsize;
|
||||
|
||||
}else if(_scratchpadsize >= (size<<5)) {
|
||||
newsize = _scratchpadsize >> 1;
|
||||
_scratchpad = (SQChar *)SQ_REALLOC(_scratchpad,_scratchpadsize,newsize);
|
||||
_scratchpadsize = newsize;
|
||||
_scratchpad.resize(newsize);
|
||||
}else if(_scratchpad.size() >= static_cast<size_t>(size<<5)) {
|
||||
newsize = _scratchpad.size() >> 1;
|
||||
_scratchpad.resize(newsize);
|
||||
}
|
||||
}
|
||||
return _scratchpad;
|
||||
|
@ -550,36 +538,36 @@ void SQStringTable::AllocNodes(SQInteger size)
|
|||
{
|
||||
_numofslots = size;
|
||||
_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)
|
||||
len = (SQInteger)strlen(news);
|
||||
SQHash h = ::_hashstr(news,(size_t)len)&(_numofslots-1);
|
||||
size_t len = new_string.size();
|
||||
auto slot = string_table_hash(new_string) & (_numofslots-1);
|
||||
SQString *s;
|
||||
for (s = _strings[h]; s; s = s->_next){
|
||||
if(s->_len == len && (!memcmp(news,s->_val,(size_t)len)))
|
||||
return s; //found
|
||||
for (s = _strings[slot]; s; s = s->_next){
|
||||
if(s->View() == new_string) return s; //found
|
||||
}
|
||||
|
||||
SQString *t=(SQString *)SQ_MALLOC(len+sizeof(SQString));
|
||||
new (t) SQString(news, len);
|
||||
t->_next = _strings[h];
|
||||
_strings[h] = t;
|
||||
new (t) SQString(new_string);
|
||||
t->_next = _strings[slot];
|
||||
_strings[slot] = t;
|
||||
_slotused++;
|
||||
if (_slotused > _numofslots) /* too crowded? */
|
||||
Resize(_numofslots*2);
|
||||
return t;
|
||||
}
|
||||
|
||||
SQString::SQString(const SQChar *news, SQInteger len)
|
||||
SQString::SQString(std::string_view new_string)
|
||||
{
|
||||
memcpy(_val,news,(size_t)len);
|
||||
_val[len] = '\0';
|
||||
_len = len;
|
||||
_hash = ::_hashstr(news,(size_t)len);
|
||||
std::ranges::copy(new_string, _val);
|
||||
_val[new_string.size()] = '\0';
|
||||
_len = new_string.size();
|
||||
_hash = string_table_hash(new_string);
|
||||
_next = nullptr;
|
||||
_sharedstate = nullptr;
|
||||
}
|
||||
|
@ -615,7 +603,7 @@ void SQStringTable::Remove(SQString *bs)
|
|||
else
|
||||
_strings[h] = s->_next;
|
||||
_slotused--;
|
||||
SQInteger slen = s->_len;
|
||||
size_t slen = s->View().size();
|
||||
s->~SQString();
|
||||
SQ_FREE(s,sizeof(SQString) + slen);
|
||||
return;
|
||||
|
|
|
@ -13,7 +13,7 @@ struct SQStringTable
|
|||
{
|
||||
SQStringTable();
|
||||
~SQStringTable();
|
||||
SQString *Add(const SQChar *,SQInteger len);
|
||||
SQString *Add(std::string_view str);
|
||||
void Remove(SQString *);
|
||||
private:
|
||||
void Resize(SQInteger size);
|
||||
|
@ -49,9 +49,6 @@ private:
|
|||
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 SQSharedState
|
||||
|
@ -59,7 +56,7 @@ struct SQSharedState
|
|||
SQSharedState();
|
||||
~SQSharedState();
|
||||
public:
|
||||
SQChar* GetScratchPad(SQInteger size);
|
||||
std::span<char> GetScratchPad(SQInteger size);
|
||||
SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name);
|
||||
void DelayFinalFree(SQCollectable *collectable);
|
||||
#ifndef NO_GARBAGE_COLLECTOR
|
||||
|
@ -84,33 +81,32 @@ public:
|
|||
#endif
|
||||
SQObjectPtr _root_vm;
|
||||
SQObjectPtr _table_default_delegate;
|
||||
static SQRegFunction _table_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _table_default_delegate_funcz;
|
||||
SQObjectPtr _array_default_delegate;
|
||||
static SQRegFunction _array_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _array_default_delegate_funcz;
|
||||
SQObjectPtr _string_default_delegate;
|
||||
static SQRegFunction _string_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _string_default_delegate_funcz;
|
||||
SQObjectPtr _number_default_delegate;
|
||||
static SQRegFunction _number_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _number_default_delegate_funcz;
|
||||
SQObjectPtr _generator_default_delegate;
|
||||
static SQRegFunction _generator_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _generator_default_delegate_funcz;
|
||||
SQObjectPtr _closure_default_delegate;
|
||||
static SQRegFunction _closure_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _closure_default_delegate_funcz;
|
||||
SQObjectPtr _thread_default_delegate;
|
||||
static SQRegFunction _thread_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _thread_default_delegate_funcz;
|
||||
SQObjectPtr _class_default_delegate;
|
||||
static SQRegFunction _class_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _class_default_delegate_funcz;
|
||||
SQObjectPtr _instance_default_delegate;
|
||||
static SQRegFunction _instance_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _instance_default_delegate_funcz;
|
||||
SQObjectPtr _weakref_default_delegate;
|
||||
static SQRegFunction _weakref_default_delegate_funcz[];
|
||||
static const std::initializer_list<SQRegFunction> _weakref_default_delegate_funcz;
|
||||
|
||||
SQCOMPILERERROR _compilererrorhandler;
|
||||
SQPRINTFUNCTION _printfunc;
|
||||
bool _debuginfo;
|
||||
bool _notifyallexceptions;
|
||||
private:
|
||||
SQChar *_scratchpad;
|
||||
SQInteger _scratchpadsize;
|
||||
std::vector<char> _scratchpad;
|
||||
};
|
||||
|
||||
#define _sp(s) (_sharedstate->GetScratchPad(s))
|
||||
|
@ -133,6 +129,6 @@ extern SQObjectPtr _false_;
|
|||
extern SQObjectPtr _one_;
|
||||
extern SQObjectPtr _minusone_;
|
||||
|
||||
bool CompileTypemask(SQIntVec &res,const SQChar *typemask);
|
||||
bool CompileTypemask(SQIntVec &res,std::string_view typemask);
|
||||
|
||||
#endif //_SQSTATE_H_
|
||||
|
|
|
@ -2,29 +2,23 @@
|
|||
#ifndef _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
|
||||
{
|
||||
SQString(const SQChar *news, SQInteger len);
|
||||
SQString(std::string_view str);
|
||||
~SQString(){}
|
||||
public:
|
||||
static SQString *Create(SQSharedState *ss, const SQChar *, SQInteger len = -1 );
|
||||
static SQString *Create(SQSharedState *ss, const std::string &str) { return Create(ss, str.data(), str.size()); }
|
||||
static SQString *Create(SQSharedState *ss, std::string_view str);
|
||||
SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval);
|
||||
void Release() override;
|
||||
SQSharedState *_sharedstate;
|
||||
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;
|
||||
SQHash _hash;
|
||||
SQChar _val[1];
|
||||
char _val[1];
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ struct SQUserData : SQDelegable
|
|||
SQInteger _size;
|
||||
SQRELEASEHOOK _hook;
|
||||
SQUserPointer _typetag;
|
||||
SQChar _val[1];
|
||||
char _val[1];
|
||||
};
|
||||
|
||||
#endif //_SQUSERDATA_H_
|
||||
|
|
|
@ -93,7 +93,7 @@ public:
|
|||
{
|
||||
_vals[idx].~T();
|
||||
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--;
|
||||
}
|
||||
|
|
|
@ -117,7 +117,6 @@ SQVM::SQVM(SQSharedState *ss)
|
|||
_in_stackoverflow = false;
|
||||
_ops_till_suspend = 0;
|
||||
_ops_till_suspend_error_threshold = INT64_MIN;
|
||||
_ops_till_suspend_error_label = nullptr;
|
||||
_callsstack = nullptr;
|
||||
_callsstacksize = 0;
|
||||
_alloccallsstacksize = 0;
|
||||
|
@ -198,7 +197,7 @@ bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result)
|
|||
SQObjectPtr res;
|
||||
switch(type(o1)){
|
||||
case OT_STRING:
|
||||
_RET_SUCCEED(strcmp(_stringval(o1),_stringval(o2)));
|
||||
_RET_SUCCEED(_stringval(o1).compare(_stringval(o2)));
|
||||
case OT_INTEGER:
|
||||
/* FS#3954: wrong integer comparison */
|
||||
_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;
|
||||
ToString(str, a);
|
||||
ToString(obj, b);
|
||||
SQInteger l = _string(a)->_len , ol = _string(b)->_len;
|
||||
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);
|
||||
dest = SQString::Create(_ss(this), fmt::format("{}{}", _stringval(a), _stringval(b)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1253,7 +1248,8 @@ bool SQVM::Get(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest,
|
|||
if(fetchroot) {
|
||||
if(_rawval(STK(0)) == _rawval(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;
|
||||
|
@ -1288,9 +1284,10 @@ bool SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPt
|
|||
case OT_STRING:
|
||||
if(sq_isnumeric(key)){
|
||||
SQInteger n=tointeger(key);
|
||||
if(abs((int)n)<_string(self)->_len){
|
||||
if(n<0)n=_string(self)->_len-n;
|
||||
dest=SQInteger(_stringval(self)[n]);
|
||||
std::string_view str = _stringval(self);
|
||||
if(std::abs(n) < static_cast<SQInteger>(str.size())){
|
||||
if(n<0)n=str.size()+n;
|
||||
dest=SQInteger(str[n]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -169,7 +169,7 @@ public:
|
|||
SQBool _can_suspend;
|
||||
SQInteger _ops_till_suspend;
|
||||
SQInteger _ops_till_suspend_error_threshold;
|
||||
const char *_ops_till_suspend_error_label;
|
||||
std::string_view _ops_till_suspend_error_label;
|
||||
SQBool _in_stackoverflow;
|
||||
|
||||
bool ShouldSuspend()
|
||||
|
|
|
@ -279,6 +279,10 @@ add_files(
|
|||
newgrf_animation_type.h
|
||||
newgrf_badge.cpp
|
||||
newgrf_badge.h
|
||||
newgrf_badge_config.cpp
|
||||
newgrf_badge_config.h
|
||||
newgrf_badge_gui.cpp
|
||||
newgrf_badge_gui.h
|
||||
newgrf_badge_type.h
|
||||
newgrf_callbacks.h
|
||||
newgrf_canal.cpp
|
||||
|
|
|
@ -135,8 +135,8 @@ public:
|
|||
static AIScannerLibrary *GetScannerLibrary();
|
||||
|
||||
/** Wrapper function for AIScanner::HasAI */
|
||||
static bool HasAI(const struct ContentInfo *ci, bool md5sum);
|
||||
static bool HasAILibrary(const ContentInfo *ci, bool md5sum);
|
||||
static bool HasAI(const ContentInfo &ci, bool md5sum);
|
||||
static bool HasAILibrary(const ContentInfo &ci, bool md5sum);
|
||||
private:
|
||||
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
|
||||
|
|
|
@ -334,12 +334,12 @@
|
|||
* @param md5sum whether to check the MD5 checksum
|
||||
* @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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ static constexpr NWidgetPart _nested_ai_config_widgets[] = {
|
|||
|
||||
/** Window definition for the configure AI window. */
|
||||
static WindowDesc _ai_config_desc(
|
||||
WDP_CENTER, nullptr, 0, 0,
|
||||
WDP_CENTER, {}, 0, 0,
|
||||
WC_GAME_OPTIONS, WC_NONE,
|
||||
{},
|
||||
_nested_ai_config_widgets
|
||||
|
@ -142,7 +142,7 @@ struct AIConfigWindow : public Window {
|
|||
|
||||
case WID_AIC_LIST:
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ struct AIConfigWindow : public Window {
|
|||
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;
|
||||
|
||||
ShowScriptTextfileWindow((TextfileType)(widget - WID_AIC_TEXTFILE), this->selected_slot);
|
||||
ShowScriptTextfileWindow(this, static_cast<TextfileType>(widget - WID_AIC_TEXTFILE), this->selected_slot);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -250,23 +250,23 @@ struct AIConfigWindow : public Window {
|
|||
}
|
||||
|
||||
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();
|
||||
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;
|
||||
}
|
||||
|
||||
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]);
|
||||
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->InvalidateData();
|
||||
}
|
||||
break;
|
||||
|
||||
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]);
|
||||
++this->selected_slot;
|
||||
this->vscroll->ScrollTowards(this->selected_slot.base());
|
||||
|
@ -282,11 +282,11 @@ struct AIConfigWindow : public Window {
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
case WID_AIC_CONFIGURE: // change the settings for an AI
|
||||
ShowScriptSettingsWindow((CompanyID)this->selected_slot);
|
||||
ShowScriptSettingsWindow(this->selected_slot);
|
||||
break;
|
||||
|
||||
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_CHANGE, !IsEditable(this->selected_slot));
|
||||
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_DOWN, !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(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());
|
||||
for (TextfileType tft = TFT_CONTENT_BEGIN; tft < TFT_CONTENT_END; tft++) {
|
||||
|
|
|
@ -27,14 +27,14 @@ static bool CheckAPIVersion(const std::string &api_version)
|
|||
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 */
|
||||
DefSQClass<AIInfo, ScriptType::AI> SQAIInfo("AIInfo");
|
||||
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::AddLabels, "AddLabels");
|
||||
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.PostRegister(engine);
|
||||
engine->AddMethod("RegisterAI", &AIInfo::Constructor, 2, "tx");
|
||||
engine->AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, 2, "tx");
|
||||
engine.AddMethod("RegisterAI", &AIInfo::Constructor, "tx");
|
||||
engine.AddMethod("RegisterDummyAI", &AIInfo::DummyConstructor, "tx");
|
||||
}
|
||||
|
||||
/* 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");
|
||||
AIInfo *info = (AIInfo *)instance;
|
||||
|
||||
SQInteger res = ScriptInfo::Constructor(vm, info);
|
||||
SQInteger res = ScriptInfo::Constructor(vm, *info);
|
||||
if (res != 0) return res;
|
||||
|
||||
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->min_loadable_version < 0) return SQ_ERROR;
|
||||
} else {
|
||||
info->min_loadable_version = info->GetVersion();
|
||||
}
|
||||
|
@ -101,13 +102,13 @@ template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq
|
|||
AIInfo *info = (AIInfo *)instance;
|
||||
info->api_version = *std::rbegin(AIInfo::ApiVersions);
|
||||
|
||||
SQInteger res = ScriptInfo::Constructor(vm, info);
|
||||
SQInteger res = ScriptInfo::Constructor(vm, *info);
|
||||
if (res != 0) return res;
|
||||
|
||||
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
|
||||
sq_setinstanceup(vm, 2, nullptr);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
engine->AddClassBegin("AILibrary");
|
||||
engine->AddClassEnd();
|
||||
engine->AddMethod("RegisterLibrary", &AILibrary::Constructor, 2, "tx");
|
||||
engine.AddClassBegin("AILibrary");
|
||||
engine.AddClassEnd();
|
||||
engine.AddMethod("RegisterLibrary", &AILibrary::Constructor, "tx");
|
||||
}
|
||||
|
||||
/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
|
||||
|
@ -137,7 +138,7 @@ bool AIInfo::CanLoadFromVersion(int version) const
|
|||
/* Create a new library */
|
||||
AILibrary *library = new AILibrary();
|
||||
|
||||
SQInteger res = ScriptInfo::Constructor(vm, library);
|
||||
SQInteger res = ScriptInfo::Constructor(vm, *library);
|
||||
if (res != 0) {
|
||||
delete library;
|
||||
return res;
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
/**
|
||||
* 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.
|
||||
|
@ -64,7 +64,7 @@ public:
|
|||
/**
|
||||
* 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.
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
#include "table/strings.h"
|
||||
|
||||
/* 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. */
|
||||
#include "../script/api/ai/ai_includes.hpp"
|
||||
|
@ -43,7 +43,7 @@ void AIInstance::Initialize(AIInfo *info)
|
|||
this->api_version = info->GetAPIVersion();
|
||||
|
||||
/* Register the AIController (including the "import" command) */
|
||||
SQAIController_Register(this->engine);
|
||||
SQAIController_Register(*this->engine);
|
||||
|
||||
ScriptInstance::Initialize(info->GetMainScript(), info->GetInstanceName(), _current_company);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ void AIInstance::RegisterAPI()
|
|||
ScriptInstance::RegisterAPI();
|
||||
|
||||
/* Register all classes */
|
||||
SQAI_RegisterAll(this->engine);
|
||||
SQAI_RegisterAll(*this->engine);
|
||||
|
||||
if (!this->LoadCompatibilityScripts(AI_DIR, AIInfo::ApiVersions)) this->Died();
|
||||
}
|
||||
|
|
|
@ -22,11 +22,8 @@
|
|||
#include "../safeguards.h"
|
||||
|
||||
|
||||
AIScannerInfo::AIScannerInfo() :
|
||||
ScriptScanner(),
|
||||
info_dummy(nullptr)
|
||||
{
|
||||
}
|
||||
AIScannerInfo::AIScannerInfo() = default;
|
||||
AIScannerInfo::~AIScannerInfo() = default;
|
||||
|
||||
void AIScannerInfo::Initialize()
|
||||
{
|
||||
|
@ -39,22 +36,17 @@ void AIScannerInfo::Initialize()
|
|||
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)
|
||||
{
|
||||
return info->GetName();
|
||||
}
|
||||
|
||||
void AIScannerInfo::RegisterAPI(class Squirrel *engine)
|
||||
void AIScannerInfo::RegisterAPI(class Squirrel &engine)
|
||||
{
|
||||
AIInfo::RegisterAPI(engine);
|
||||
}
|
||||
|
@ -63,7 +55,7 @@ AIInfo *AIScannerInfo::SelectRandomAI() const
|
|||
{
|
||||
if (_game_mode == GM_MENU) {
|
||||
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. */
|
||||
|
@ -72,7 +64,7 @@ AIInfo *AIScannerInfo::SelectRandomAI() const
|
|||
uint num_random_ais = std::ranges::distance(random_ais);
|
||||
if (num_random_ais == 0) {
|
||||
Debug(script, 0, "No suitable AI found, loading 'dummy' AI.");
|
||||
return this->info_dummy;
|
||||
return this->info_dummy.get();
|
||||
}
|
||||
|
||||
/* Pick a random AI */
|
||||
|
@ -125,13 +117,13 @@ void AIScannerLibrary::Initialize()
|
|||
ScriptScanner::Initialize("AIScanner");
|
||||
}
|
||||
|
||||
std::string AIScannerLibrary::GetScriptName(ScriptInfo *info)
|
||||
std::string AIScannerLibrary::GetScriptName(ScriptInfo &info)
|
||||
{
|
||||
AILibrary *library = static_cast<AILibrary *>(info);
|
||||
return fmt::format("{}.{}", library->GetCategory(), library->GetInstanceName());
|
||||
AILibrary &library = static_cast<AILibrary &>(info);
|
||||
return fmt::format("{}.{}", library.GetCategory(), library.GetInstanceName());
|
||||
}
|
||||
|
||||
void AIScannerLibrary::RegisterAPI(class Squirrel *engine)
|
||||
void AIScannerLibrary::RegisterAPI(class Squirrel &engine)
|
||||
{
|
||||
AILibrary::RegisterAPI(engine);
|
||||
}
|
||||
|
|
|
@ -37,17 +37,17 @@ public:
|
|||
/**
|
||||
* Set the Dummy AI.
|
||||
*/
|
||||
void SetDummyAI(class AIInfo *info);
|
||||
void SetDummyAI(std::unique_ptr<class AIInfo> &&info);
|
||||
|
||||
protected:
|
||||
std::string GetScriptName(ScriptInfo *info) override;
|
||||
const char *GetFileName() const override { return PATHSEP "info.nut"; }
|
||||
std::string GetScriptName(ScriptInfo &info) override;
|
||||
std::string_view GetFileName() const override { return PATHSEP "info.nut"; }
|
||||
Subdirectory GetDirectory() const override { return AI_DIR; }
|
||||
const char *GetScannerName() const override { return "AIs"; }
|
||||
void RegisterAPI(class Squirrel *engine) override;
|
||||
std::string_view GetScannerName() const override { return "AIs"; }
|
||||
void RegisterAPI(class Squirrel &engine) override;
|
||||
|
||||
private:
|
||||
AIInfo *info_dummy; ///< The dummy AI.
|
||||
std::unique_ptr<AIInfo> info_dummy; ///< The dummy AI.
|
||||
};
|
||||
|
||||
class AIScannerLibrary : public ScriptScanner {
|
||||
|
@ -63,11 +63,11 @@ public:
|
|||
class AILibrary *FindLibrary(const std::string &library, int version);
|
||||
|
||||
protected:
|
||||
std::string GetScriptName(ScriptInfo *info) override;
|
||||
const char *GetFileName() const override { return PATHSEP "library.nut"; }
|
||||
std::string GetScriptName(ScriptInfo &info) override;
|
||||
std::string_view GetFileName() const override { return PATHSEP "library.nut"; }
|
||||
Subdirectory GetDirectory() const override { return AI_LIBRARY_DIR; }
|
||||
const char *GetScannerName() const override { return "AI Libraries"; }
|
||||
void RegisterAPI(class Squirrel *engine) override;
|
||||
std::string_view GetScannerName() const override { return "AI Libraries"; }
|
||||
void RegisterAPI(class Squirrel &engine) override;
|
||||
};
|
||||
|
||||
#endif /* AI_SCANNER_HPP */
|
||||
|
|
|
@ -175,7 +175,7 @@ void Aircraft::GetImage(Direction direction, EngineImageType image_type, Vehicle
|
|||
{
|
||||
uint8_t spritenum = this->spritenum;
|
||||
|
||||
if (is_custom_sprite(spritenum)) {
|
||||
if (IsCustomVehicleSpriteNum(spritenum)) {
|
||||
GetCustomVehicleSprite(this, direction, image_type, result);
|
||||
if (result->IsValid()) return;
|
||||
|
||||
|
@ -191,7 +191,7 @@ void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteS
|
|||
assert(v->subtype == AIR_HELICOPTER);
|
||||
|
||||
const Aircraft *w = v->Next()->Next();
|
||||
if (is_custom_sprite(v->spritenum)) {
|
||||
if (IsCustomVehicleSpriteNum(v->spritenum)) {
|
||||
GetCustomRotorSprite(v, image_type, result);
|
||||
if (result->IsValid()) return;
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ static void GetAircraftIcon(EngineID engine, EngineImageType image_type, Vehicle
|
|||
const Engine *e = Engine::Get(engine);
|
||||
uint8_t spritenum = e->u.air.image_index;
|
||||
|
||||
if (is_custom_sprite(spritenum)) {
|
||||
if (IsCustomVehicleSpriteNum(spritenum)) {
|
||||
GetCustomVehicleIcon(engine, DIR_W, image_type, result);
|
||||
if (result->IsValid()) return;
|
||||
|
||||
|
@ -1430,7 +1430,7 @@ static void AircraftLandAirplane(Aircraft *v)
|
|||
|
||||
v->UpdateDeltaXY();
|
||||
|
||||
AirportTileAnimationTrigger(st, vt, AAT_STATION_AIRPLANE_LAND);
|
||||
TriggerAirportTileAnimation(st, vt, AirportAnimationTrigger::AirplaneTouchdown);
|
||||
|
||||
if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
|
||||
SndPlayVehicleFx(SND_17_SKID_PLANE, v);
|
||||
|
|
|
@ -82,7 +82,7 @@ void DrawAircraftImage(const Vehicle *v, const Rect &r, VehicleID selection, Eng
|
|||
int x_offs = UnScaleGUI(rect.left);
|
||||
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 */
|
||||
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;
|
||||
|
||||
int heli_offs = 0;
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "company_base.h"
|
||||
#include "station_type.h"
|
||||
#include "newgrf_airport.h"
|
||||
#include "newgrf_badge.h"
|
||||
#include "newgrf_badge_gui.h"
|
||||
#include "newgrf_callbacks.h"
|
||||
#include "dropdown_type.h"
|
||||
#include "dropdown_func.h"
|
||||
|
@ -204,9 +204,9 @@ static constexpr NWidgetPart _nested_air_toolbar_widgets[] = {
|
|||
NWidget(WWT_STICKYBOX, COLOUR_DARK_GREEN),
|
||||
EndContainer(),
|
||||
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_PANEL, COLOUR_DARK_GREEN), SetMinimalSize(4, 22), 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_AIRPORT), SetFill(0, 1), SetToolbarMinimalSize(2), SetSpriteTip(SPR_IMG_AIRPORT, STR_TOOLBAR_AIRCRAFT_BUILD_AIRPORT_TOOLTIP),
|
||||
NWidget(WWT_PANEL, COLOUR_DARK_GREEN), SetToolbarSpacerMinimalSize(), SetFill(1, 1), EndContainer(),
|
||||
NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_AT_DEMOLISH), SetFill(0, 1), SetToolbarMinimalSize(1), SetSpriteTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC),
|
||||
EndContainer(),
|
||||
};
|
||||
|
||||
|
@ -401,7 +401,7 @@ public:
|
|||
case WID_AP_AIRPORT_SPRITE:
|
||||
if (this->preview_sprite != 0) {
|
||||
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;
|
||||
|
||||
|
@ -561,7 +561,7 @@ public:
|
|||
this->SelectOtherAirport(-1);
|
||||
}
|
||||
|
||||
void OnDropdownSelect(WidgetID widget, int index) override
|
||||
void OnDropdownSelect(WidgetID widget, int index, int) override
|
||||
{
|
||||
if (widget == WID_AP_CLASS_DROPDOWN) {
|
||||
_selected_airport_class = (AirportClassID)index;
|
||||
|
@ -575,7 +575,7 @@ public:
|
|||
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();
|
||||
}};
|
||||
};
|
||||
|
@ -618,7 +618,7 @@ static constexpr NWidgetPart _nested_build_airport_widgets[] = {
|
|||
};
|
||||
|
||||
static WindowDesc _build_airport_desc(
|
||||
WDP_AUTO, nullptr, 0, 0,
|
||||
WDP_AUTO, {}, 0, 0,
|
||||
WC_BUILD_STATION, WC_BUILD_TOOLBAR,
|
||||
WindowDefaultFlag::Construction,
|
||||
_nested_build_airport_widgets
|
||||
|
|
|
@ -80,20 +80,18 @@ uint CountArticulatedParts(EngineID engine_type, bool purchase_window)
|
|||
* either, so it doesn't matter how many articulated parts there are. */
|
||||
if (!Vehicle::CanAllocateItem()) return 0;
|
||||
|
||||
Vehicle *v = nullptr;
|
||||
std::unique_ptr<Vehicle> v;
|
||||
if (!purchase_window) {
|
||||
v = new Vehicle();
|
||||
v = std::make_unique<Vehicle>();
|
||||
v->engine_type = engine_type;
|
||||
v->owner = _current_company;
|
||||
}
|
||||
|
||||
uint 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;
|
||||
}
|
||||
|
||||
|
@ -432,7 +430,10 @@ void AddArticulatedParts(Vehicle *first)
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@
|
|||
|
||||
#include "safeguards.h"
|
||||
|
||||
/**
|
||||
* @return true, if the textbuf was updated.
|
||||
*/
|
||||
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()) {
|
||||
this->InitSuggestions(this->textbuf->GetText());
|
||||
if (this->suggestions.empty()) {
|
||||
|
@ -29,11 +32,11 @@ bool AutoCompletion::AutoComplete()
|
|||
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()) {
|
||||
this->ApplySuggestion(prefix, this->suggestions[++this->current_suggestion_index]);
|
||||
} else {
|
||||
// We are out of options, restore original text.
|
||||
/* We are out of options, restore original text. */
|
||||
this->textbuf->Assign(initial_buf);
|
||||
this->Reset();
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ public:
|
|||
}
|
||||
virtual ~AutoCompletion() = default;
|
||||
|
||||
// Returns true the textbuf was updated.
|
||||
bool AutoComplete();
|
||||
void Reset();
|
||||
|
||||
|
|
|
@ -185,9 +185,9 @@ static bool VerifyAutoreplaceRefitForOrders(const Vehicle *v, EngineID engine_ty
|
|||
CargoTypes union_refit_mask_b = GetUnionOfArticulatedRefitMasks(engine_type, false);
|
||||
|
||||
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
|
||||
for (const Order *o : u->Orders()) {
|
||||
if (!o->IsRefit() || o->IsAutoRefit()) continue;
|
||||
CargoType cargo_type = o->GetRefitCargo();
|
||||
for (const Order &o : u->Orders()) {
|
||||
if (!o.IsRefit() || o.IsAutoRefit()) continue;
|
||||
CargoType cargo_type = o.GetRefitCargo();
|
||||
|
||||
if (!HasBit(union_refit_mask_a, cargo_type)) continue;
|
||||
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);
|
||||
|
||||
const Order *o;
|
||||
const Vehicle *u = (v->type == VEH_TRAIN) ? v->First() : v;
|
||||
|
||||
const OrderList *orders = u->orders;
|
||||
if (orders == nullptr) return -1;
|
||||
for (VehicleOrderID i = 0; i < orders->GetNumOrders(); i++) {
|
||||
o = orders->GetOrderAt(i);
|
||||
const Order *o = orders->GetOrderAt(i);
|
||||
if (!o->IsRefit()) continue;
|
||||
if (!HasBit(union_refit_mask, o->GetRefitCargo())) return i;
|
||||
}
|
||||
|
@ -373,8 +372,12 @@ 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 */
|
||||
if (new_veh->type == VEH_TRAIN && HasBit(Train::From(old_veh)->flags, VRF_REVERSE_DIRECTION)) {
|
||||
Command<CMD_REVERSE_TRAIN_DIRECTION>::Do(DoCommandFlag::Execute, new_veh->index, true);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
|
|
|
@ -311,7 +311,7 @@ public:
|
|||
|
||||
case WID_RV_LEFT_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;
|
||||
break;
|
||||
|
||||
|
@ -406,11 +406,11 @@ public:
|
|||
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE:
|
||||
if (const Group *g = Group::GetIfValid(this->sel_group); g != nullptr) {
|
||||
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 {
|
||||
const Company *c = Company::Get(_local_company);
|
||||
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;
|
||||
|
||||
|
@ -552,7 +552,7 @@ public:
|
|||
if (g != nullptr) {
|
||||
Command<CMD_SET_GROUP_FLAG>::Post(this->sel_group, GroupFlag::ReplaceWagonRemoval, !g->flags.Test(GroupFlag::ReplaceWagonRemoval), _ctrl_pressed);
|
||||
} 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);
|
||||
}
|
||||
break;
|
||||
|
@ -623,7 +623,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void OnDropdownSelect(WidgetID widget, int index) override
|
||||
void OnDropdownSelect(WidgetID widget, int index, int) override
|
||||
{
|
||||
switch (widget) {
|
||||
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(NWID_VERTICAL),
|
||||
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(),
|
||||
NWidget(NWID_HORIZONTAL),
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
#include "3rdparty/md5/md5.h"
|
||||
#include <unordered_map>
|
||||
|
||||
/* Forward declare these; can't do 'struct X' in functions as older GCCs barf on that */
|
||||
struct IniFile;
|
||||
struct IniGroup;
|
||||
struct IniItem;
|
||||
struct ContentInfo;
|
||||
|
||||
/** Structure holding filename and MD5 information about a single file */
|
||||
|
@ -47,7 +48,7 @@ template <class T> struct BaseSetTraits;
|
|||
*/
|
||||
template <class T>
|
||||
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 */
|
||||
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
|
||||
TranslatedStrings description; ///< Description of the base set
|
||||
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
|
||||
|
||||
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 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.
|
||||
* @return the number
|
||||
|
@ -96,6 +89,9 @@ struct BaseSet {
|
|||
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);
|
||||
void CopyCompatibleConfig([[maybe_unused]] const T &src) {}
|
||||
|
||||
|
@ -107,7 +103,7 @@ struct BaseSet {
|
|||
* @param isocode the isocode to search for
|
||||
* @return the description
|
||||
*/
|
||||
const std::string &GetDescription(const std::string &isocode) const
|
||||
const std::string &GetDescription(std::string_view isocode) const
|
||||
{
|
||||
if (!isocode.empty()) {
|
||||
/* First the full ISO code */
|
||||
|
@ -166,9 +162,9 @@ struct BaseSet {
|
|||
template <class Tbase_set>
|
||||
class BaseMedia : FileScanner {
|
||||
protected:
|
||||
static inline Tbase_set *available_sets = nullptr; ///< 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 const Tbase_set *used_set = nullptr; ///< The currently used set
|
||||
static inline std::vector<std::unique_ptr<Tbase_set>> available_sets; ///< All available sets
|
||||
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; ///< The currently used set
|
||||
|
||||
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.
|
||||
* @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:
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
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 SetSetByName(const std::string &name);
|
||||
|
@ -211,7 +217,7 @@ public:
|
|||
* @param md5sum whether to check the MD5 checksum
|
||||
* @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 md5sum Should the MD5 checksum be tested as well?
|
||||
* @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>
|
||||
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 */
|
||||
|
|
|
@ -14,20 +14,39 @@
|
|||
#include "ini_type.h"
|
||||
#include "string_func.h"
|
||||
#include "error_func.h"
|
||||
#include "core/string_consumer.hpp"
|
||||
#include "3rdparty/fmt/ranges.h"
|
||||
|
||||
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.
|
||||
*/
|
||||
#define fetch_metadata(name) \
|
||||
item = metadata->GetItem(name); \
|
||||
if (item == nullptr || !item->value.has_value() || item->value->empty()) { \
|
||||
Debug(grf, 0, "Base {}set detail loading: {} field missing.", BaseSet::SET_TYPE, name); \
|
||||
Debug(grf, 0, " Is {} readable for the user running OpenTTD?", full_filename); \
|
||||
return false; \
|
||||
}
|
||||
template <class T>
|
||||
const IniItem *BaseSet<T>::GetMandatoryItem(std::string_view full_filename, const IniGroup &group, std::string_view name) const
|
||||
{
|
||||
auto *item = group.GetItem(name);
|
||||
if (item != nullptr && item->value.has_value() && !item->value->empty()) return item;
|
||||
this->LogError(full_filename, fmt::format("{}.{} field missing.", group.name, name));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the set information from a loaded ini.
|
||||
|
@ -42,16 +61,17 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
|
|||
{
|
||||
const IniGroup *metadata = ini.GetGroup("metadata");
|
||||
if (metadata == nullptr) {
|
||||
Debug(grf, 0, "Base {}set detail loading: metadata missing.", BaseSet<T>::SET_TYPE);
|
||||
Debug(grf, 0, " Is {} readable for the user running OpenTTD?", full_filename);
|
||||
this->LogError(full_filename, "Is the file readable for the user running OpenTTD?");
|
||||
return false;
|
||||
}
|
||||
const IniItem *item;
|
||||
|
||||
fetch_metadata("name");
|
||||
item = this->GetMandatoryItem(full_filename, *metadata, "name");
|
||||
if (item == nullptr) return false;
|
||||
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;
|
||||
|
||||
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("");
|
||||
}
|
||||
|
||||
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++) {
|
||||
this->shortname |= ((uint8_t)(*item->value)[i]) << (i * 8);
|
||||
}
|
||||
|
||||
fetch_metadata("version");
|
||||
this->version = atoi(item->value->c_str());
|
||||
item = this->GetMandatoryItem(full_filename, *metadata, "version");
|
||||
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");
|
||||
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 *origin = ini.GetGroup("origin");
|
||||
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++) {
|
||||
MD5File *file = &this->files[i];
|
||||
/* Find the filename first. */
|
||||
item = files != nullptr ? files->GetItem(file_names[i]) : nullptr;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -104,34 +140,19 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
|
|||
/* Then find the MD5 checksum */
|
||||
item = md5s != nullptr ? md5s->GetItem(filename) : nullptr;
|
||||
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;
|
||||
}
|
||||
const char *c = item->value->c_str();
|
||||
for (size_t i = 0; i < file->hash.size() * 2; i++, c++) {
|
||||
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;
|
||||
}
|
||||
if (i % 2 == 0) {
|
||||
file->hash[i / 2] = j << 4;
|
||||
} else {
|
||||
file->hash[i / 2] |= j;
|
||||
}
|
||||
if (!ConvertHexToBytes(*item->value, file->hash)) {
|
||||
this->LogError(full_filename, fmt::format("md5s.{} is malformed: {}", filename, *item->value));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Then find the warning message when the file's missing */
|
||||
item = origin != nullptr ? origin->GetItem(filename) : nullptr;
|
||||
if (item == nullptr) item = origin != nullptr ? origin->GetItem("default") : nullptr;
|
||||
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();
|
||||
} else {
|
||||
file->missing_warning = item->value.value();
|
||||
|
@ -148,12 +169,14 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
|
|||
break;
|
||||
|
||||
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++;
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -164,10 +187,9 @@ bool BaseSet<T>::FillSetDetails(const IniFile &ini, const std::string &path, con
|
|||
template <class Tbase_set>
|
||||
bool BaseMedia<Tbase_set>::AddFile(const std::string &filename, size_t basepath_length, const std::string &)
|
||||
{
|
||||
bool ret = false;
|
||||
Debug(grf, 1, "Checking {} for base {} set", filename, BaseSet<Tbase_set>::SET_TYPE);
|
||||
Debug(misc, 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{};
|
||||
std::string path{ filename, basepath_length };
|
||||
ini.LoadFromDisk(path, BASESET_DIR);
|
||||
|
@ -179,60 +201,44 @@ bool BaseMedia<Tbase_set>::AddFile(const std::string &filename, size_t basepath_
|
|||
path.clear();
|
||||
}
|
||||
|
||||
if (set->FillSetDetails(ini, path, filename)) {
|
||||
Tbase_set *duplicate = nullptr;
|
||||
for (Tbase_set *c = BaseMedia<Tbase_set>::available_sets; c != nullptr; c = c->next) {
|
||||
if (c->name == set->name || c->shortname == set->shortname) {
|
||||
duplicate = c;
|
||||
break;
|
||||
}
|
||||
if (!set->FillSetDetails(ini, path, filename)) return false;
|
||||
|
||||
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 (existing != std::end(BaseMedia<Tbase_set>::available_sets)) {
|
||||
/* The more complete set takes precedence over the version number. */
|
||||
if (((*existing)->valid_files == set->valid_files && (*existing)->version >= set->version) ||
|
||||
(*existing)->valid_files > set->valid_files) {
|
||||
|
||||
Debug(misc, 1, "Not adding {} ({}) as base {} set (duplicate, {})", set->name, fmt::join(set->version, "."),
|
||||
BaseSet<Tbase_set>::SET_TYPE,
|
||||
(*existing)->valid_files > set->valid_files ? "fewer valid files" : "lower version");
|
||||
|
||||
duplicate_sets.push_back(std::move(set));
|
||||
return false;
|
||||
}
|
||||
if (duplicate != nullptr) {
|
||||
/* The more complete set takes precedence over the version number. */
|
||||
if ((duplicate->valid_files == set->valid_files && duplicate->version >= set->version) ||
|
||||
duplicate->valid_files > set->valid_files) {
|
||||
Debug(grf, 1, "Not adding {} ({}) as base {} set (duplicate, {})", set->name, set->version,
|
||||
BaseSet<Tbase_set>::SET_TYPE,
|
||||
duplicate->valid_files > set->valid_files ? "less 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;
|
||||
set->next = duplicate->next;
|
||||
/* 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
|
||||
* version number until a new game is started which isn't a big problem */
|
||||
if (BaseMedia<Tbase_set>::used_set == existing->get()) BaseMedia<Tbase_set>::used_set = set.get();
|
||||
|
||||
/* Keep baseset configuration, if compatible */
|
||||
set->CopyCompatibleConfig(*duplicate);
|
||||
/* Keep baseset configuration, if compatible */
|
||||
set->CopyCompatibleConfig(**existing);
|
||||
|
||||
/* 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
|
||||
* 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;
|
||||
Debug(misc, 1, "Removing {} ({}) as base {} set (duplicate, {})", (*existing)->name, fmt::join((*existing)->version, "."), BaseSet<Tbase_set>::SET_TYPE,
|
||||
(*existing)->valid_files < set->valid_files ? "fewer valid files" : "lower version");
|
||||
|
||||
Debug(grf, 1, "Removing {} ({}) as base {} set (duplicate, {})", duplicate->name, duplicate->version,
|
||||
BaseSet<Tbase_set>::SET_TYPE,
|
||||
duplicate->valid_files < set->valid_files ? "less valid files" : "lower version");
|
||||
duplicate->next = BaseMedia<Tbase_set>::duplicate_sets;
|
||||
BaseMedia<Tbase_set>::duplicate_sets = duplicate;
|
||||
ret = true;
|
||||
}
|
||||
} else {
|
||||
Tbase_set **last = &BaseMedia<Tbase_set>::available_sets;
|
||||
while (*last != nullptr) last = &(*last)->next;
|
||||
/* Existing set is worse, move it to duplicates and replace with the current set. */
|
||||
duplicate_sets.push_back(std::move(*existing));
|
||||
|
||||
*last = set;
|
||||
ret = true;
|
||||
}
|
||||
if (ret) {
|
||||
Debug(grf, 1, "Adding {} ({}) as base {} set", set->name, set->version, BaseSet<Tbase_set>::SET_TYPE);
|
||||
}
|
||||
Debug(misc, 1, "Adding {} ({}) as base {} set", set->name, fmt::join(set->version, "."), BaseSet<Tbase_set>::SET_TYPE);
|
||||
*existing = std::move(set);
|
||||
} else {
|
||||
delete set;
|
||||
Debug(grf, 1, "Adding {} ({}) as base {} set", set->name, set->version, BaseSet<Tbase_set>::SET_TYPE);
|
||||
available_sets.push_back(std::move(set));
|
||||
}
|
||||
|
||||
return ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -264,9 +270,9 @@ template <class Tbase_set>
|
|||
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) {
|
||||
return SetSet(s);
|
||||
return SetSet(s.get());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -284,9 +290,9 @@ template <class Tbase_set>
|
|||
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) {
|
||||
return SetSet(s);
|
||||
return SetSet(s.get());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -300,7 +306,7 @@ template <class Tbase_set>
|
|||
/* 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);
|
||||
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({}));
|
||||
int invalid = s->GetNumInvalid();
|
||||
if (invalid != 0) {
|
||||
|
@ -319,28 +325,28 @@ template <class Tbase_set>
|
|||
|
||||
#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->shortname != ci->unique_id) continue;
|
||||
if (!md5sum) return s->files[0].filename.c_str();
|
||||
if (s->shortname != ci.unique_id) continue;
|
||||
if (!md5sum) return s->files[0].filename;
|
||||
|
||||
MD5Hash md5;
|
||||
for (const auto &file : s->files) {
|
||||
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>
|
||||
/* 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) ||
|
||||
(TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::duplicate_sets) != nullptr);
|
||||
return TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::GetAvailableSets()).has_value() ||
|
||||
TryGetBaseSetFile(ci, md5sum, BaseMedia<Tbase_set>::GetDuplicateSets()).has_value();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -350,12 +356,9 @@ template <class Tbase_set>
|
|||
template <class Tbase_set>
|
||||
/* static */ int BaseMedia<Tbase_set>::GetNumSets()
|
||||
{
|
||||
int n = 0;
|
||||
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) {
|
||||
if (s != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
return std::ranges::count_if(BaseMedia<Tbase_set>::GetAvailableSets(), [](const auto &set) {
|
||||
return set.get() == BaseMedia<Tbase_set>::used_set || set->GetNumMissing() == 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -366,8 +369,8 @@ template <class Tbase_set>
|
|||
/* static */ int BaseMedia<Tbase_set>::GetIndexOfUsedSet()
|
||||
{
|
||||
int n = 0;
|
||||
for (const Tbase_set *s = BaseMedia<Tbase_set>::available_sets; s != nullptr; s = s->next) {
|
||||
if (s == BaseMedia<Tbase_set>::used_set) return n;
|
||||
for (const auto &s : BaseMedia<Tbase_set>::available_sets) {
|
||||
if (s.get() == BaseMedia<Tbase_set>::used_set) return n;
|
||||
if (s->GetNumMissing() != 0) continue;
|
||||
n++;
|
||||
}
|
||||
|
@ -381,9 +384,9 @@ template <class Tbase_set>
|
|||
template <class Tbase_set>
|
||||
/* 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) {
|
||||
if (s != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue;
|
||||
if (index == 0) return s;
|
||||
for (const auto &s : BaseMedia<Tbase_set>::available_sets) {
|
||||
if (s.get() != BaseMedia<Tbase_set>::used_set && s->GetNumMissing() != 0) continue;
|
||||
if (index == 0) return s.get();
|
||||
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 the available sets.
|
||||
* @return The available sets.
|
||||
*/
|
||||
template <class Tbase_set>
|
||||
/* static */ Tbase_set *BaseMedia<Tbase_set>::GetAvailableSets()
|
||||
{
|
||||
return BaseMedia<Tbase_set>::available_sets;
|
||||
}
|
||||
|
|
|
@ -74,9 +74,9 @@ struct BaseStation : StationPool::PoolItem<&_station_pool> {
|
|||
TimerGameCalendar::Date build_date{}; ///< Date of construction
|
||||
|
||||
uint16_t random_bits = 0; ///< Random bits assigned to this station
|
||||
uint8_t waiting_triggers = 0; ///< Waiting triggers (NewGRF) for this station
|
||||
uint8_t cached_anim_triggers = 0; ///< 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.
|
||||
StationRandomTriggers waiting_random_triggers; ///< Waiting triggers (NewGRF), shared by all station parts/tiles, road stops, ... essentially useless and broken by design.
|
||||
StationAnimationTriggers cached_anim_triggers; ///< NOSAVE: Combined animation trigger bitmask, 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_roadstop_cargo_triggers{}; ///< NOSAVE: Combined cargo trigger bitmask for road stops
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@ inline void Blitter_32bppAnim::Draw(const Blitter::BlitterParams *bp, ZoomLevel
|
|||
{
|
||||
const SpriteData *src = (const SpriteData *)bp->sprite;
|
||||
|
||||
const Colour *src_px = (const Colour *)(src->data + src->offset[zoom][0]);
|
||||
const uint16_t *src_n = (const uint16_t *)(src->data + src->offset[zoom][1]);
|
||||
const Colour *src_px = reinterpret_cast<const Colour *>(src->data + src->offset[0][zoom]);
|
||||
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--) {
|
||||
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;
|
||||
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;
|
||||
dst += _screen.pitch;
|
||||
/* 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];
|
||||
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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32_t));
|
||||
std::copy_n(src, width, udst);
|
||||
src += _screen.pitch;
|
||||
udst += width;
|
||||
/* 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];
|
||||
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 th = height - scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memcpy(dst, src, tw * sizeof(uint16_t));
|
||||
src -= this->anim_buf_pitch;
|
||||
dst -= this->anim_buf_pitch;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, tw, th, -this->anim_buf_pitch);
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 th = height + scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memmove(dst, src, tw * sizeof(uint16_t));
|
||||
src += this->anim_buf_pitch;
|
||||
dst += this->anim_buf_pitch;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, tw, th, this->anim_buf_pitch);
|
||||
}
|
||||
|
||||
Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y);
|
||||
|
|
|
@ -190,8 +190,8 @@ bmno_full_transparency:
|
|||
const uint m1 = (uint8_t) (mvX2 >> 16);
|
||||
const uint r1 = remap[m1];
|
||||
if (mvX2 & 0x00FF00FF) {
|
||||
/* Written so the compiler uses CMOV. */
|
||||
#define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
|
||||
/* Written so the compiler uses CMOV. */ \
|
||||
Colour m_colour = m_colour_init; \
|
||||
{ \
|
||||
const Colour srcm = (Colour) (m_src); \
|
||||
|
|
|
@ -39,8 +39,10 @@ public:
|
|||
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(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"; }
|
||||
using Blitter_32bppSSE2_Anim::LookupColourInPalette;
|
||||
|
|
|
@ -51,7 +51,7 @@ void Blitter_32bppBase::CopyFromBuffer(void *video, const void *src, int width,
|
|||
const uint32_t *usrc = (const uint32_t *)src;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(dst, usrc, width * sizeof(uint32_t));
|
||||
std::copy_n(usrc, width, dst);
|
||||
usrc += width;
|
||||
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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32_t));
|
||||
std::copy_n(src, width, udst);
|
||||
src += _screen.pitch;
|
||||
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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32_t));
|
||||
std::copy_n(src, width, udst);
|
||||
src += _screen.pitch;
|
||||
udst += dst_pitch;
|
||||
}
|
||||
|
@ -106,11 +106,7 @@ void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &widt
|
|||
width += scroll_x;
|
||||
}
|
||||
|
||||
for (int h = height; h > 0; h--) {
|
||||
memcpy(dst, src, width * sizeof(uint32_t));
|
||||
src -= _screen.pitch;
|
||||
dst -= _screen.pitch;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, width, height, -_screen.pitch);
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
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;
|
||||
}
|
||||
|
||||
/* the y-displacement may be 0 therefore we have to use memmove,
|
||||
* 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;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, width, height, _screen.pitch);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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',
|
||||
* 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',
|
||||
* then interleaved stream of 'm' and 'n' channels. 'm' is remap,
|
||||
* '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 */
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
*
|
||||
* 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
|
||||
* '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 -
|
||||
* 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 */
|
||||
uint32_t lengths[ZOOM_LVL_END][2];
|
||||
SpriteCollMap<uint32_t> lengths[2];
|
||||
|
||||
ZoomLevel zoom_min;
|
||||
ZoomLevel zoom_max;
|
||||
|
||||
if (sprite[ZOOM_LVL_MIN].type == SpriteType::Font) {
|
||||
zoom_min = ZOOM_LVL_MIN;
|
||||
zoom_max = ZOOM_LVL_MIN;
|
||||
if (sprite_type == SpriteType::Font) {
|
||||
zoom_min = ZoomLevel::Min;
|
||||
zoom_max = ZoomLevel::Min;
|
||||
} else {
|
||||
zoom_min = _settings_client.gui.zoom_min;
|
||||
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++) {
|
||||
|
@ -405,40 +406,43 @@ template <bool Tpal_to_rgb> Sprite *Blitter_32bppOptimized::EncodeInternal(const
|
|||
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[z][1] = reinterpret_cast<uint8_t *>(dst_n_ln) - reinterpret_cast<uint8_t *>(dst_n_orig[z].get());
|
||||
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[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
|
||||
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);
|
||||
|
||||
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
|
||||
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
|
||||
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
|
||||
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
|
||||
const auto &root_sprite = sprite.Root();
|
||||
dest_sprite->height = root_sprite.height;
|
||||
dest_sprite->width = root_sprite.width;
|
||||
dest_sprite->x_offs = root_sprite.x_offs;
|
||||
dest_sprite->y_offs = root_sprite.y_offs;
|
||||
|
||||
SpriteData *dst = (SpriteData *)dest_sprite->data;
|
||||
memset(dst, 0, sizeof(*dst));
|
||||
|
||||
uint32_t offset = 0;
|
||||
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[z][1] = lengths[z][0] + dst->offset[z][0];
|
||||
dst->offset[0][z] = offset;
|
||||
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]);
|
||||
memcpy(dst->data + dst->offset[z][1], dst_n_orig[z].get(), lengths[z][1]);
|
||||
std::copy_n(reinterpret_cast<uint8_t *>(dst_px_orig[z].get()), lengths[0][z], dst->data + dst->offset[0][z]);
|
||||
std::copy_n(reinterpret_cast<uint8_t *>(dst_n_orig[z].get()), lengths[1][z], dst->data + dst->offset[1][z]);
|
||||
}
|
||||
|
||||
return dest_sprite;
|
||||
}
|
||||
|
||||
template Sprite *Blitter_32bppOptimized::EncodeInternal<true>(const SpriteLoader::SpriteCollection &sprite, SpriteAllocator &allocator);
|
||||
template Sprite *Blitter_32bppOptimized::EncodeInternal<false>(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>(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);
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@ class Blitter_32bppOptimized : public Blitter_32bppSimple {
|
|||
public:
|
||||
/** Data stored about a (single) sprite. */
|
||||
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.
|
||||
};
|
||||
|
||||
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"; }
|
||||
|
||||
|
@ -30,7 +30,7 @@ public:
|
|||
|
||||
protected:
|
||||
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). */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
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;
|
||||
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->width = sprite[ZOOM_LVL_MIN].width;
|
||||
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
|
||||
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
|
||||
dest_sprite->height = root_sprite.height;
|
||||
dest_sprite->width = root_sprite.width;
|
||||
dest_sprite->x_offs = root_sprite.x_offs;
|
||||
dest_sprite->y_offs = root_sprite.y_offs;
|
||||
|
||||
dst = (Blitter_32bppSimple::Pixel *)dest_sprite->data;
|
||||
SpriteLoader::CommonPixel *src = (SpriteLoader::CommonPixel *)sprite[ZOOM_LVL_MIN].data;
|
||||
dst = reinterpret_cast<Blitter_32bppSimple::Pixel *>(dest_sprite->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) {
|
||||
dst[i].r = src->r;
|
||||
dst[i].g = src->g;
|
||||
|
|
|
@ -26,7 +26,7 @@ class Blitter_32bppSimple : public Blitter_32bppBase {
|
|||
public:
|
||||
void Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) 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"; }
|
||||
};
|
||||
|
|
|
@ -20,18 +20,18 @@
|
|||
/** Instantiation of the SSE2 32bpp blitter factory. */
|
||||
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.
|
||||
* Second uint32_t of a line = the number of transparent pixels from the right.
|
||||
* Then all RGBA then all MV.
|
||||
*/
|
||||
ZoomLevel zoom_min = ZOOM_LVL_MIN;
|
||||
ZoomLevel zoom_max = ZOOM_LVL_MIN;
|
||||
if (sprite[ZOOM_LVL_MIN].type != SpriteType::Font) {
|
||||
ZoomLevel zoom_min = ZoomLevel::Min;
|
||||
ZoomLevel zoom_max = ZoomLevel::Min;
|
||||
if (sprite_type != SpriteType::Font) {
|
||||
zoom_min = _settings_client.gui.zoom_min;
|
||||
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. */
|
||||
|
@ -39,23 +39,25 @@ Sprite *Blitter_32bppSSE_Base::Encode(const SpriteLoader::SpriteCollection &spri
|
|||
uint all_sprites_size = 0;
|
||||
for (ZoomLevel z = zoom_min; z <= zoom_max; z++) {
|
||||
const SpriteLoader::Sprite *src_sprite = &sprite[z];
|
||||
sd.infos[z].sprite_width = src_sprite->width;
|
||||
sd.infos[z].sprite_offset = all_sprites_size;
|
||||
sd.infos[z].sprite_line_size = sizeof(Colour) * src_sprite->width + sizeof(uint32_t) * META_LENGTH;
|
||||
auto &info = sd.infos[z];
|
||||
info.sprite_width = src_sprite->width;
|
||||
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;
|
||||
sd.infos[z].mv_offset = all_sprites_size + rgba_size;
|
||||
const uint rgba_size = info.sprite_line_size * src_sprite->height;
|
||||
info.mv_offset = all_sprites_size + rgba_size;
|
||||
|
||||
const uint mv_size = sizeof(MapValue) * src_sprite->width * src_sprite->height;
|
||||
all_sprites_size += rgba_size + mv_size;
|
||||
}
|
||||
|
||||
Sprite *dst_sprite = allocator.Allocate<Sprite>(sizeof(Sprite) + sizeof(SpriteData) + all_sprites_size);
|
||||
dst_sprite->height = sprite[ZOOM_LVL_MIN].height;
|
||||
dst_sprite->width = sprite[ZOOM_LVL_MIN].width;
|
||||
dst_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
|
||||
dst_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
|
||||
memcpy(dst_sprite->data, &sd, sizeof(SpriteData));
|
||||
const auto &root_sprite = sprite.Root();
|
||||
dst_sprite->height = root_sprite.height;
|
||||
dst_sprite->width = root_sprite.width;
|
||||
dst_sprite->x_offs = root_sprite.x_offs;
|
||||
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. */
|
||||
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++) {
|
||||
const SpriteLoader::Sprite *src_sprite = &sprite[z];
|
||||
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];
|
||||
MapValue *dst_mv = (MapValue *) &dst_sprite->data[sizeof(SpriteData) + sd.infos[z].mv_offset];
|
||||
const auto &info = sd.infos[z];
|
||||
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--) {
|
||||
Colour *dst_rgba = dst_rgba_line + META_LENGTH;
|
||||
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;
|
||||
|
||||
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. */
|
||||
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_remap) sd.flags.Set(SpriteFlag::NoRemap);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -73,11 +73,11 @@ public:
|
|||
};
|
||||
struct SpriteData {
|
||||
SpriteFlags flags{};
|
||||
std::array<SpriteInfo, ZOOM_LVL_END> infos{};
|
||||
SpriteCollMap<SpriteInfo> infos{};
|
||||
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). */
|
||||
|
@ -87,8 +87,9 @@ public:
|
|||
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);
|
||||
|
||||
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-sse2"; }
|
||||
|
|
|
@ -309,8 +309,8 @@ inline void Blitter_32bppSSE4::Draw(const Blitter::BlitterParams *bp, ZoomLevel
|
|||
|
||||
/* Remap colours. */
|
||||
if (mvX2 & 0x00FF00FF) {
|
||||
/* Written so the compiler uses CMOV. */
|
||||
#define CMOV_REMAP(m_colour, m_colour_init, m_src, m_m) \
|
||||
/* Written so the compiler uses CMOV. */ \
|
||||
Colour m_colour = m_colour_init; \
|
||||
{ \
|
||||
const Colour srcm = (Colour) (m_src); \
|
||||
|
|
|
@ -28,18 +28,10 @@
|
|||
#endif
|
||||
|
||||
#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.
|
||||
|
||||
#undef ALIGN
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define ALIGN(n) __declspec(align(n))
|
||||
#else
|
||||
#define ALIGN(n) __attribute__ ((aligned (n)))
|
||||
#endif
|
||||
|
||||
typedef union ALIGN(16) um128i {
|
||||
typedef union alignas(16) um128i {
|
||||
__m128i m128i;
|
||||
uint8_t m128i_u8[16];
|
||||
uint16_t m128i_u16[8];
|
||||
|
|
|
@ -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',
|
||||
* 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',
|
||||
* then interleaved stream of 'm' and 'n' channels. 'm' is remap,
|
||||
* '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 */
|
||||
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;
|
||||
do {
|
||||
for (int i = 0; i != width; i++) {
|
||||
if (*anim == 0) *udst = MakeGrey(*udst);
|
||||
*anim = remap[*anim];
|
||||
if (*anim == 0) {
|
||||
*udst = MakeGrey(*udst);
|
||||
} else {
|
||||
*anim = remap[*anim];
|
||||
}
|
||||
udst++;
|
||||
anim++;
|
||||
}
|
||||
|
@ -389,7 +392,7 @@ void Blitter_40bppAnim::DrawColourMappingRect(void *dst, int width, int height,
|
|||
const uint8_t *remap = GetNonSprite(pal, SpriteType::Recolour) + 1;
|
||||
do {
|
||||
for (int i = 0; i != width; i++) {
|
||||
*anim = remap[*anim];
|
||||
if (*anim != 0) *anim = remap[*anim];
|
||||
anim++;
|
||||
}
|
||||
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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(dst, usrc, width * sizeof(uint32_t));
|
||||
std::copy_n(usrc, width, dst);
|
||||
usrc += width;
|
||||
dst += _screen.pitch;
|
||||
/* 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);
|
||||
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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint32_t));
|
||||
std::copy_n(src, width, udst);
|
||||
src += _screen.pitch;
|
||||
udst += width;
|
||||
/* 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);
|
||||
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 th = height - scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memcpy(dst, src, tw * sizeof(uint8_t));
|
||||
src -= _screen.pitch;
|
||||
dst -= _screen.pitch;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, tw, th, -_screen.pitch);
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 th = height + scroll_y;
|
||||
for (; th > 0; th--) {
|
||||
memmove(dst, src, tw * sizeof(uint8_t));
|
||||
src += _screen.pitch;
|
||||
dst += _screen.pitch;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, tw, th, _screen.pitch);
|
||||
}
|
||||
|
||||
Blitter_32bppBase::ScrollBuffer(video, left, top, width, height, scroll_x, scroll_y);
|
||||
|
|
|
@ -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 Draw(Blitter::BlitterParams *bp, BlitterMode mode, ZoomLevel zoom) 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;
|
||||
Blitter::PaletteAnimation UsePaletteAnimation() override;
|
||||
bool NeedsAnimationBuffer() override;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
std::byte *p = static_cast<std::byte *>(video);
|
||||
do {
|
||||
memset(video, colour, width);
|
||||
video = (uint8_t *)video + _screen.pitch;
|
||||
std::fill_n(p, width, static_cast<std::byte>(colour));
|
||||
p += _screen.pitch;
|
||||
} 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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(dst, usrc, width * sizeof(uint8_t));
|
||||
std::copy_n(usrc, width, dst);
|
||||
usrc += width;
|
||||
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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint8_t));
|
||||
std::copy_n(src, width, udst);
|
||||
src += _screen.pitch;
|
||||
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;
|
||||
|
||||
for (; height > 0; height--) {
|
||||
memcpy(udst, src, width * sizeof(uint8_t));
|
||||
std::copy_n(src, width, udst);
|
||||
src += _screen.pitch;
|
||||
udst += dst_pitch;
|
||||
}
|
||||
|
@ -110,11 +111,7 @@ void Blitter_8bppBase::ScrollBuffer(void *video, int &left, int &top, int &width
|
|||
width += scroll_x;
|
||||
}
|
||||
|
||||
for (int h = height; h > 0; h--) {
|
||||
memcpy(dst, src, width * sizeof(uint8_t));
|
||||
src -= _screen.pitch;
|
||||
dst -= _screen.pitch;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, width, height, -_screen.pitch);
|
||||
} else {
|
||||
/* Calculate pointers */
|
||||
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;
|
||||
}
|
||||
|
||||
/* the y-displacement may be 0 therefore we have to use memmove,
|
||||
* 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;
|
||||
}
|
||||
Blitter::MovePixels(src, dst, width, height, _screen.pitch);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "../zoom_func.h"
|
||||
#include "../settings_type.h"
|
||||
#include "../core/math_func.hpp"
|
||||
#include "../core/mem_func.hpp"
|
||||
#include "8bpp_optimized.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
@ -96,7 +95,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z
|
|||
}
|
||||
|
||||
case BlitterMode::BlackRemap:
|
||||
MemSetT(dst, 0, pixels);
|
||||
std::fill_n(dst, pixels, 0);
|
||||
dst += pixels;
|
||||
break;
|
||||
|
||||
|
@ -112,7 +111,7 @@ void Blitter_8bppOptimized::Draw(Blitter::BlitterParams *bp, BlitterMode mode, Z
|
|||
}
|
||||
|
||||
default:
|
||||
MemCpyT(dst, src, pixels);
|
||||
std::copy_n(src, pixels, dst);
|
||||
dst += pixels; src += pixels;
|
||||
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 */
|
||||
uint memory = sizeof(SpriteData);
|
||||
|
@ -128,13 +127,13 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
|
|||
ZoomLevel zoom_min;
|
||||
ZoomLevel zoom_max;
|
||||
|
||||
if (sprite[ZOOM_LVL_MIN].type == SpriteType::Font) {
|
||||
zoom_min = ZOOM_LVL_MIN;
|
||||
zoom_max = ZOOM_LVL_MIN;
|
||||
if (sprite_type == SpriteType::Font) {
|
||||
zoom_min = ZoomLevel::Min;
|
||||
zoom_max = ZoomLevel::Min;
|
||||
} else {
|
||||
zoom_min = _settings_client.gui.zoom_min;
|
||||
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++) {
|
||||
|
@ -153,13 +152,14 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
|
|||
|
||||
/* Make the sprites per zoom-level */
|
||||
for (ZoomLevel i = zoom_min; i <= zoom_max; i++) {
|
||||
const SpriteLoader::Sprite &src_orig = sprite[i];
|
||||
/* Store the index table */
|
||||
uint offset = dst - temp_dst->data;
|
||||
temp_dst->offset[i] = offset;
|
||||
|
||||
/* cache values, because compiler can't cache it */
|
||||
int scaled_height = sprite[i].height;
|
||||
int scaled_width = sprite[i].width;
|
||||
int scaled_height = src_orig.height;
|
||||
int scaled_width = src_orig.width;
|
||||
|
||||
for (int y = 0; y < scaled_height; y++) {
|
||||
uint trans = 0;
|
||||
|
@ -168,7 +168,7 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
|
|||
uint8_t *count_dst = nullptr;
|
||||
|
||||
/* 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++) {
|
||||
uint colour = src++->m;
|
||||
|
@ -220,11 +220,12 @@ Sprite *Blitter_8bppOptimized::Encode(const SpriteLoader::SpriteCollection &spri
|
|||
/* Allocate the exact amount of memory we need */
|
||||
Sprite *dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite) + size);
|
||||
|
||||
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
|
||||
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
|
||||
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
|
||||
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
|
||||
memcpy(dest_sprite->data, temp_dst, size);
|
||||
const auto &root_sprite = sprite.Root();
|
||||
dest_sprite->height = root_sprite.height;
|
||||
dest_sprite->width = root_sprite.width;
|
||||
dest_sprite->x_offs = root_sprite.x_offs;
|
||||
dest_sprite->y_offs = root_sprite.y_offs;
|
||||
std::copy_n(reinterpret_cast<std::byte *>(temp_dst), size, dest_sprite->data);
|
||||
|
||||
return dest_sprite;
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@ class Blitter_8bppOptimized final : public Blitter_8bppBase {
|
|||
public:
|
||||
/** Data stored about a (single) sprite. */
|
||||
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.
|
||||
};
|
||||
|
||||
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"; }
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
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->width = sprite[ZOOM_LVL_MIN].width;
|
||||
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
|
||||
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
|
||||
dest_sprite->height = root_sprite.height;
|
||||
dest_sprite->width = root_sprite.width;
|
||||
dest_sprite->x_offs = root_sprite.x_offs;
|
||||
dest_sprite->y_offs = root_sprite.y_offs;
|
||||
|
||||
/* 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++) {
|
||||
dest_sprite->data[i] = sprite[ZOOM_LVL_MIN].data[i].m;
|
||||
uint8_t *dst = reinterpret_cast<uint8_t *>(dest_sprite->data);
|
||||
for (int i = 0; i < root_sprite.height * root_sprite.width; i++) {
|
||||
dst[i] = root_sprite.data[i].m;
|
||||
}
|
||||
|
||||
return dest_sprite;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
class Blitter_8bppSimple final : public Blitter_8bppBase {
|
||||
public:
|
||||
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"; }
|
||||
};
|
||||
|
|
|
@ -207,6 +207,8 @@ public:
|
|||
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 T> static void MovePixels(const T *src, T *dst, size_t width, size_t height, ptrdiff_t pitch);
|
||||
};
|
||||
|
||||
#endif /* BLITTER_BASE_HPP */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -55,7 +55,7 @@ protected:
|
|||
* @pre description != nullptr.
|
||||
* @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)
|
||||
{
|
||||
if (usable) {
|
||||
|
@ -93,7 +93,7 @@ public:
|
|||
* @param name the blitter to select.
|
||||
* @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);
|
||||
if (b == nullptr) return nullptr;
|
||||
|
@ -109,17 +109,17 @@ public:
|
|||
* @param name the blitter factory to select.
|
||||
* @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)
|
||||
const std::string_view default_blitter = "null";
|
||||
static const std::string_view default_blitter = "null";
|
||||
#elif defined(WITH_COCOA)
|
||||
const std::string_view default_blitter = "32bpp-anim";
|
||||
static const std::string_view default_blitter = "32bpp-anim";
|
||||
#else
|
||||
const std::string_view default_blitter = "8bpp-optimized";
|
||||
static const std::string_view default_blitter = "8bpp-optimized";
|
||||
#endif
|
||||
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()) {
|
||||
BlitterFactory *b = it.second;
|
||||
|
|
|
@ -15,15 +15,16 @@
|
|||
/** Instantiation of the null blitter factory. */
|
||||
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;
|
||||
dest_sprite = allocator.Allocate<Sprite>(sizeof(*dest_sprite));
|
||||
|
||||
dest_sprite->height = sprite[ZOOM_LVL_MIN].height;
|
||||
dest_sprite->width = sprite[ZOOM_LVL_MIN].width;
|
||||
dest_sprite->x_offs = sprite[ZOOM_LVL_MIN].x_offs;
|
||||
dest_sprite->y_offs = sprite[ZOOM_LVL_MIN].y_offs;
|
||||
const auto &root_sprite = sprite.Root();
|
||||
dest_sprite->height = root_sprite.height;
|
||||
dest_sprite->width = root_sprite.width;
|
||||
dest_sprite->x_offs = root_sprite.x_offs;
|
||||
dest_sprite->y_offs = root_sprite.y_offs;
|
||||
|
||||
return dest_sprite;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
uint8_t GetScreenDepth() override { return 0; }
|
||||
void Draw(Blitter::BlitterParams *, BlitterMode, ZoomLevel) 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 SetPixel(void *, int, int, uint8_t) override {};
|
||||
void DrawRect(void *, int, int, uint8_t) override {};
|
||||
|
|
|
@ -42,7 +42,7 @@ static constexpr NWidgetPart _background_widgets[] = {
|
|||
* Window description for the background window to prevent smearing.
|
||||
*/
|
||||
static WindowDesc _background_desc(
|
||||
WDP_MANUAL, nullptr, 0, 0,
|
||||
WDP_MANUAL, {}, 0, 0,
|
||||
WC_BOOTSTRAP, WC_NONE,
|
||||
WindowDefaultFlag::NoClose,
|
||||
_background_widgets
|
||||
|
@ -78,7 +78,7 @@ static constexpr NWidgetPart _nested_bootstrap_errmsg_widgets[] = {
|
|||
|
||||
/** Window description for the error window. */
|
||||
static WindowDesc _bootstrap_errmsg_desc(
|
||||
WDP_CENTER, nullptr, 0, 0,
|
||||
WDP_CENTER, {}, 0, 0,
|
||||
WC_BOOTSTRAP, WC_NONE,
|
||||
{WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose},
|
||||
_nested_bootstrap_errmsg_widgets
|
||||
|
@ -135,7 +135,7 @@ static constexpr NWidgetPart _nested_bootstrap_download_status_window_widgets[]
|
|||
|
||||
/** Window description for the download window */
|
||||
static WindowDesc _bootstrap_download_status_window_desc(
|
||||
WDP_CENTER, nullptr, 0, 0,
|
||||
WDP_CENTER, {}, 0, 0,
|
||||
WC_NETWORK_STATUS_WINDOW, WC_NONE,
|
||||
{WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose},
|
||||
_nested_bootstrap_download_status_window_widgets
|
||||
|
@ -187,7 +187,7 @@ static constexpr NWidgetPart _bootstrap_query_widgets[] = {
|
|||
|
||||
/** The window description for the query. */
|
||||
static WindowDesc _bootstrap_query_desc(
|
||||
WDP_CENTER, nullptr, 0, 0,
|
||||
WDP_CENTER, {}, 0, 0,
|
||||
WC_CONFIRM_POPUP_QUERY, WC_NONE,
|
||||
WindowDefaultFlag::NoClose,
|
||||
_bootstrap_query_widgets
|
||||
|
@ -273,10 +273,10 @@ public:
|
|||
_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. */
|
||||
_network_content_client.Select(ci->id);
|
||||
_network_content_client.Select(ci.id);
|
||||
new BootstrapContentDownloadStatusWindow();
|
||||
this->Close();
|
||||
}
|
||||
|
@ -320,19 +320,19 @@ public:
|
|||
_network_content_client.RequestContentList(CONTENT_TYPE_BASE_GRAPHICS);
|
||||
}
|
||||
|
||||
void OnReceiveContentInfo(const ContentInfo *ci) override
|
||||
void OnReceiveContentInfo(const ContentInfo &ci) override
|
||||
{
|
||||
if (this->downloading) return;
|
||||
|
||||
/* 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);
|
||||
this->downloading = true;
|
||||
|
||||
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. */
|
||||
if (bytes < 0) {
|
||||
|
|
|
@ -205,7 +205,7 @@ public:
|
|||
sprite_dim = maxdim(sprite_dim, GetScaledSpriteSize(bridge_data.spec->sprite));
|
||||
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.
|
||||
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) {
|
||||
const BridgeSpec *b = it->spec;
|
||||
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);
|
||||
}
|
||||
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) {
|
||||
this->bridges.SetSortType(index);
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "company_func.h"
|
||||
#include "vehicle_gui.h"
|
||||
#include "newgrf_badge.h"
|
||||
#include "newgrf_badge_config.h"
|
||||
#include "newgrf_badge_gui.h"
|
||||
#include "newgrf_engine.h"
|
||||
#include "newgrf_text.h"
|
||||
#include "group.h"
|
||||
|
@ -72,10 +74,13 @@ static constexpr NWidgetPart _nested_build_vehicle_widgets[] = {
|
|||
NWidget(NWID_HORIZONTAL),
|
||||
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_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(),
|
||||
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),
|
||||
EndContainer(),
|
||||
NWidget(NWID_VERTICAL, NWidContainerFlag{}, WID_BV_BADGE_FILTER),
|
||||
EndContainer(),
|
||||
EndContainer(),
|
||||
/* Vehicle list. */
|
||||
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)
|
||||
{
|
||||
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;
|
||||
const GRFFile *grffile = Engine::Get(engine)->GetGRF();
|
||||
assert(grffile != nullptr);
|
||||
if (callback == 0x40F) {
|
||||
return GetGRFStringWithTextStack(grffile, static_cast<GRFStringID>(regs100[0]), std::span{regs100}.subspan(1));
|
||||
}
|
||||
if (callback > 0x400) {
|
||||
ErrorUnknownCallbackResult(grffile->grfid, CBID_VEHICLE_ADDITIONAL_TEXT, callback);
|
||||
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;
|
||||
}
|
||||
/* 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);
|
||||
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. */
|
||||
struct BuildVehicleWindow : 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.
|
||||
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
|
||||
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()
|
||||
{
|
||||
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->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 */
|
||||
|
@ -1352,6 +1367,7 @@ struct BuildVehicleWindow : Window {
|
|||
list.clear();
|
||||
|
||||
BadgeTextFilter btf(this->string_filter, GSF_TRAINS);
|
||||
BadgeDropdownFilter bdf(this->badge_filter_choices);
|
||||
|
||||
/* Make list of all available train engines and wagons.
|
||||
* 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 */
|
||||
if (!FilterSingleEngine(eid)) continue;
|
||||
|
||||
if (!bdf.Filter(e->badges)) continue;
|
||||
|
||||
/* Filter by name or NewGRF extra text */
|
||||
if (!FilterByText(e) && !btf.Filter(e->badges)) continue;
|
||||
|
||||
|
@ -1573,6 +1591,12 @@ struct BuildVehicleWindow : Window {
|
|||
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()
|
||||
{
|
||||
EngineID sel_eng = this->sel_engine;
|
||||
|
@ -1656,6 +1680,11 @@ struct BuildVehicleWindow : Window {
|
|||
ShowDropDownList(this, this->BuildCargoDropDownList(), this->cargo_filter_criteria, widget);
|
||||
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: {
|
||||
const Engine *e = (this->sel_engine == EngineID::Invalid()) ? nullptr : Engine::Get(this->sel_engine);
|
||||
if (e != nullptr) {
|
||||
|
@ -1676,6 +1705,12 @@ struct BuildVehicleWindow : Window {
|
|||
}
|
||||
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:
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1734,7 +1773,7 @@ struct BuildVehicleWindow : Window {
|
|||
{
|
||||
switch (widget) {
|
||||
case WID_BV_LIST:
|
||||
resize.height = GetEngineListHeight(this->vehicle_type);
|
||||
fill.height = resize.height = GetEngineListHeight(this->vehicle_type);
|
||||
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;
|
||||
break;
|
||||
|
@ -1755,6 +1794,11 @@ struct BuildVehicleWindow : Window {
|
|||
size.width = std::max(size.width, GetDropDownListDimension(this->BuildCargoDropDownList()).width + padding.width);
|
||||
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:
|
||||
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));
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
void OnDropdownSelect(WidgetID widget, int index) override
|
||||
void OnDropdownSelect(WidgetID widget, int index, int click_result) override
|
||||
{
|
||||
switch (widget) {
|
||||
case WID_BV_SORT_DROPDOWN:
|
||||
|
@ -1850,6 +1894,31 @@ struct BuildVehicleWindow : Window {
|
|||
this->SelectEngine(this->sel_engine);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
@ -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", {
|
||||
Hotkey('F', "focus_filter_box", BVHK_FOCUS_FILTER_BOX),
|
||||
Hotkey('F', "focus_filter_box", WID_BV_FILTER),
|
||||
}};
|
||||
};
|
||||
|
||||
|
|
|
@ -74,9 +74,8 @@ void CheckCaches()
|
|||
for (const RoadStop *rs : RoadStop::Iterate()) {
|
||||
if (IsBayRoadStopTile(rs->xy)) continue;
|
||||
|
||||
assert(rs->GetEntry(DIAGDIR_NE) != rs->GetEntry(DIAGDIR_NW));
|
||||
rs->GetEntry(DIAGDIR_NE)->CheckIntegrity(rs);
|
||||
rs->GetEntry(DIAGDIR_NW)->CheckIntegrity(rs);
|
||||
rs->GetEntry(DIAGDIR_NE).CheckIntegrity(rs);
|
||||
rs->GetEntry(DIAGDIR_NW).CheckIntegrity(rs);
|
||||
}
|
||||
|
||||
std::vector<NewGRFCache> grf_cache;
|
||||
|
|
|
@ -18,5 +18,5 @@ Cheats _cheats;
|
|||
/** Reinitialise all the cheats. */
|
||||
void InitializeCheats()
|
||||
{
|
||||
memset(&_cheats, 0, sizeof(Cheats));
|
||||
_cheats = {};
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
#include "cheat_type.h"
|
||||
|
||||
extern Cheats _cheats;
|
||||
|
||||
void ShowCheatWindow();
|
||||
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "timer/timer.h"
|
||||
#include "timer/timer_game_calendar.h"
|
||||
#include "timer/timer_game_economy.h"
|
||||
#include "core/string_consumer.hpp"
|
||||
|
||||
#include "widgets/cheat_widget.h"
|
||||
|
||||
|
@ -286,7 +287,7 @@ struct CheatWindow : Window {
|
|||
case SLE_BOOL: {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
|
@ -354,7 +355,7 @@ struct CheatWindow : Window {
|
|||
int32_t value = sd->Read(&GetGameSettings());
|
||||
if (sd->IsBoolSetting()) {
|
||||
/* 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)) {
|
||||
/* Draw [v] button for settings of an enum-type */
|
||||
DrawDropDownButton(buttons.left, buttons.top, COLOUR_YELLOW, state != 0, editable);
|
||||
|
@ -607,12 +608,13 @@ struct CheatWindow : Window {
|
|||
|
||||
int32_t value;
|
||||
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 */
|
||||
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 {
|
||||
value = sd->GetDefaultValue();
|
||||
}
|
||||
|
@ -621,18 +623,19 @@ struct CheatWindow : Window {
|
|||
} else {
|
||||
const CheatEntry *ce = &_cheats_ui[clicked_cheat];
|
||||
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;
|
||||
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->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();
|
||||
}};
|
||||
};
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
* Info about each of the cheats.
|
||||
*/
|
||||
struct Cheat {
|
||||
bool been_used; ///< has this cheat been used before?
|
||||
bool value; ///< tells if the bool cheat is active or not
|
||||
bool been_used = false; ///< has this cheat been used before?
|
||||
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!
|
||||
*/
|
||||
struct Cheats {
|
||||
Cheat magic_bulldozer; ///< dynamite industries, objects
|
||||
Cheat switch_company; ///< change to another company
|
||||
Cheat money; ///< get rich or poor
|
||||
Cheat crossing_tunnels; ///< allow tunnels that cross each other
|
||||
Cheat no_jetcrash; ///< no jet will crash on small airports anymore
|
||||
Cheat change_date; ///< changes date ingame
|
||||
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 station_rating; ///< Fix station ratings at 100%
|
||||
Cheat magic_bulldozer{}; ///< dynamite industries, objects
|
||||
Cheat switch_company{}; ///< change to another company
|
||||
Cheat money{}; ///< get rich or poor
|
||||
Cheat crossing_tunnels{}; ///< allow tunnels that cross each other
|
||||
Cheat no_jetcrash{}; ///< no jet will crash on small airports anymore
|
||||
Cheat change_date{}; ///< changes date ingame
|
||||
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 station_rating{}; ///< Fix station ratings at 100%
|
||||
};
|
||||
|
||||
extern Cheats _cheats;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue