1
0
Fork 0

Compare commits

..

2 Commits

Author SHA1 Message Date
Peter Nelson b96da4d7a0
Change: Automatically push/pop colours when formatting a sub-string.
Reverts 226a44bf86.

This universally prevents the sub-string from changing colours in the outer string.
2025-04-15 19:31:09 +01:00
Peter Nelson ffed3c7f1c
Change: Don't replace stripped control codes with '?' for scripts. 2025-04-15 19:31:08 +01:00
809 changed files with 22457 additions and 23604 deletions

View File

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

View File

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

View File

@ -86,7 +86,7 @@ jobs:
echo "::endgroup::" echo "::endgroup::"
- name: Import code signing certificates - name: Import code signing certificates
uses: Apple-Actions/import-codesign-certs@v5 uses: Apple-Actions/import-codesign-certs@v3
with: with:
# The certificates in a PKCS12 file encoded as a base64 string # The certificates in a PKCS12 file encoded as a base64 string
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }} p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}

View File

@ -77,7 +77,7 @@ jobs:
curl -L https://cdn.openttd.org/openmsx-releases/0.4.2/openmsx-0.4.2-all.zip -o openmsx-all.zip curl -L https://cdn.openttd.org/openmsx-releases/0.4.2/openmsx-0.4.2-all.zip -o openmsx-all.zip
echo "::endgroup::" echo "::endgroup::"
echo "::group::Unpack OpenMSX" echo "::group::Unpack OpenGFX"
unzip openmsx-all.zip unzip openmsx-all.zip
tar xf openmsx-*.tar tar xf openmsx-*.tar
echo "::endgroup::" echo "::endgroup::"

View File

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

View File

@ -36,11 +36,3 @@ AITown.FoundTown <- function(tile, size, city, layout, name) { return AITown.Fou
AIVehicle.SetNameCompat14 <- AIVehicle.SetName; AIVehicle.SetNameCompat14 <- AIVehicle.SetName;
AIVehicle.SetName <- function(id, name) { return AIVehicle.SetNameCompat14(id, AICompat14.Text(name)); } AIVehicle.SetName <- function(id, name) { return AIVehicle.SetNameCompat14(id, AICompat14.Text(name)); }
AIObject.constructorCompat14 <- AIObject.constructor;
foreach(name, object in CompatScriptRootTable) {
if (type(object) != "class") continue;
if (!object.rawin("constructor")) continue;
if (object.constructor != AIObject.constructorCompat14) continue;
object.constructor <- function() : (name) { AILog.Error("'" + name + "' is not instantiable"); }
}

View File

@ -9,9 +9,6 @@
GSBridge.GetBridgeID <- GSBridge.GetBridgeType; GSBridge.GetBridgeID <- GSBridge.GetBridgeType;
/* Emulate old GSText parameter padding behaviour */
GSText.SCRIPT_TEXT_MAX_PARAMETERS <- 20;
class GSCompat14 { class GSCompat14 {
function Text(text) function Text(text)
{ {
@ -81,11 +78,3 @@ GSTown.FoundTown <- function(tile, size, city, layout, name) { return GSTown.Fou
GSVehicle.SetNameCompat14 <- GSVehicle.SetName; GSVehicle.SetNameCompat14 <- GSVehicle.SetName;
GSVehicle.SetName <- function(id, name) { return GSVehicle.SetNameCompat14(id, GSCompat14.Text(name)); } GSVehicle.SetName <- function(id, name) { return GSVehicle.SetNameCompat14(id, GSCompat14.Text(name)); }
GSObject.constructorCompat14 <- GSObject.constructor;
foreach(name, object in CompatScriptRootTable) {
if (type(object) != "class") continue;
if (!object.rawin("constructor")) continue;
if (object.constructor != GSObject.constructorCompat14) continue;
object.constructor <- function() : (name) { GSLog.Error("'" + name + "' is not instantiable"); }
}

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

@ -1 +1 @@
a4a727b03a7cd07ee0499231f7f233f4 019dba4830a64ee4345d3d647633e1da

View File

@ -671,9 +671,9 @@
// U+2026: Horizontal Ellipsis // U+2026: Horizontal Ellipsis
-1 * 18 12 04 00 01 26 20 01 01 26 20 02 01 26 20 03 01 26 20 -1 * 18 12 04 00 01 26 20 01 01 26 20 02 01 26 20 03 01 26 20
-1 sprites/chars.png 8bpp 560 370 9 12 0 -1 normal -1 sprites/chars.png 8bpp 560 370 11 12 0 -1 normal
-1 sprites/chars.png 8bpp 560 390 5 7 0 0 normal -1 sprites/chars.png 8bpp 560 390 5 7 0 0 normal
-1 sprites/chars.png 8bpp 560 400 14 21 0 -2 normal -1 sprites/chars.png 8bpp 560 400 15 21 0 -2 normal
-1 sprites/mono.png 8bpp 265 270 7 13 0 0 normal -1 sprites/mono.png 8bpp 265 270 7 13 0 0 normal
// U+2039: Single Left-Pointing Angle Quotation Mark // U+2039: Single Left-Pointing Angle Quotation Mark
@ -840,7 +840,3 @@
-1 sprites/chars.png 8bpp 630 400 6 21 0 -2 normal -1 sprites/chars.png 8bpp 630 400 6 21 0 -2 normal
-1 sprites/mono.png 8bpp 325 270 7 13 0 0 normal -1 sprites/mono.png 8bpp 325 270 7 13 0 0 normal
-1 sprites/mono.png 8bpp 340 270 7 13 0 0 normal -1 sprites/mono.png 8bpp 340 270 7 13 0 0 normal
// U+E29D: Small left arrow
-1 * 6 12 01 01 01 9D E2
-1 sprites/chars.png 8bpp 10 430 5 5 0 1 normal

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -132,7 +132,7 @@ void sq_close(HSQUIRRELVM v)
sq_delete(ss, SQSharedState); sq_delete(ss, SQSharedState);
} }
SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,std::string_view sourcename,SQBool raiseerror) SQRESULT sq_compile(HSQUIRRELVM v,SQLEXREADFUNC read,SQUserPointer p,const SQChar *sourcename,SQBool raiseerror)
{ {
SQObjectPtr o; SQObjectPtr o;
if(Compile(v, read, p, sourcename, o, raiseerror != 0, _ss(v)->_debuginfo)) { if(Compile(v, read, p, sourcename, o, raiseerror != 0, _ss(v)->_debuginfo)) {
@ -173,12 +173,12 @@ SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po)
#endif #endif
} }
std::optional<std::string_view> sq_objtostring(HSQOBJECT *o) const SQChar *sq_objtostring(HSQOBJECT *o)
{ {
if(sq_type(*o) == OT_STRING) { if(sq_type(*o) == OT_STRING) {
return _stringval(*o); return _stringval(*o);
} }
return std::nullopt; return nullptr;
} }
SQInteger sq_objtointeger(HSQOBJECT *o) SQInteger sq_objtointeger(HSQOBJECT *o)
@ -210,9 +210,11 @@ void sq_pushnull(HSQUIRRELVM v)
v->Push(_null_); v->Push(_null_);
} }
void sq_pushstring(HSQUIRRELVM v,std::string_view s) void sq_pushstring(HSQUIRRELVM v,const SQChar *s,SQInteger len)
{ {
v->Push(SQObjectPtr(SQString::Create(_ss(v), s))); if(s)
v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len)));
else v->Push(_null_);
} }
void sq_pushinteger(HSQUIRRELVM v,SQInteger n) void sq_pushinteger(HSQUIRRELVM v,SQInteger n)
@ -374,7 +376,7 @@ SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger *nparam
return sq_throwerror(v,"the object is not a closure"); return sq_throwerror(v,"the object is not a closure");
} }
SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,std::string_view name) SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const SQChar *name)
{ {
SQObject o = stack_get(v, idx); SQObject o = stack_get(v, idx);
if(sq_isnativeclosure(o)) { if(sq_isnativeclosure(o)) {
@ -385,16 +387,16 @@ SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,std::string_view na
return sq_throwerror(v,"the object is not a nativeclosure"); return sq_throwerror(v,"the object is not a nativeclosure");
} }
SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,std::optional<std::string_view> typemask) SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const SQChar *typemask)
{ {
SQObject o = stack_get(v, -1); SQObject o = stack_get(v, -1);
if(!sq_isnativeclosure(o)) if(!sq_isnativeclosure(o))
return sq_throwerror(v, "native closure expected"); return sq_throwerror(v, "native closure expected");
SQNativeClosure *nc = _nativeclosure(o); SQNativeClosure *nc = _nativeclosure(o);
nc->_nparamscheck = nparamscheck; nc->_nparamscheck = nparamscheck;
if(typemask.has_value()) { if(typemask) {
SQIntVec res; SQIntVec res;
if(!CompileTypemask(res, *typemask)) if(!CompileTypemask(res, typemask))
return sq_throwerror(v, "invalid typemask"); return sq_throwerror(v, "invalid typemask");
nc->_typecheck.copy(res); nc->_typecheck.copy(res);
} }
@ -551,11 +553,11 @@ SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b)
return SQ_ERROR; return SQ_ERROR;
} }
SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,std::string_view &str) SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const SQChar **c)
{ {
SQObjectPtr *o = nullptr; SQObjectPtr *o = nullptr;
_GETSAFE_OBJ(v, idx, OT_STRING,o); _GETSAFE_OBJ(v, idx, OT_STRING,o);
str = _stringval(*o); *c = _stringval(*o);
return SQ_OK; return SQ_OK;
} }
@ -583,7 +585,7 @@ SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx)
SQObjectPtr &o = stack_get(v, idx); SQObjectPtr &o = stack_get(v, idx);
SQObjectType type = type(o); SQObjectType type = type(o);
switch(type) { switch(type) {
case OT_STRING: return _string(o)->View().size(); case OT_STRING: return _string(o)->_len;
case OT_TABLE: return _table(o)->CountUsed(); case OT_TABLE: return _table(o)->CountUsed();
case OT_ARRAY: return _array(o)->Size(); case OT_ARRAY: return _array(o)->Size();
case OT_USERDATA: return _userdata(o)->_size; case OT_USERDATA: return _userdata(o)->_size;
@ -895,7 +897,7 @@ SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po)
return SQ_OK; return SQ_OK;
} }
std::optional<std::string_view> sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx) const SQChar *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx)
{ {
SQUnsignedInteger cstksize=v->_callsstacksize; SQUnsignedInteger cstksize=v->_callsstacksize;
SQUnsignedInteger lvl=(cstksize-level)-1; SQUnsignedInteger lvl=(cstksize-level)-1;
@ -907,7 +909,7 @@ std::optional<std::string_view> sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger leve
} }
SQVM::CallInfo &ci=v->_callsstack[lvl]; SQVM::CallInfo &ci=v->_callsstack[lvl];
if(type(ci._closure)!=OT_CLOSURE) if(type(ci._closure)!=OT_CLOSURE)
return std::nullopt; return nullptr;
SQClosure *c=_closure(ci._closure); SQClosure *c=_closure(ci._closure);
SQFunctionProto *func=_funcproto(c->_function); SQFunctionProto *func=_funcproto(c->_function);
if(func->_noutervalues > (SQInteger)idx) { if(func->_noutervalues > (SQInteger)idx) {
@ -917,7 +919,7 @@ std::optional<std::string_view> sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger leve
idx -= func->_noutervalues; idx -= func->_noutervalues;
return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1); return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)-1);
} }
return std::nullopt; return nullptr;
} }
void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj) void sq_pushobject(HSQUIRRELVM v,HSQOBJECT obj)
@ -930,9 +932,9 @@ void sq_resetobject(HSQOBJECT *po)
po->_unVal.pUserPointer=nullptr;po->_type=OT_NULL; po->_unVal.pUserPointer=nullptr;po->_type=OT_NULL;
} }
SQRESULT sq_throwerror(HSQUIRRELVM v,std::string_view error) SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err, SQInteger len)
{ {
v->_lasterror=SQString::Create(_ss(v),error); v->_lasterror=SQString::Create(_ss(v),err, len);
return -1; return -1;
} }
@ -977,10 +979,6 @@ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror,
if(!v->_suspended) { if(!v->_suspended) {
v->Pop(params);//pop closure and args v->Pop(params);//pop closure and args
} }
if (!v->_can_suspend && v->IsOpsTillSuspendError()) {
v->Raise_Error(fmt::format("excessive CPU usage in {}", v->_ops_till_suspend_error_label));
return SQ_ERROR;
}
if(retval){ if(retval){
v->Push(res); return SQ_OK; v->Push(res); return SQ_OK;
} }
@ -1078,7 +1076,7 @@ SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up)
return SQ_OK; return SQ_OK;
} }
std::span<char> sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize) SQChar *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize)
{ {
return _ss(v)->GetScratchPad(minsize); return _ss(v)->GetScratchPad(minsize);
} }
@ -1092,18 +1090,19 @@ SQInteger sq_collectgarbage(HSQUIRRELVM v)
#endif #endif
} }
std::optional<std::string_view> sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) const SQChar *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
{ {
SQObjectPtr &self = stack_get(v,idx); SQObjectPtr &self = stack_get(v,idx);
const SQChar *name = nullptr;
if(type(self) == OT_CLOSURE) { if(type(self) == OT_CLOSURE) {
if(_closure(self)->_outervalues.size()>nval) { if(_closure(self)->_outervalues.size()>nval) {
v->Push(_closure(self)->_outervalues[nval]); v->Push(_closure(self)->_outervalues[nval]);
SQFunctionProto *fp = _funcproto(_closure(self)->_function); SQFunctionProto *fp = _funcproto(_closure(self)->_function);
SQOuterVar &ov = fp->_outervalues[nval]; SQOuterVar &ov = fp->_outervalues[nval];
return _stringval(ov._name); name = _stringval(ov._name);
} }
} }
return std::nullopt; return name;
} }
SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval)
@ -1262,8 +1261,8 @@ char32_t buf_lexfeed(SQUserPointer file)
return consumer.AnyBytesLeft() ? consumer.ReadUtf8(-1) : 0; return consumer.AnyBytesLeft() ? consumer.ReadUtf8(-1) : 0;
} }
SQRESULT sq_compilebuffer(HSQUIRRELVM v,std::string_view buffer,std::string_view sourcename,SQBool raiseerror) { SQRESULT sq_compilebuffer(HSQUIRRELVM v,const SQChar *s,SQInteger size,const SQChar *sourcename,SQBool raiseerror) {
StringConsumer consumer{buffer}; StringConsumer consumer(s, size);
return sq_compile(v, buf_lexfeed, &consumer, sourcename, raiseerror); return sq_compile(v, buf_lexfeed, &consumer, sourcename, raiseerror);
} }
@ -1297,7 +1296,7 @@ void sq_free(void *p,SQUnsignedInteger size)
SQ_FREE(p,size); SQ_FREE(p,size);
} }
SQOpsLimiter::SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, std::string_view label) : _v(v) SQOpsLimiter::SQOpsLimiter(HSQUIRRELVM v, SQInteger ops, const char *label) : _v(v)
{ {
this->_ops = v->_ops_till_suspend_error_threshold; this->_ops = v->_ops_till_suspend_error_threshold;
if (this->_ops == INT64_MIN) { if (this->_ops == INT64_MIN) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -279,10 +279,6 @@ add_files(
newgrf_animation_type.h newgrf_animation_type.h
newgrf_badge.cpp newgrf_badge.cpp
newgrf_badge.h newgrf_badge.h
newgrf_badge_config.cpp
newgrf_badge_config.h
newgrf_badge_gui.cpp
newgrf_badge_gui.h
newgrf_badge_type.h newgrf_badge_type.h
newgrf_callbacks.h newgrf_callbacks.h
newgrf_canal.cpp newgrf_canal.cpp
@ -454,7 +450,6 @@ add_files(
spritecache.cpp spritecache.cpp
spritecache.h spritecache.h
spritecache_internal.h spritecache_internal.h
spritecache_type.h
station.cpp station.cpp
station_base.h station_base.h
station_cmd.cpp station_cmd.cpp

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -175,7 +175,7 @@ void Aircraft::GetImage(Direction direction, EngineImageType image_type, Vehicle
{ {
uint8_t spritenum = this->spritenum; uint8_t spritenum = this->spritenum;
if (IsCustomVehicleSpriteNum(spritenum)) { if (is_custom_sprite(spritenum)) {
GetCustomVehicleSprite(this, direction, image_type, result); GetCustomVehicleSprite(this, direction, image_type, result);
if (result->IsValid()) return; if (result->IsValid()) return;
@ -191,7 +191,7 @@ void GetRotorImage(const Aircraft *v, EngineImageType image_type, VehicleSpriteS
assert(v->subtype == AIR_HELICOPTER); assert(v->subtype == AIR_HELICOPTER);
const Aircraft *w = v->Next()->Next(); const Aircraft *w = v->Next()->Next();
if (IsCustomVehicleSpriteNum(v->spritenum)) { if (is_custom_sprite(v->spritenum)) {
GetCustomRotorSprite(v, image_type, result); GetCustomRotorSprite(v, image_type, result);
if (result->IsValid()) return; if (result->IsValid()) return;
} }
@ -205,7 +205,7 @@ static void GetAircraftIcon(EngineID engine, EngineImageType image_type, Vehicle
const Engine *e = Engine::Get(engine); const Engine *e = Engine::Get(engine);
uint8_t spritenum = e->u.air.image_index; uint8_t spritenum = e->u.air.image_index;
if (IsCustomVehicleSpriteNum(spritenum)) { if (is_custom_sprite(spritenum)) {
GetCustomVehicleIcon(engine, DIR_W, image_type, result); GetCustomVehicleIcon(engine, DIR_W, image_type, result);
if (result->IsValid()) return; if (result->IsValid()) return;
@ -1125,15 +1125,6 @@ static bool AircraftController(Aircraft *v)
} }
if (amd.flags.Test(AirportMovingDataFlag::Land)) { if (amd.flags.Test(AirportMovingDataFlag::Land)) {
if (st->airport.blocks.Test(AirportBlock::Zeppeliner)) {
/* Zeppeliner blocked the runway, abort landing */
v->state = FLYING;
UpdateAircraftCache(v);
SetAircraftPosition(v, gp.x, gp.y, GetAircraftFlightLevel(v));
v->pos = v->previous_pos;
continue;
}
if (st->airport.tile == INVALID_TILE) { if (st->airport.tile == INVALID_TILE) {
/* Airport has been removed, abort the landing procedure */ /* Airport has been removed, abort the landing procedure */
v->state = FLYING; v->state = FLYING;
@ -1439,7 +1430,7 @@ static void AircraftLandAirplane(Aircraft *v)
v->UpdateDeltaXY(); v->UpdateDeltaXY();
TriggerAirportTileAnimation(st, vt, AirportAnimationTrigger::AirplaneTouchdown); AirportTileAnimationTrigger(st, vt, AAT_STATION_AIRPLANE_LAND);
if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) { if (!PlayVehicleSound(v, VSE_TOUCHDOWN)) {
SndPlayVehicleFx(SND_17_SKID_PLANE, v); SndPlayVehicleFx(SND_17_SKID_PLANE, v);
@ -1791,11 +1782,6 @@ static void AirportClearBlock(const Aircraft *v, const AirportFTAClass *apc)
if (apc->layout[v->previous_pos].blocks != apc->layout[v->pos].blocks) { if (apc->layout[v->previous_pos].blocks != apc->layout[v->pos].blocks) {
Station *st = Station::Get(v->targetairport); Station *st = Station::Get(v->targetairport);
if (st->airport.blocks.Test(AirportBlock::Zeppeliner) &&
apc->layout[v->previous_pos].blocks == AirportBlock::RunwayIn) {
return;
}
st->airport.blocks.Reset(apc->layout[v->previous_pos].blocks); st->airport.blocks.Reset(apc->layout[v->previous_pos].blocks);
} }
} }

View File

@ -44,7 +44,7 @@ enum AirportTypes : uint8_t {
}; };
/** Flags for airport movement data. */ /** Flags for airport movement data. */
enum class AirportMovingDataFlag : uint8_t { enum AirportMovingDataFlag : uint8_t {
NoSpeedClamp, ///< No speed restrictions. NoSpeedClamp, ///< No speed restrictions.
Takeoff, ///< Takeoff movement. Takeoff, ///< Takeoff movement.
SlowTurn, ///< Turn slowly (mostly used in the air). SlowTurn, ///< Turn slowly (mostly used in the air).
@ -127,7 +127,6 @@ enum class AirportBlock : uint8_t {
/* end of new blocks */ /* end of new blocks */
Nothing = 30, Nothing = 30,
Zeppeliner = 62, ///< Block for the zeppeliner disaster vehicle.
AirportClosed = 63, ///< Dummy block for indicating a closed airport. AirportClosed = 63, ///< Dummy block for indicating a closed airport.
}; };
using AirportBlocks = EnumBitSet<AirportBlock, uint64_t>; using AirportBlocks = EnumBitSet<AirportBlock, uint64_t>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -122,7 +122,7 @@ class ReplaceVehicleWindow : public Window {
*/ */
void GenerateReplaceVehList(bool draw_left) void GenerateReplaceVehList(bool draw_left)
{ {
FlatSet<EngineID> variants; std::vector<EngineID> variants;
EngineID selected_engine = EngineID::Invalid(); EngineID selected_engine = EngineID::Invalid();
VehicleType type = this->window_number; VehicleType type = this->window_number;
uint8_t side = draw_left ? 0 : 1; uint8_t side = draw_left ? 0 : 1;
@ -161,7 +161,8 @@ class ReplaceVehicleWindow : public Window {
if (side == 1) { if (side == 1) {
EngineID parent = e->info.variant_id; EngineID parent = e->info.variant_id;
while (parent != EngineID::Invalid() && variants.insert(parent).second) { while (parent != EngineID::Invalid()) {
variants.push_back(parent);
parent = Engine::Get(parent)->info.variant_id; parent = Engine::Get(parent)->info.variant_id;
} }
} }
@ -310,7 +311,7 @@ public:
case WID_RV_LEFT_MATRIX: case WID_RV_LEFT_MATRIX:
case WID_RV_RIGHT_MATRIX: case WID_RV_RIGHT_MATRIX:
fill.height = resize.height = GetEngineListHeight(this->window_number); resize.height = GetEngineListHeight(this->window_number);
size.height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize.height; size.height = (this->window_number <= VEH_ROAD ? 8 : 4) * resize.height;
break; break;
@ -405,11 +406,11 @@ public:
case WID_RV_TRAIN_WAGONREMOVE_TOGGLE: case WID_RV_TRAIN_WAGONREMOVE_TOGGLE:
if (const Group *g = Group::GetIfValid(this->sel_group); g != nullptr) { if (const Group *g = Group::GetIfValid(this->sel_group); g != nullptr) {
bool remove_wagon = g->flags.Test(GroupFlag::ReplaceWagonRemoval); bool remove_wagon = g->flags.Test(GroupFlag::ReplaceWagonRemoval);
return GetString(STR_REPLACE_REMOVE_WAGON, STR_GROUP_NAME, sel_group, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); return GetString(STR_GROUP_NAME, sel_group, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
} else { } else {
const Company *c = Company::Get(_local_company); const Company *c = Company::Get(_local_company);
bool remove_wagon = c->settings.renew_keep_length; bool remove_wagon = c->settings.renew_keep_length;
return GetString(STR_REPLACE_REMOVE_WAGON, STR_GROUP_DEFAULT_TRAINS + this->window_number, std::monostate{}, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF); return GetString(STR_GROUP_DEFAULT_TRAINS + this->window_number, std::monostate{}, remove_wagon ? STR_CONFIG_SETTING_ON : STR_CONFIG_SETTING_OFF);
} }
break; break;
@ -551,7 +552,7 @@ public:
if (g != nullptr) { if (g != nullptr) {
Command<CMD_SET_GROUP_FLAG>::Post(this->sel_group, GroupFlag::ReplaceWagonRemoval, !g->flags.Test(GroupFlag::ReplaceWagonRemoval), _ctrl_pressed); Command<CMD_SET_GROUP_FLAG>::Post(this->sel_group, GroupFlag::ReplaceWagonRemoval, !g->flags.Test(GroupFlag::ReplaceWagonRemoval), _ctrl_pressed);
} else { } else {
/* toggle renew_keep_length */ // toggle renew_keep_length
Command<CMD_CHANGE_COMPANY_SETTING>::Post("company.renew_keep_length", Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1); Command<CMD_CHANGE_COMPANY_SETTING>::Post("company.renew_keep_length", Company::Get(_local_company)->settings.renew_keep_length ? 0 : 1);
} }
break; break;
@ -622,7 +623,7 @@ public:
} }
} }
void OnDropdownSelect(WidgetID widget, int index, int) override void OnDropdownSelect(WidgetID widget, int index) override
{ {
switch (widget) { switch (widget) {
case WID_RV_SORT_DROPDOWN: case WID_RV_SORT_DROPDOWN:
@ -743,7 +744,7 @@ static constexpr NWidgetPart _nested_replace_rail_vehicle_widgets[] = {
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_LEFT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
NWidget(NWID_VERTICAL), NWidget(NWID_VERTICAL),
NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(), NWidget(WWT_PANEL, COLOUR_GREY, WID_RV_RIGHT_DETAILS), SetMinimalSize(240, 122), SetResize(1, 0), EndContainer(),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_RV_TRAIN_WAGONREMOVE_TOGGLE), SetMinimalSize(138, 12), SetToolTip(STR_REPLACE_REMOVE_WAGON_TOOLTIP), SetFill(1, 0), SetResize(1, 0), 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),
EndContainer(), EndContainer(),
EndContainer(), EndContainer(),
NWidget(NWID_HORIZONTAL), NWidget(NWID_HORIZONTAL),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -51,7 +51,7 @@ void Blitter_32bppBase::CopyFromBuffer(void *video, const void *src, int width,
const uint32_t *usrc = (const uint32_t *)src; const uint32_t *usrc = (const uint32_t *)src;
for (; height > 0; height--) { for (; height > 0; height--) {
std::copy_n(usrc, width, dst); memcpy(dst, usrc, width * sizeof(uint32_t));
usrc += width; usrc += width;
dst += _screen.pitch; dst += _screen.pitch;
} }
@ -63,7 +63,7 @@ void Blitter_32bppBase::CopyToBuffer(const void *video, void *dst, int width, in
const uint32_t *src = (const uint32_t *)video; const uint32_t *src = (const uint32_t *)video;
for (; height > 0; height--) { for (; height > 0; height--) {
std::copy_n(src, width, udst); memcpy(udst, src, width * sizeof(uint32_t));
src += _screen.pitch; src += _screen.pitch;
udst += width; udst += width;
} }
@ -75,7 +75,7 @@ void Blitter_32bppBase::CopyImageToBuffer(const void *video, void *dst, int widt
const uint32_t *src = (const uint32_t *)video; const uint32_t *src = (const uint32_t *)video;
for (; height > 0; height--) { for (; height > 0; height--) {
std::copy_n(src, width, udst); memcpy(udst, src, width * sizeof(uint32_t));
src += _screen.pitch; src += _screen.pitch;
udst += dst_pitch; udst += dst_pitch;
} }
@ -106,7 +106,11 @@ void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &widt
width += scroll_x; width += scroll_x;
} }
Blitter::MovePixels(src, dst, width, height, -_screen.pitch); for (int h = height; h > 0; h--) {
memcpy(dst, src, width * sizeof(uint32_t));
src -= _screen.pitch;
dst -= _screen.pitch;
}
} else { } else {
/* Calculate pointers */ /* Calculate pointers */
dst = (uint32_t *)video + left + top * _screen.pitch; dst = (uint32_t *)video + left + top * _screen.pitch;
@ -126,7 +130,13 @@ void Blitter_32bppBase::ScrollBuffer(void *video, int &left, int &top, int &widt
width += scroll_x; width += scroll_x;
} }
Blitter::MovePixels(src, dst, width, height, _screen.pitch); /* 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;
}
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -42,7 +42,7 @@ static constexpr NWidgetPart _background_widgets[] = {
* Window description for the background window to prevent smearing. * Window description for the background window to prevent smearing.
*/ */
static WindowDesc _background_desc( static WindowDesc _background_desc(
WDP_MANUAL, {}, 0, 0, WDP_MANUAL, nullptr, 0, 0,
WC_BOOTSTRAP, WC_NONE, WC_BOOTSTRAP, WC_NONE,
WindowDefaultFlag::NoClose, WindowDefaultFlag::NoClose,
_background_widgets _background_widgets
@ -78,7 +78,7 @@ static constexpr NWidgetPart _nested_bootstrap_errmsg_widgets[] = {
/** Window description for the error window. */ /** Window description for the error window. */
static WindowDesc _bootstrap_errmsg_desc( static WindowDesc _bootstrap_errmsg_desc(
WDP_CENTER, {}, 0, 0, WDP_CENTER, nullptr, 0, 0,
WC_BOOTSTRAP, WC_NONE, WC_BOOTSTRAP, WC_NONE,
{WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose}, {WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose},
_nested_bootstrap_errmsg_widgets _nested_bootstrap_errmsg_widgets
@ -135,7 +135,7 @@ static constexpr NWidgetPart _nested_bootstrap_download_status_window_widgets[]
/** Window description for the download window */ /** Window description for the download window */
static WindowDesc _bootstrap_download_status_window_desc( static WindowDesc _bootstrap_download_status_window_desc(
WDP_CENTER, {}, 0, 0, WDP_CENTER, nullptr, 0, 0,
WC_NETWORK_STATUS_WINDOW, WC_NONE, WC_NETWORK_STATUS_WINDOW, WC_NONE,
{WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose}, {WindowDefaultFlag::Modal, WindowDefaultFlag::NoClose},
_nested_bootstrap_download_status_window_widgets _nested_bootstrap_download_status_window_widgets
@ -187,7 +187,7 @@ static constexpr NWidgetPart _bootstrap_query_widgets[] = {
/** The window description for the query. */ /** The window description for the query. */
static WindowDesc _bootstrap_query_desc( static WindowDesc _bootstrap_query_desc(
WDP_CENTER, {}, 0, 0, WDP_CENTER, nullptr, 0, 0,
WC_CONFIRM_POPUP_QUERY, WC_NONE, WC_CONFIRM_POPUP_QUERY, WC_NONE,
WindowDefaultFlag::NoClose, WindowDefaultFlag::NoClose,
_bootstrap_query_widgets _bootstrap_query_widgets

View File

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

View File

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

View File

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

View File

@ -410,7 +410,7 @@ void VehicleCargoList::AgeCargo()
return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP; return (accepted && cp->first_station != current_station) ? MTA_DELIVER : MTA_KEEP;
} else if (cargo_next == current_station) { } else if (cargo_next == current_station) {
return MTA_DELIVER; return MTA_DELIVER;
} else if (next_station.Contains(cargo_next)) { } else if (next_station.Contains(cargo_next.base())) {
return MTA_KEEP; return MTA_KEEP;
} else { } else {
return MTA_TRANSFER; return MTA_TRANSFER;
@ -470,7 +470,7 @@ bool VehicleCargoList::Stage(bool accepted, StationID current_station, StationID
new_shares.ChangeShare(current_station, INT_MIN); new_shares.ChangeShare(current_station, INT_MIN);
StationIDStack excluded = next_station; StationIDStack excluded = next_station;
while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) { while (!excluded.IsEmpty() && !new_shares.GetShares()->empty()) {
new_shares.ChangeShare(excluded.Pop(), INT_MIN); new_shares.ChangeShare(StationID{excluded.Pop()}, INT_MIN);
} }
if (new_shares.GetShares()->empty()) { if (new_shares.GetShares()->empty()) {
cargo_next = StationID::Invalid(); cargo_next = StationID::Invalid();
@ -743,7 +743,7 @@ uint StationCargoList::ShiftCargo(Taction action, StationIDStack next, bool incl
{ {
uint max_move = action.MaxMove(); uint max_move = action.MaxMove();
while (!next.IsEmpty()) { while (!next.IsEmpty()) {
this->ShiftCargo(action, next.Pop()); this->ShiftCargo(action, StationID{next.Pop()});
if (action.MaxMove() == 0) break; if (action.MaxMove() == 0) break;
} }
if (include_invalid && action.MaxMove() > 0) { if (include_invalid && action.MaxMove() > 0) {
@ -853,7 +853,7 @@ uint StationCargoList::Load(uint max_move, VehicleCargoList *dest, StationIDStac
*/ */
uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge) uint StationCargoList::Reroute(uint max_move, StationCargoList *dest, StationID avoid, StationID avoid2, const GoodsEntry *ge)
{ {
return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid, false); return this->ShiftCargo(StationCargoReroute(this, dest, max_move, avoid, avoid2, ge), avoid.base(), false);
} }
/* /*

View File

@ -555,7 +555,7 @@ public:
inline bool HasCargoFor(StationIDStack next) const inline bool HasCargoFor(StationIDStack next) const
{ {
while (!next.IsEmpty()) { while (!next.IsEmpty()) {
if (this->packets.find(next.Pop()) != this->packets.end()) return true; if (this->packets.find(StationID{next.Pop()}) != this->packets.end()) return true;
} }
/* Packets for StationID::Invalid() can go anywhere. */ /* Packets for StationID::Invalid() can go anywhere. */
return this->packets.find(StationID::Invalid()) != this->packets.end(); return this->packets.find(StationID::Invalid()) != this->packets.end();

View File

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

View File

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

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