@@ -88,6 +88,8 @@ ifeq ($(USING_RTAUDIO),true) | |||
3RD_LIBS += $(MODULEDIR)/rtmidi.a | |||
endif | |||
3RD_LIBS += $(MODULEDIR)/ysfx.a | |||
ALL_LIBS += $(3RD_LIBS) | |||
3rd: $(3RD_LIBS) | |||
@@ -515,6 +515,14 @@ JUCE_GUI_BASICS_LIBS = -lgdi32 -limm32 -lcomdlg32 -lole32 | |||
endif # USING_JUCE | |||
endif # WIN32 | |||
YSFX_FLAGS = -I$(CWD)/modules/ysfx/include | |||
ifeq ($(MACOS),true) | |||
YSFX_GRAPHICS_LIBS = -framework Cocoa -framework Carbon -framework Metal -framework Foundation | |||
else ifneq ($(WIN32),true) | |||
YSFX_GRAPHICS_FLAGS = $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --cflags freetype2 fontconfig) | |||
YSFX_GRAPHICS_LIBS = $(shell $(PKG_CONFIG) $(PKG_CONFIG_FLAGS) --libs freetype2 fontconfig) | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
ifeq ($(STATIC_PLUGIN_TARGET),true) | |||
@@ -0,0 +1,13 @@ | |||
Copyright 2021 Jean Pierre Cimalando | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. |
@@ -0,0 +1,202 @@ | |||
Apache License | |||
Version 2.0, January 2004 | |||
http://www.apache.org/licenses/ | |||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | |||
1. Definitions. | |||
"License" shall mean the terms and conditions for use, reproduction, | |||
and distribution as defined by Sections 1 through 9 of this document. | |||
"Licensor" shall mean the copyright owner or entity authorized by | |||
the copyright owner that is granting the License. | |||
"Legal Entity" shall mean the union of the acting entity and all | |||
other entities that control, are controlled by, or are under common | |||
control with that entity. For the purposes of this definition, | |||
"control" means (i) the power, direct or indirect, to cause the | |||
direction or management of such entity, whether by contract or | |||
otherwise, or (ii) ownership of fifty percent (50%) or more of the | |||
outstanding shares, or (iii) beneficial ownership of such entity. | |||
"You" (or "Your") shall mean an individual or Legal Entity | |||
exercising permissions granted by this License. | |||
"Source" form shall mean the preferred form for making modifications, | |||
including but not limited to software source code, documentation | |||
source, and configuration files. | |||
"Object" form shall mean any form resulting from mechanical | |||
transformation or translation of a Source form, including but | |||
not limited to compiled object code, generated documentation, | |||
and conversions to other media types. | |||
"Work" shall mean the work of authorship, whether in Source or | |||
Object form, made available under the License, as indicated by a | |||
copyright notice that is included in or attached to the work | |||
(an example is provided in the Appendix below). | |||
"Derivative Works" shall mean any work, whether in Source or Object | |||
form, that is based on (or derived from) the Work and for which the | |||
editorial revisions, annotations, elaborations, or other modifications | |||
represent, as a whole, an original work of authorship. For the purposes | |||
of this License, Derivative Works shall not include works that remain | |||
separable from, or merely link (or bind by name) to the interfaces of, | |||
the Work and Derivative Works thereof. | |||
"Contribution" shall mean any work of authorship, including | |||
the original version of the Work and any modifications or additions | |||
to that Work or Derivative Works thereof, that is intentionally | |||
submitted to Licensor for inclusion in the Work by the copyright owner | |||
or by an individual or Legal Entity authorized to submit on behalf of | |||
the copyright owner. For the purposes of this definition, "submitted" | |||
means any form of electronic, verbal, or written communication sent | |||
to the Licensor or its representatives, including but not limited to | |||
communication on electronic mailing lists, source code control systems, | |||
and issue tracking systems that are managed by, or on behalf of, the | |||
Licensor for the purpose of discussing and improving the Work, but | |||
excluding communication that is conspicuously marked or otherwise | |||
designated in writing by the copyright owner as "Not a Contribution." | |||
"Contributor" shall mean Licensor and any individual or Legal Entity | |||
on behalf of whom a Contribution has been received by Licensor and | |||
subsequently incorporated within the Work. | |||
2. Grant of Copyright License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
copyright license to reproduce, prepare Derivative Works of, | |||
publicly display, publicly perform, sublicense, and distribute the | |||
Work and such Derivative Works in Source or Object form. | |||
3. Grant of Patent License. Subject to the terms and conditions of | |||
this License, each Contributor hereby grants to You a perpetual, | |||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable | |||
(except as stated in this section) patent license to make, have made, | |||
use, offer to sell, sell, import, and otherwise transfer the Work, | |||
where such license applies only to those patent claims licensable | |||
by such Contributor that are necessarily infringed by their | |||
Contribution(s) alone or by combination of their Contribution(s) | |||
with the Work to which such Contribution(s) was submitted. If You | |||
institute patent litigation against any entity (including a | |||
cross-claim or counterclaim in a lawsuit) alleging that the Work | |||
or a Contribution incorporated within the Work constitutes direct | |||
or contributory patent infringement, then any patent licenses | |||
granted to You under this License for that Work shall terminate | |||
as of the date such litigation is filed. | |||
4. Redistribution. You may reproduce and distribute copies of the | |||
Work or Derivative Works thereof in any medium, with or without | |||
modifications, and in Source or Object form, provided that You | |||
meet the following conditions: | |||
(a) You must give any other recipients of the Work or | |||
Derivative Works a copy of this License; and | |||
(b) You must cause any modified files to carry prominent notices | |||
stating that You changed the files; and | |||
(c) You must retain, in the Source form of any Derivative Works | |||
that You distribute, all copyright, patent, trademark, and | |||
attribution notices from the Source form of the Work, | |||
excluding those notices that do not pertain to any part of | |||
the Derivative Works; and | |||
(d) If the Work includes a "NOTICE" text file as part of its | |||
distribution, then any Derivative Works that You distribute must | |||
include a readable copy of the attribution notices contained | |||
within such NOTICE file, excluding those notices that do not | |||
pertain to any part of the Derivative Works, in at least one | |||
of the following places: within a NOTICE text file distributed | |||
as part of the Derivative Works; within the Source form or | |||
documentation, if provided along with the Derivative Works; or, | |||
within a display generated by the Derivative Works, if and | |||
wherever such third-party notices normally appear. The contents | |||
of the NOTICE file are for informational purposes only and | |||
do not modify the License. You may add Your own attribution | |||
notices within Derivative Works that You distribute, alongside | |||
or as an addendum to the NOTICE text from the Work, provided | |||
that such additional attribution notices cannot be construed | |||
as modifying the License. | |||
You may add Your own copyright statement to Your modifications and | |||
may provide additional or different license terms and conditions | |||
for use, reproduction, or distribution of Your modifications, or | |||
for any such Derivative Works as a whole, provided Your use, | |||
reproduction, and distribution of the Work otherwise complies with | |||
the conditions stated in this License. | |||
5. Submission of Contributions. Unless You explicitly state otherwise, | |||
any Contribution intentionally submitted for inclusion in the Work | |||
by You to the Licensor shall be under the terms and conditions of | |||
this License, without any additional terms or conditions. | |||
Notwithstanding the above, nothing herein shall supersede or modify | |||
the terms of any separate license agreement you may have executed | |||
with Licensor regarding such Contributions. | |||
6. Trademarks. This License does not grant permission to use the trade | |||
names, trademarks, service marks, or product names of the Licensor, | |||
except as required for reasonable and customary use in describing the | |||
origin of the Work and reproducing the content of the NOTICE file. | |||
7. Disclaimer of Warranty. Unless required by applicable law or | |||
agreed to in writing, Licensor provides the Work (and each | |||
Contributor provides its Contributions) on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |||
implied, including, without limitation, any warranties or conditions | |||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | |||
PARTICULAR PURPOSE. You are solely responsible for determining the | |||
appropriateness of using or redistributing the Work and assume any | |||
risks associated with Your exercise of permissions under this License. | |||
8. Limitation of Liability. In no event and under no legal theory, | |||
whether in tort (including negligence), contract, or otherwise, | |||
unless required by applicable law (such as deliberate and grossly | |||
negligent acts) or agreed to in writing, shall any Contributor be | |||
liable to You for damages, including any direct, indirect, special, | |||
incidental, or consequential damages of any character arising as a | |||
result of this License or out of the use or inability to use the | |||
Work (including but not limited to damages for loss of goodwill, | |||
work stoppage, computer failure or malfunction, or any and all | |||
other commercial damages or losses), even if such Contributor | |||
has been advised of the possibility of such damages. | |||
9. Accepting Warranty or Additional Liability. While redistributing | |||
the Work or Derivative Works thereof, You may choose to offer, | |||
and charge a fee for, acceptance of support, warranty, indemnity, | |||
or other liability obligations and/or rights consistent with this | |||
License. However, in accepting such obligations, You may act only | |||
on Your own behalf and on Your sole responsibility, not on behalf | |||
of any other Contributor, and only if You agree to indemnify, | |||
defend, and hold each Contributor harmless for any liability | |||
incurred by, or claims asserted against, such Contributor by reason | |||
of your accepting any such warranty or additional liability. | |||
END OF TERMS AND CONDITIONS | |||
APPENDIX: How to apply the Apache License to your work. | |||
To apply the Apache License to your work, attach the following | |||
boilerplate notice, with the fields enclosed by brackets "[]" | |||
replaced with your own identifying information. (Don't include | |||
the brackets!) The text should be enclosed in the appropriate | |||
comment syntax for the file format. We also recommend that a | |||
file or class name and description of purpose be included on the | |||
same "printed page" as the copyright notice for easier | |||
identification within third-party archives. | |||
Copyright [yyyy] [name of copyright owner] | |||
Licensed under the Apache License, Version 2.0 (the "License"); | |||
you may not use this file except in compliance with the License. | |||
You may obtain a copy of the License at | |||
http://www.apache.org/licenses/LICENSE-2.0 | |||
Unless required by applicable law or agreed to in writing, software | |||
distributed under the License is distributed on an "AS IS" BASIS, | |||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
See the License for the specific language governing permissions and | |||
limitations under the License. |
@@ -0,0 +1,169 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for eel2 # | |||
# ----------------- # | |||
# Created by falkTX | |||
# | |||
CWD=../.. | |||
MODULENAME=ysfx | |||
include ../Makefile.mk | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
BUILD_C_FLAGS += $(YSFX_FLAGS) | |||
BUILD_C_FLAGS += $(YSFX_GRAPHICS_FLAGS) | |||
BUILD_C_FLAGS += -Isources | |||
BUILD_C_FLAGS += -Ithirdparty/WDL/source -Ithirdparty/dr_libs -Ithirdparty/stb | |||
BUILD_C_FLAGS += -DYSFX_NO_STANDARD_MUTEX | |||
BUILD_C_FLAGS += -DEELSCRIPT_NO_NET | |||
BUILD_C_FLAGS += -DEELSCRIPT_NO_LICE | |||
BUILD_C_FLAGS += -DWDL_FFT_REALSIZE=8 | |||
BUILD_C_FLAGS += -DWDL_LINEPARSE_ATOF=ysfx_wdl_atof | |||
BUILD_C_FLAGS += -DNSEEL_ATOF=ysfx_wdl_atof | |||
BUILD_CXX_FLAGS += $(YSFX_FLAGS) | |||
BUILD_CXX_FLAGS += $(YSFX_GRAPHICS_FLAGS) | |||
BUILD_CXX_FLAGS += -Isources | |||
BUILD_CXX_FLAGS += -Ithirdparty/WDL/source -Ithirdparty/dr_libs -Ithirdparty/stb | |||
BUILD_CXX_FLAGS += -DYSFX_NO_STANDARD_MUTEX | |||
BUILD_CXX_FLAGS += -DEELSCRIPT_NO_NET | |||
BUILD_CXX_FLAGS += -DEELSCRIPT_NO_LICE | |||
BUILD_CXX_FLAGS += -DWDL_FFT_REALSIZE=8 | |||
BUILD_CXX_FLAGS += -DWDL_LINEPARSE_ATOF=ysfx_wdl_atof | |||
BUILD_CXX_FLAGS += -DNSEEL_ATOF=ysfx_wdl_atof | |||
ifneq ($(WIN32),true) | |||
# NOTE: not compatible with MingGW, breaks win32_utf8 | |||
BUILD_C_FLAGS += -D_FILE_OFFSET_BITS=64 | |||
BUILD_CXX_FLAGS += -D_FILE_OFFSET_BITS=64 | |||
endif | |||
ifeq ($(WIN32),true) | |||
BUILD_C_FLAGS += -DNOMINMAX | |||
BUILD_CXX_FLAGS += -DNOMINMAX | |||
endif | |||
ifneq ($(WIN32),true) | |||
ifneq ($(MACOS),true) | |||
BUILD_C_FLAGS += -DSWELL_LICE_GDI -DSWELL_FONTCONFIG -DSWELL_FREETYPE | |||
BUILD_CXX_FLAGS += -DSWELL_LICE_GDI -DSWELL_FONTCONFIG -DSWELL_FREETYPE | |||
endif | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
OBJS = $(OBJDIR)/sources/ysfx.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_api_eel.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_api_file.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_api_gfx.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_api_reaper.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_audio_flac.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_audio_wav.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_config.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_eel_utils.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_midi.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_parse.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_reader.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_utils.cpp.o | |||
OBJS += $(OBJDIR)/sources/ysfx_utils_fts.cpp.o | |||
OBJS += $(OBJDIR)/sources/eel2-gas/sources/asm-nseel-x64-sse.S.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-caltab.c.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-cfunc.c.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-compiler.c.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-eval.c.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-lextab.c.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-ram.c.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/eel2/nseel-yylex.c.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/fft.c.o | |||
ifeq ($(WIN32),true) | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/win32_utf8.c.o | |||
endif | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_arc.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_colorspace.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_image.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_line.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_palette.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_texgen.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_text.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/lice/lice_textnew.cpp.o | |||
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_loaders.cpp.o | |||
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_bmp.cpp.o | |||
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_gif.cpp.o | |||
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_jpg.cpp.o | |||
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_png.cpp.o | |||
OBJS += $(OBJDIR)/sources/lice_stb/lice_stb_write.cpp.o | |||
ifneq ($(WIN32),true) | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-ini.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell.cpp.o | |||
ifeq ($(MACOS),true) | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-appstub.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-dlg.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-kb.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-menu.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-misc.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-miscdlg.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-modstub.mm.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-wnd.mm.o | |||
else | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-appstub-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-dlg-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-gdi-lice.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-generic-gdk.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-generic-headless.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-kb-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-menu-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-misc-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-miscdlg-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-modstub-generic.cpp.o | |||
OBJS += $(OBJDIR)/thirdparty/WDL/source/WDL/swell/swell-wnd-generic.cpp.o | |||
endif | |||
endif | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
all: $(MODULEDIR)/$(MODULENAME).a | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
clean: | |||
rm -f $(OBJS) $(MODULEDIR)/$(MODULENAME)*.a | |||
debug: | |||
$(MAKE) DEBUG=true | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
$(MODULEDIR)/$(MODULENAME).a: $(OBJS) | |||
-@mkdir -p $(MODULEDIR) | |||
@echo "Creating $(MODULENAME).a" | |||
@rm -f $@ | |||
@$(AR) crs $@ $^ | |||
# --------------------------------------------------------------------------------------------------------------------- | |||
$(OBJDIR)/%.c.o: %.c | |||
-@mkdir -p $(dir $@) | |||
@echo "Compiling $<" | |||
@$(CC) $< $(BUILD_C_FLAGS) -c -o $@ | |||
$(OBJDIR)/%.cpp.o: %.cpp | |||
-@mkdir -p $(dir $@) | |||
@echo "Compiling $<" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
$(OBJDIR)/%.mm.o: %.mm | |||
-@mkdir -p $(dir $@) | |||
@echo "Compiling $<" | |||
@$(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
$(OBJDIR)/%.S.o: %.S | |||
-@mkdir -p $(dir $@) | |||
@echo "Compiling $<" | |||
@$(CC) $< $(BUILD_ASM_FLAGS) -c -o $@ | |||
-include $(OBJS:%.o=%.d) | |||
# --------------------------------------------------------------------------------------------------------------------- |
@@ -0,0 +1,484 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#if !defined(YSFX_INCLUDED_YSFX_H) | |||
#define YSFX_INCLUDED_YSFX_H | |||
#include <stdio.h> | |||
#include <stddef.h> | |||
#include <stdint.h> | |||
#include <stdbool.h> | |||
#if defined(_WIN32) | |||
# include <wchar.h> | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#if !defined(YSFX_API) | |||
# if defined(_WIN32) && defined(YSFX_DLL_BUILD) | |||
# define YSFX_API __declspec(dllexport) | |||
# elif defined(__GNUC__) | |||
# define YSFX_API __attribute__((visibility("default"))) | |||
# else | |||
# define YSFX_API | |||
# endif | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
// YSFX definitions | |||
typedef double ysfx_real; | |||
enum { | |||
ysfx_max_sliders = 64, | |||
ysfx_max_channels = 64, | |||
ysfx_max_midi_buses = 16, | |||
ysfx_max_triggers = 10, | |||
}; | |||
typedef enum ysfx_log_level_e { | |||
ysfx_log_info, | |||
ysfx_log_warning, | |||
ysfx_log_error, | |||
} ysfx_log_level; | |||
typedef void (ysfx_log_reporter_t)(intptr_t userdata, ysfx_log_level level, const char *message); | |||
//------------------------------------------------------------------------------ | |||
// YSFX configuration | |||
typedef struct ysfx_config_s ysfx_config_t; | |||
typedef struct ysfx_audio_format_s ysfx_audio_format_t; | |||
// create a new configuration | |||
YSFX_API ysfx_config_t *ysfx_config_new(); | |||
// delete a configuration | |||
YSFX_API void ysfx_config_free(ysfx_config_t *config); | |||
// increase the reference counter | |||
YSFX_API void ysfx_config_add_ref(ysfx_config_t *config); | |||
// set the path of the import root, a folder usually named "Effects" | |||
YSFX_API void ysfx_set_import_root(ysfx_config_t *config, const char *root); | |||
// set the path of the data root, a folder usually named "Data" | |||
YSFX_API void ysfx_set_data_root(ysfx_config_t *config, const char *root); | |||
// get the path of the import root, a folder usually named "Effects" | |||
YSFX_API const char *ysfx_get_import_root(ysfx_config_t *config); | |||
// get the path of the data root, a folder usually named "Data" | |||
YSFX_API const char *ysfx_get_data_root(ysfx_config_t *config); | |||
// guess the undefined root folders, based on the path to the JSFX file | |||
YSFX_API void ysfx_guess_file_roots(ysfx_config_t *config, const char *sourcepath); | |||
// register an audio format into the system | |||
YSFX_API void ysfx_register_audio_format(ysfx_config_t *config, ysfx_audio_format_t *afmt); | |||
// register the builtin audio formats (at least WAV file support) | |||
YSFX_API void ysfx_register_builtin_audio_formats(ysfx_config_t *config); | |||
// set the log reporting function | |||
YSFX_API void ysfx_set_log_reporter(ysfx_config_t *config, ysfx_log_reporter_t *reporter); | |||
// set the callback user data | |||
YSFX_API void ysfx_set_user_data(ysfx_config_t *config, intptr_t userdata); | |||
// get a string which textually represents the log level | |||
YSFX_API const char *ysfx_log_level_string(ysfx_log_level level); | |||
//------------------------------------------------------------------------------ | |||
// YSFX effect | |||
typedef struct ysfx_s ysfx_t; | |||
// create a new effect, taking a reference to config | |||
YSFX_API ysfx_t *ysfx_new(ysfx_config_t *config); | |||
// delete an effect | |||
YSFX_API void ysfx_free(ysfx_t *fx); | |||
// increase the reference counter | |||
YSFX_API void ysfx_add_ref(ysfx_t *fx); | |||
// get the configuration | |||
YSFX_API ysfx_config_t *ysfx_get_config(ysfx_t *fx); | |||
typedef enum ysfx_load_option_e { | |||
// skip imports; useful just for accessing header information and nothing else | |||
ysfx_load_ignoring_imports = 1, | |||
} ysfx_load_option_t; | |||
// load the source code from file without compiling | |||
YSFX_API bool ysfx_load_file(ysfx_t *fx, const char *filepath, uint32_t loadopts); | |||
// unload the source code and any compiled code | |||
YSFX_API void ysfx_unload(ysfx_t *fx); | |||
// check whether the effect is loaded | |||
YSFX_API bool ysfx_is_loaded(ysfx_t *fx); | |||
// get the name of the effect | |||
YSFX_API const char *ysfx_get_name(ysfx_t *fx); | |||
// get the path of the file which is loaded | |||
YSFX_API const char *ysfx_get_file_path(ysfx_t *fx); | |||
// get the author of the effect | |||
YSFX_API const char *ysfx_get_author(ysfx_t *fx); | |||
// get the number of tags of the effect | |||
#define ysfx_get_num_tags(fx) ysfx_get_tags((fx), NULL, 0) | |||
// get the list of tags of the effect | |||
YSFX_API uint32_t ysfx_get_tags(ysfx_t *fx, const char **dest, uint32_t destsize); | |||
// get a single tag of the effect | |||
YSFX_API const char *ysfx_get_tag(ysfx_t *fx, uint32_t index); | |||
// get the number of inputs | |||
YSFX_API uint32_t ysfx_get_num_inputs(ysfx_t *fx); | |||
// get the number of outputs | |||
YSFX_API uint32_t ysfx_get_num_outputs(ysfx_t *fx); | |||
// get the name of the input | |||
YSFX_API const char *ysfx_get_input_name(ysfx_t *fx, uint32_t index); | |||
// get the name of the output | |||
YSFX_API const char *ysfx_get_output_name(ysfx_t *fx, uint32_t index); | |||
// get whether this effect wants metering | |||
YSFX_API bool ysfx_wants_meters(ysfx_t *fx); | |||
// get requested dimensions of the graphics area; 0 means host should decide | |||
YSFX_API bool ysfx_get_gfx_dim(ysfx_t *fx, uint32_t dim[2]); | |||
typedef enum ysfx_section_type_e { | |||
ysfx_section_init = 1, | |||
ysfx_section_slider = 2, | |||
ysfx_section_block = 3, | |||
ysfx_section_sample = 4, | |||
ysfx_section_gfx = 5, | |||
ysfx_section_serialize = 6, | |||
} ysfx_section_type_t; | |||
// get whether the source has the given section | |||
YSFX_API bool ysfx_has_section(ysfx_t *fx, uint32_t type); | |||
typedef struct ysfx_slider_range_s { | |||
ysfx_real def; | |||
ysfx_real min; | |||
ysfx_real max; | |||
ysfx_real inc; | |||
} ysfx_slider_range_t; | |||
// determine if slider exists; call from 0 to max-1 to scan available ones | |||
YSFX_API bool ysfx_slider_exists(ysfx_t *fx, uint32_t index); | |||
// get the name of a slider | |||
YSFX_API const char *ysfx_slider_get_name(ysfx_t *fx, uint32_t index); | |||
// get the range of a slider | |||
YSFX_API bool ysfx_slider_get_range(ysfx_t *fx, uint32_t index, ysfx_slider_range_t *range); | |||
// get whether the slider is an enumeration | |||
YSFX_API bool ysfx_slider_is_enum(ysfx_t *fx, uint32_t index); | |||
// get the number of labels for the enumeration slider | |||
#define ysfx_slider_get_enum_size(fx, index) ysfx_slider_get_enum_names((fx), (index), NULL, 0) | |||
// get the list of labels for the enumeration slider | |||
YSFX_API uint32_t ysfx_slider_get_enum_names(ysfx_t *fx, uint32_t index, const char **dest, uint32_t destsize); | |||
// get a single label for the enumeration slider | |||
YSFX_API const char *ysfx_slider_get_enum_name(ysfx_t *fx, uint32_t slider_index, uint32_t enum_index); | |||
// get whether the slider is a path (implies enumeration) | |||
YSFX_API bool ysfx_slider_is_path(ysfx_t *fx, uint32_t index); | |||
// get whether the slider is initially visible | |||
YSFX_API bool ysfx_slider_is_initially_visible(ysfx_t *fx, uint32_t index); | |||
// get the value of the slider | |||
YSFX_API ysfx_real ysfx_slider_get_value(ysfx_t *fx, uint32_t index); | |||
// set the value of the slider, and call @slider later if value has changed | |||
YSFX_API void ysfx_slider_set_value(ysfx_t *fx, uint32_t index, ysfx_real value); | |||
typedef enum ysfx_compile_option_e { | |||
// skip compiling the @serialize section | |||
ysfx_compile_no_serialize = 1 << 0, | |||
// skip compiling the @gfx section | |||
ysfx_compile_no_gfx = 1 << 1, | |||
} ysfx_compile_option_t; | |||
// compile the previously loaded source | |||
YSFX_API bool ysfx_compile(ysfx_t *fx, uint32_t compileopts); | |||
// check whether the effect is compiled | |||
YSFX_API bool ysfx_is_compiled(ysfx_t *fx); | |||
// get the block size | |||
YSFX_API uint32_t ysfx_get_block_size(ysfx_t *fx); | |||
// get the sample rate | |||
YSFX_API ysfx_real ysfx_get_sample_rate(ysfx_t *fx); | |||
// update the block size; don't forget to call @init | |||
YSFX_API void ysfx_set_block_size(ysfx_t *fx, uint32_t blocksize); | |||
// update the sample rate; don't forget to call @init | |||
YSFX_API void ysfx_set_sample_rate(ysfx_t *fx, ysfx_real samplerate); | |||
// set the capacity of the MIDI buffer | |||
YSFX_API void ysfx_set_midi_capacity(ysfx_t *fx, uint32_t capacity, bool extensible); | |||
// activate and invoke @init | |||
YSFX_API void ysfx_init(ysfx_t *fx); | |||
// get the output latency | |||
YSFX_API ysfx_real ysfx_get_pdc_delay(ysfx_t *fx); | |||
// get the range of channels where output latency applies (end not included) | |||
YSFX_API void ysfx_get_pdc_channels(ysfx_t *fx, uint32_t channels[2]); | |||
// get whether the output latency applies to MIDI as well | |||
YSFX_API bool ysfx_get_pdc_midi(ysfx_t *fx); | |||
typedef enum ysfx_playback_state_e { | |||
ysfx_playback_error = 0, | |||
ysfx_playback_playing = 1, | |||
ysfx_playback_paused = 2, | |||
ysfx_playback_recording = 5, | |||
ysfx_playback_recording_paused = 6, | |||
} ysfx_playback_state_t; | |||
typedef struct ysfx_time_info_s { | |||
// tempo in beats/minute | |||
ysfx_real tempo; | |||
// state of the playback (ysfx_playback_state_t) | |||
uint32_t playback_state; | |||
// time position in seconds | |||
ysfx_real time_position; | |||
// time position in beats | |||
ysfx_real beat_position; | |||
// time signature in fraction form | |||
uint32_t time_signature[2]; | |||
} ysfx_time_info_t; | |||
// update time information; do this before processing the cycle | |||
YSFX_API void ysfx_set_time_info(ysfx_t *fx, const ysfx_time_info_t *info); | |||
typedef struct ysfx_midi_event_s { | |||
// the bus number | |||
uint32_t bus; | |||
// the frame when it happens within the cycle | |||
uint32_t offset; | |||
// the size of the message | |||
uint32_t size; | |||
// the contents of the message | |||
const uint8_t *data; | |||
} ysfx_midi_event_t; | |||
// send MIDI, it will be processed during the cycle | |||
YSFX_API bool ysfx_send_midi(ysfx_t *fx, const ysfx_midi_event_t *event); | |||
// receive MIDI, after having processed the cycle | |||
YSFX_API bool ysfx_receive_midi(ysfx_t *fx, ysfx_midi_event_t *event); | |||
// receive MIDI from a single bus (do not mix with API above, use either) | |||
YSFX_API bool ysfx_receive_midi_from_bus(ysfx_t *fx, uint32_t bus, ysfx_midi_event_t *event); | |||
// send a trigger, it will be processed during the cycle | |||
YSFX_API bool ysfx_send_trigger(ysfx_t *fx, uint32_t index); | |||
// get a bit mask of sliders whose values must be redisplayed, and clear it to zero | |||
YSFX_API uint64_t ysfx_fetch_slider_changes(ysfx_t *fx); | |||
// get a bit mask of sliders whose values must be automated, and clear it to zero | |||
YSFX_API uint64_t ysfx_fetch_slider_automations(ysfx_t *fx); | |||
// get a bit mask of sliders currently visible | |||
YSFX_API uint64_t ysfx_get_slider_visibility(ysfx_t *fx); | |||
// process a cycle in 32-bit float | |||
YSFX_API void ysfx_process_float(ysfx_t *fx, const float *const *ins, float *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames); | |||
// process a cycle in 64-bit float | |||
YSFX_API void ysfx_process_double(ysfx_t *fx, const double *const *ins, double *const *outs, uint32_t num_ins, uint32_t num_outs, uint32_t num_frames); | |||
typedef struct ysfx_state_slider_s { | |||
// index of the slider | |||
uint32_t index; | |||
// value of the slider | |||
ysfx_real value; | |||
} ysfx_state_slider_t; | |||
typedef struct ysfx_state_s { | |||
// values of the sliders | |||
ysfx_state_slider_t *sliders; | |||
// number of sliders | |||
uint32_t slider_count; | |||
// serialized data | |||
uint8_t *data; | |||
// size of serialized data | |||
size_t data_size; | |||
} ysfx_state_t; | |||
// load state | |||
YSFX_API bool ysfx_load_state(ysfx_t *fx, ysfx_state_t *state); | |||
// save current state; release this object when done | |||
YSFX_API ysfx_state_t *ysfx_save_state(ysfx_t *fx); | |||
// release a saved state object | |||
YSFX_API void ysfx_state_free(ysfx_state_t *state); | |||
// duplicate a state object | |||
YSFX_API ysfx_state_t *ysfx_state_dup(ysfx_state_t *state); | |||
typedef struct ysfx_preset_s { | |||
// name of the preset | |||
char *name; | |||
// state of the preset | |||
ysfx_state_t *state; | |||
} ysfx_preset_t; | |||
typedef struct ysfx_bank_s { | |||
// name of the bank | |||
char *name; | |||
// list of presets | |||
ysfx_preset_t *presets; | |||
// number of programs | |||
uint32_t preset_count; | |||
} ysfx_bank_t; | |||
// get the path of the RPL preset bank of the loaded JSFX, if present | |||
YSFX_API const char *ysfx_get_bank_path(ysfx_t *fx); | |||
// read a preset bank from RPL file | |||
YSFX_API ysfx_bank_t *ysfx_load_bank(const char *path); | |||
// free a preset bank | |||
YSFX_API void ysfx_bank_free(ysfx_bank_t *bank); | |||
// type of a function which can enumerate VM variables; returning 0 ends the search | |||
typedef int (ysfx_enum_vars_callback_t)(const char *name, ysfx_real *var, void *userdata); | |||
// enumerate all variables currently in the VM | |||
YSFX_API void ysfx_enum_vars(ysfx_t *fx, ysfx_enum_vars_callback_t *callback, void *userdata); | |||
// find a single variable in the VM | |||
YSFX_API ysfx_real *ysfx_find_var(ysfx_t *fx, const char *name); | |||
// read a chunk of virtual memory from the VM | |||
YSFX_API void ysfx_read_vmem(ysfx_t *fx, uint32_t addr, ysfx_real *dest, uint32_t count); | |||
//------------------------------------------------------------------------------ | |||
// YSFX graphics | |||
// NOTE: all `ysfx_gfx_*` functions must be invoked from a dedicated UI thread | |||
typedef struct ysfx_gfx_config_s { | |||
// opaque user data passed to callbacks | |||
void *user_data; | |||
// the width of the frame buffer (having the scale factor applied) | |||
uint32_t pixel_width; | |||
// the height of the frame buffer (having the scale factor applied) | |||
uint32_t pixel_height; | |||
// the distance in bytes between lines; if 0, it defaults to (4*width) | |||
// currently it is required to be a multiple of 4 | |||
uint32_t pixel_stride; | |||
// the pixel data of the frame buffer, of size (stride*height) bytes | |||
// the byte order in little-endian is 'BGRA', big-endian is 'ARGB' | |||
uint8_t *pixels; | |||
// the scale factor of the display; 1.0 or greater, 2.0 for Retina display | |||
ysfx_real scale_factor; | |||
// show a menu and run it synchronously; returns an item ID >= 1, or 0 if none | |||
int32_t (*show_menu)(void *user_data, const char *menu_spec, int32_t xpos, int32_t ypos); | |||
// change the cursor | |||
void (*set_cursor)(void *user_data, int32_t cursor); | |||
// if index is not -1, get the dropped file at this index (otherwise null) | |||
// if index is -1, clear the list of dropped files, and return null | |||
const char *(*get_drop_file)(void *user_data, int32_t index); | |||
} ysfx_gfx_config_t; | |||
// set up the graphics rendering | |||
YSFX_API void ysfx_gfx_setup(ysfx_t *fx, ysfx_gfx_config_t *gc); | |||
// get whether the current effect is requesting Retina support | |||
YSFX_API bool ysfx_gfx_wants_retina(ysfx_t *fx); | |||
// push a key to the input queue | |||
YSFX_API void ysfx_gfx_add_key(ysfx_t *fx, uint32_t mods, uint32_t key, bool press); | |||
// update mouse information; position is relative to canvas; wheel should be in steps normalized to ±1.0 | |||
YSFX_API void ysfx_gfx_update_mouse(ysfx_t *fx, uint32_t mods, int32_t xpos, int32_t ypos, uint32_t buttons, ysfx_real wheel, ysfx_real hwheel); | |||
// invoke @gfx to paint the graphics; returns whether the framer buffer is modified | |||
YSFX_API bool ysfx_gfx_run(ysfx_t *fx); | |||
//------------------------------------------------------------------------------ | |||
// YSFX key map | |||
// these key definitions match those of pugl | |||
enum { | |||
ysfx_mod_shift = 1 << 0, | |||
ysfx_mod_ctrl = 1 << 1, | |||
ysfx_mod_alt = 1 << 2, | |||
ysfx_mod_super = 1 << 3, | |||
}; | |||
enum { | |||
ysfx_key_backspace = 0x08, | |||
ysfx_key_escape = 0x1b, | |||
ysfx_key_delete = 0x7f, | |||
ysfx_key_f1 = 0xe000, | |||
ysfx_key_f2, | |||
ysfx_key_f3, | |||
ysfx_key_f4, | |||
ysfx_key_f5, | |||
ysfx_key_f6, | |||
ysfx_key_f7, | |||
ysfx_key_f8, | |||
ysfx_key_f9, | |||
ysfx_key_f10, | |||
ysfx_key_f11, | |||
ysfx_key_f12, | |||
ysfx_key_left, | |||
ysfx_key_up, | |||
ysfx_key_right, | |||
ysfx_key_down, | |||
ysfx_key_page_up, | |||
ysfx_key_page_down, | |||
ysfx_key_home, | |||
ysfx_key_end, | |||
ysfx_key_insert, | |||
}; | |||
enum { | |||
ysfx_button_left = 1 << 0, | |||
ysfx_button_middle = 1 << 1, | |||
ysfx_button_right = 1 << 2, | |||
}; | |||
//------------------------------------------------------------------------------ | |||
// YSFX audio formats | |||
typedef struct ysfx_audio_reader_s ysfx_audio_reader_t; | |||
typedef struct ysfx_audio_file_info_s { | |||
uint32_t channels; | |||
ysfx_real sample_rate; | |||
} ysfx_audio_file_info_t; | |||
typedef struct ysfx_audio_format_s { | |||
// quickly checks if this format would be able to handle the given file | |||
bool (*can_handle)(const char *path); | |||
// open an audio file of this format for reading | |||
ysfx_audio_reader_t *(*open)(const char *path); | |||
// close the audio file | |||
void (*close)(ysfx_audio_reader_t *reader); | |||
// get the sample rate and the channel count | |||
ysfx_audio_file_info_t (*info)(ysfx_audio_reader_t *reader); | |||
// get the number of samples left to read | |||
uint64_t (*avail)(ysfx_audio_reader_t *reader); | |||
// move the read pointer back to the beginning | |||
void (*rewind)(ysfx_audio_reader_t *reader); | |||
// read the next block of samples | |||
uint64_t (*read)(ysfx_audio_reader_t *reader, ysfx_real *samples, uint64_t count); | |||
} ysfx_audio_format_t; | |||
//------------------------------------------------------------------------------ | |||
#ifdef __cplusplus | |||
} // extern "C" | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
// YSFX RAII helpers | |||
#if defined(__cplusplus) && (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSVC_LANG >= 201103L)) | |||
#include <memory> | |||
#define YSFX_DEFINE_AUTO_PTR(aptr, styp, freefn) \ | |||
struct aptr##_deleter { \ | |||
void operator()(styp *x) const noexcept { freefn(x); } \ | |||
}; \ | |||
using aptr = std::unique_ptr<styp, aptr##_deleter> | |||
YSFX_DEFINE_AUTO_PTR(ysfx_config_u, ysfx_config_t, ysfx_config_free); | |||
YSFX_DEFINE_AUTO_PTR(ysfx_u, ysfx_t, ysfx_free); | |||
YSFX_DEFINE_AUTO_PTR(ysfx_state_u, ysfx_state_t, ysfx_state_free); | |||
YSFX_DEFINE_AUTO_PTR(ysfx_bank_u, ysfx_bank_t, ysfx_bank_free); | |||
#endif // defined(__cplusplus) && (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSVC_LANG >= 201103L)) | |||
//------------------------------------------------------------------------------ | |||
#endif // !defined(YSFX_INCLUDED_YSFX_H) |
@@ -0,0 +1,139 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com> | |||
* Copyright (C) 2022 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
* | |||
* Permission to use, copy, modify, and/or distribute this software for any purpose with | |||
* or without fee is hereby granted, provided that the above copyright notice and this | |||
* permission notice appear in all copies. | |||
* | |||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD | |||
* TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN | |||
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | |||
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER | |||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | |||
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
*/ | |||
#pragma once | |||
#include <cstdint> | |||
#include <cstring> | |||
#include <cassert> | |||
#include <array> | |||
#include <vector> | |||
// ----------------------------------------------------------------------- | |||
// base64 stuff, based on http://www.adp-gmbh.ch/cpp/common/base64.html | |||
/* | |||
Copyright (C) 2004-2008 René Nyffenegger | |||
This source code is provided 'as-is', without any express or implied | |||
warranty. In no event will the author be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this source code must not be misrepresented; you must not | |||
claim that you wrote the original source code. If you use this source code | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original source code. | |||
3. This notice may not be removed or altered from any source distribution. | |||
René Nyffenegger rene.nyffenegger@adp-gmbh.ch | |||
*/ | |||
// ----------------------------------------------------------------------- | |||
// Helpers | |||
#ifndef DOXYGEN | |||
namespace DistrhoBase64Helpers { | |||
static | |||
std::array<int8_t, 256> createCharIndexTable() | |||
{ | |||
std::array<int8_t, 256> table; | |||
table.fill(-1); | |||
int8_t index = 0; | |||
for (uint8_t c = 'A'; c <= 'Z'; ++c) table[c] = index++; | |||
for (uint8_t c = 'a'; c <= 'z'; ++c) table[c] = index++; | |||
for (uint8_t c = '0'; c <= '9'; ++c) table[c] = index++; | |||
table['+'] = index++; | |||
table['/'] = index++; | |||
return table; | |||
} | |||
static const std::array<int8_t, 256> kCharIndexTable = createCharIndexTable(); | |||
} // namespace DistrhoBase64Helpers | |||
#endif | |||
// ----------------------------------------------------------------------- | |||
static inline | |||
std::vector<uint8_t> d_getChunkFromBase64String(const char* const base64string, std::size_t base64stringLen = ~std::size_t(0)) | |||
{ | |||
std::vector<uint8_t> ret; | |||
if (! base64string) | |||
return ret; | |||
uint32_t i=0, j=0; | |||
uint32_t charArray3[3], charArray4[4]; | |||
if (base64stringLen == ~std::size_t(0)) | |||
base64stringLen = std::strlen(base64string); | |||
ret.reserve(base64stringLen*3/4 + 4); | |||
for (std::size_t l=0; l<base64stringLen; ++l) | |||
{ | |||
const unsigned char c = base64string[l]; | |||
if (c == '\0' || c == '=') | |||
break; | |||
if (DistrhoBase64Helpers::kCharIndexTable[c] == -1) | |||
continue; | |||
charArray4[i++] = static_cast<uint32_t>(c); | |||
if (i == 4) | |||
{ | |||
for (i=0; i<4; ++i) | |||
charArray4[i] = static_cast<uint8_t>(DistrhoBase64Helpers::kCharIndexTable[charArray4[i]]); | |||
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4); | |||
charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2); | |||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3]; | |||
for (i=0; i<3; ++i) | |||
ret.push_back(static_cast<uint8_t>(charArray3[i])); | |||
i = 0; | |||
} | |||
} | |||
if (i != 0) | |||
{ | |||
for (j=0; j<i && j<4; ++j) | |||
charArray4[j] = static_cast<uint8_t>(DistrhoBase64Helpers::kCharIndexTable[charArray4[j]]); | |||
for (j=i; j<4; ++j) | |||
charArray4[j] = 0; | |||
charArray3[0] = (charArray4[0] << 2) + ((charArray4[1] & 0x30) >> 4); | |||
charArray3[1] = ((charArray4[1] & 0xf) << 4) + ((charArray4[2] & 0x3c) >> 2); | |||
charArray3[2] = ((charArray4[2] & 0x3) << 6) + charArray4[3]; | |||
for (j=0; i>0 && j<i-1; j++) | |||
ret.push_back(static_cast<uint8_t>(charArray3[j])); | |||
} | |||
return ret; | |||
} |
@@ -0,0 +1,20 @@ | |||
Copyright (C) 2005 and later Cockos Incorporated | |||
Copyright (C) 2021 and later Jean Pierre Cimalando | |||
Portions copyright other contributors, see each source file for more information | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. |
@@ -0,0 +1 @@ | |||
157d18abd0ac1fe6c4cf59c6f8fbe2c146b39079cd2cc9987837835d4c998e03c2b4816d9e1de13144692081f171d66659b7d1ec735d995b64ee1b033d5f3801 |
@@ -0,0 +1,62 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#define STBI_ONLY_BMP | |||
#include "lice_stb_generic.hpp" | |||
LICE_IBitmap *LICE_LoadBMP(const char *filename, LICE_IBitmap *bmp) | |||
{ | |||
return LICE_LoadSTB(filename, bmp); | |||
} | |||
class LICE_stb_BMPLoader | |||
{ | |||
_LICE_ImageLoader_rec rec; | |||
public: | |||
LICE_stb_BMPLoader() | |||
{ | |||
rec.loadfunc = loadfunc; | |||
rec.get_extlist = get_extlist; | |||
rec._next = LICE_ImageLoader_list; | |||
LICE_ImageLoader_list = &rec; | |||
} | |||
static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase) | |||
{ | |||
if (checkFileName) { | |||
const char *p = filename; | |||
while (*p) | |||
p++; | |||
while (p > filename && *p != '\\' && *p != '/' && *p != '.') | |||
p--; | |||
if (stricmp(p, ".bmp")) | |||
return nullptr; | |||
} | |||
return LICE_LoadSTB(filename, bmpbase); | |||
} | |||
static const char *get_extlist() | |||
{ | |||
return "BMP files (*.BMP)\0*.BMP\0"; | |||
} | |||
}; | |||
void lice_stb_install_bmp_loader() | |||
{ | |||
static LICE_stb_BMPLoader loader; | |||
} |
@@ -0,0 +1,76 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#define STB_IMAGE_IMPLEMENTATION | |||
#define STB_IMAGE_STATIC | |||
#define STBI_WINDOWS_UTF8 | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wunused-function" | |||
#endif | |||
#include "stb_image.h" | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic pop | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
#include "WDL/lice/lice.h" | |||
#include "WDL/wdltypes.h" | |||
static LICE_IBitmap *LICE_LoadSTB(const char *filename, LICE_IBitmap *bmp) | |||
{ | |||
LICE_IBitmap *delbmp = nullptr; | |||
stbi_uc *srcpx = nullptr; | |||
LICE_pixel *dstpx = nullptr; | |||
bool dstflip = false; | |||
unsigned dstspan = 0; | |||
unsigned w = 0; | |||
unsigned h = 0; | |||
unsigned ch = 0; | |||
srcpx = stbi_load(filename, (int *)&w, (int *)&h, (int *)&ch, 4); | |||
if (!srcpx) | |||
goto fail; | |||
if (bmp) | |||
bmp->resize(w, h); | |||
else | |||
bmp = delbmp = new WDL_NEW LICE_MemBitmap(w, h); | |||
if (!bmp || (unsigned)bmp->getWidth() != w || (unsigned)bmp->getHeight() != h) | |||
goto fail; | |||
dstpx = bmp->getBits(); | |||
dstflip = bmp->isFlipped(); | |||
dstspan = bmp->getRowSpan(); | |||
for (unsigned row = 0; row < h; ++row) { | |||
const stbi_uc *src = srcpx + row * (4 * w); | |||
LICE_pixel *dst = dstpx + dstspan * (dstflip ? (h - 1 - row) : row); | |||
for (unsigned col = 0; col < w; ++col, src += 4, ++dst) | |||
*dst = LICE_RGBA(src[0], src[1], src[2], src[3]); | |||
} | |||
stbi_image_free(srcpx); | |||
return bmp; | |||
fail: | |||
delete delbmp; | |||
stbi_image_free(srcpx); | |||
return nullptr; | |||
} |
@@ -0,0 +1,67 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#define STBI_ONLY_GIF | |||
#include "lice_stb_generic.hpp" | |||
LICE_IBitmap *LICE_LoadGIF(const char *filename, LICE_IBitmap *bmp, int *nframes) | |||
{ | |||
LICE_IBitmap *ret = LICE_LoadSTB(filename, bmp); | |||
if (ret) { | |||
if (nframes) // animation not implemented | |||
*nframes = 1; | |||
} | |||
return ret; | |||
} | |||
class LICE_stb_GIFLoader | |||
{ | |||
_LICE_ImageLoader_rec rec; | |||
public: | |||
LICE_stb_GIFLoader() | |||
{ | |||
rec.loadfunc = loadfunc; | |||
rec.get_extlist = get_extlist; | |||
rec._next = LICE_ImageLoader_list; | |||
LICE_ImageLoader_list = &rec; | |||
} | |||
static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase) | |||
{ | |||
if (checkFileName) { | |||
const char *p = filename; | |||
while (*p) | |||
p++; | |||
while (p > filename && *p != '\\' && *p != '/' && *p != '.') | |||
p--; | |||
if (stricmp(p, ".gif")) | |||
return nullptr; | |||
} | |||
return LICE_LoadSTB(filename, bmpbase); | |||
} | |||
static const char *get_extlist() | |||
{ | |||
return "GIF files (*.GIF)\0*.GIF\0"; | |||
} | |||
}; | |||
void lice_stb_install_gif_loader() | |||
{ | |||
static LICE_stb_GIFLoader loader; | |||
} |
@@ -0,0 +1,62 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#define STBI_ONLY_JPEG | |||
#include "lice_stb_generic.hpp" | |||
LICE_IBitmap *LICE_LoadJPG(const char *filename, LICE_IBitmap *bmp) | |||
{ | |||
return LICE_LoadSTB(filename, bmp); | |||
} | |||
class LICE_stb_JPGLoader | |||
{ | |||
_LICE_ImageLoader_rec rec; | |||
public: | |||
LICE_stb_JPGLoader() | |||
{ | |||
rec.loadfunc = loadfunc; | |||
rec.get_extlist = get_extlist; | |||
rec._next = LICE_ImageLoader_list; | |||
LICE_ImageLoader_list = &rec; | |||
} | |||
static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *jpgbase) | |||
{ | |||
if (checkFileName) { | |||
const char *p = filename; | |||
while (*p) | |||
p++; | |||
while (p > filename && *p != '\\' && *p != '/' && *p != '.') | |||
p--; | |||
if (stricmp(p, ".jpg") && stricmp(p, ".jpeg") && stricmp(p, ".jfif")) | |||
return nullptr; | |||
} | |||
return LICE_LoadSTB(filename, jpgbase); | |||
} | |||
static const char *get_extlist() | |||
{ | |||
return "JPEG files (*.JPG;*.JPEG;*.JFIF)\0*.JPG;*.JPEG;*.JFIF\0"; | |||
} | |||
}; | |||
void lice_stb_install_jpg_loader() | |||
{ | |||
static LICE_stb_JPGLoader loader; | |||
} |
@@ -0,0 +1,26 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "lice_stb_loaders.hpp" | |||
void lice_stb_install_loaders() | |||
{ | |||
lice_stb_install_bmp_loader(); | |||
lice_stb_install_gif_loader(); | |||
lice_stb_install_jpg_loader(); | |||
lice_stb_install_png_loader(); | |||
} |
@@ -0,0 +1,23 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
void lice_stb_install_loaders(); | |||
void lice_stb_install_bmp_loader(); | |||
void lice_stb_install_gif_loader(); | |||
void lice_stb_install_jpg_loader(); | |||
void lice_stb_install_png_loader(); |
@@ -0,0 +1,62 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#define STBI_ONLY_PNG | |||
#include "lice_stb_generic.hpp" | |||
LICE_IBitmap *LICE_LoadPNG(const char *filename, LICE_IBitmap *bmp) | |||
{ | |||
return LICE_LoadSTB(filename, bmp); | |||
} | |||
class LICE_stb_PNGLoader | |||
{ | |||
_LICE_ImageLoader_rec rec; | |||
public: | |||
LICE_stb_PNGLoader() | |||
{ | |||
rec.loadfunc = loadfunc; | |||
rec.get_extlist = get_extlist; | |||
rec._next = LICE_ImageLoader_list; | |||
LICE_ImageLoader_list = &rec; | |||
} | |||
static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase) | |||
{ | |||
if (checkFileName) { | |||
const char *p = filename; | |||
while (*p) | |||
p++; | |||
while (p > filename && *p != '\\' && *p != '/' && *p != '.') | |||
p--; | |||
if (stricmp(p, ".png")) | |||
return nullptr; | |||
} | |||
return LICE_LoadSTB(filename, bmpbase); | |||
} | |||
static const char *get_extlist() | |||
{ | |||
return "PNG files (*.PNG)\0*.PNG\0"; | |||
} | |||
}; | |||
void lice_stb_install_png_loader() | |||
{ | |||
static LICE_stb_PNGLoader loader; | |||
} |
@@ -0,0 +1,101 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#define STBIW_WINDOWS_UTF8 | |||
#define STB_IMAGE_WRITE_STATIC | |||
#define STB_IMAGE_WRITE_IMPLEMENTATION | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wunused-function" | |||
#endif | |||
#include "stb_image_write.h" | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic pop | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
#define WDL_NO_DEFINE_MINMAX | |||
#include "WDL/lice/lice.h" | |||
#include "WDL/wdltypes.h" | |||
#include <memory> | |||
static std::unique_ptr<unsigned char[]> bmp_to_stbi(LICE_IBitmap *bmp, unsigned ch) | |||
{ | |||
unsigned w = (unsigned)bmp->getWidth(); | |||
unsigned h = (unsigned)bmp->getHeight(); | |||
unsigned srcspan = (unsigned)bmp->getRowSpan(); | |||
bool srcflip = bmp->isFlipped(); | |||
LICE_pixel *srcpx = bmp->getBits(); | |||
std::unique_ptr<unsigned char[]> result; | |||
if (ch == 4) { | |||
unsigned char *dstpx = new unsigned char[4 * w * h]; | |||
result.reset(dstpx); | |||
for (unsigned row = 0; row < h; ++row) { | |||
LICE_pixel *src = srcpx + srcspan * (srcflip ? (h - 1 - row) : row); | |||
unsigned char *dst = dstpx + row * (4 * w); | |||
for (unsigned col = 0; col < w; ++col, ++src, dst += 4) { | |||
LICE_pixel px = *src; | |||
dst[0] = LICE_GETR(px); | |||
dst[1] = LICE_GETG(px); | |||
dst[2] = LICE_GETB(px); | |||
dst[3] = LICE_GETA(px); | |||
} | |||
} | |||
} | |||
else if (ch == 3) { | |||
unsigned char *dstpx = new unsigned char[3 * w * h]; | |||
result.reset(dstpx); | |||
for (unsigned row = 0; row < h; ++row) { | |||
LICE_pixel *src = srcpx + srcspan * (srcflip ? (h - 1 - row) : row); | |||
unsigned char *dst = dstpx + row * (3 * w); | |||
for (unsigned col = 0; col < w; ++col, ++src, dst += 3) { | |||
LICE_pixel px = *src; | |||
dst[0] = LICE_GETR(px); | |||
dst[1] = LICE_GETG(px); | |||
dst[2] = LICE_GETB(px); | |||
} | |||
} | |||
} | |||
return result; | |||
} | |||
bool LICE_WritePNG(const char *filename, LICE_IBitmap *bmp, bool wantalpha) | |||
{ | |||
unsigned ch = wantalpha ? 4 : 3; | |||
std::unique_ptr<unsigned char[]> data = bmp_to_stbi(bmp, ch); | |||
if (!data) | |||
return false; | |||
return stbi_write_png(filename, bmp->getWidth(), bmp->getHeight(), (int)ch, data.get(), 0); | |||
} | |||
bool LICE_WriteJPG(const char *filename, LICE_IBitmap *bmp, int quality, bool force_baseline) | |||
{ | |||
(void)force_baseline; // always baseline | |||
unsigned ch = 4; | |||
std::unique_ptr<unsigned char[]> data = bmp_to_stbi(bmp, ch); | |||
if (!data) | |||
return false; | |||
return stbi_write_jpg(filename, bmp->getWidth(), bmp->getHeight(), (int)ch, data.get(), quality); | |||
} |
@@ -0,0 +1,169 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include <type_traits> | |||
#include <atomic> | |||
#include <cstdint> | |||
namespace ysfx { | |||
//------------------------------------------------------------------------------ | |||
// sync_bitset64: A lock-free synchronized bitset of size 64 | |||
// | |||
// This is implemented by a single qword on 64-bit machine, otherwise a pair of | |||
// dwords on 32-bit machine, which is to ensure lock-freedom. | |||
// | |||
// This bitset is synchronized but not atomic; a thread might see a | |||
// partially updated set after a modification. Conceptually, it can be seen as | |||
// atomic on individual bit flips (that is, masking operations do not race). | |||
class sync_bitset64_single; | |||
class sync_bitset64_dual; | |||
// could use std::atomic::is_always_lock_free on C++17 and up | |||
using sync_bitset64 = std::conditional< | |||
sizeof(intptr_t) <= 4, sync_bitset64_dual, sync_bitset64_single>::type; | |||
//------------------------------------------------------------------------------ | |||
class sync_bitset64_single { | |||
public: | |||
uint64_t load() const | |||
{ | |||
return bits_.load(std::memory_order_relaxed); | |||
} | |||
void store(uint64_t value) | |||
{ | |||
bits_.store(value, std::memory_order_relaxed); | |||
} | |||
uint64_t exchange(uint64_t value) | |||
{ | |||
return bits_.exchange(value, std::memory_order_relaxed); | |||
} | |||
uint64_t fetch_or(uint64_t value) | |||
{ | |||
return bits_.fetch_or(value, std::memory_order_relaxed); | |||
} | |||
uint64_t fetch_and(uint64_t value) | |||
{ | |||
return bits_.fetch_and(value, std::memory_order_relaxed); | |||
} | |||
uint64_t fetch_xor(uint64_t value) | |||
{ | |||
return bits_.fetch_xor(value, std::memory_order_relaxed); | |||
} | |||
void operator|=(uint64_t value) | |||
{ | |||
fetch_or(value); | |||
} | |||
void operator&=(uint64_t value) | |||
{ | |||
fetch_and(value); | |||
} | |||
void operator^=(uint64_t value) | |||
{ | |||
fetch_xor(value); | |||
} | |||
private: | |||
std::atomic<uint64_t> bits_{0}; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
class sync_bitset64_dual { | |||
public: | |||
uint64_t load() const | |||
{ | |||
return join(lobits_.load(std::memory_order_relaxed), | |||
hibits_.load(std::memory_order_relaxed)); | |||
} | |||
void store(uint64_t value) | |||
{ | |||
lobits_.store(lo(value), std::memory_order_relaxed); | |||
hibits_.store(hi(value), std::memory_order_relaxed); | |||
} | |||
uint64_t exchange(uint64_t value) | |||
{ | |||
return join(lobits_.exchange(lo(value), std::memory_order_relaxed), | |||
hibits_.exchange(hi(value), std::memory_order_relaxed)); | |||
} | |||
uint64_t fetch_or(uint64_t value) | |||
{ | |||
return join(lobits_.fetch_or(lo(value), std::memory_order_relaxed), | |||
hibits_.fetch_or(hi(value), std::memory_order_relaxed)); | |||
} | |||
uint64_t fetch_and(uint64_t value) | |||
{ | |||
return join(lobits_.fetch_and(lo(value), std::memory_order_relaxed), | |||
hibits_.fetch_and(hi(value), std::memory_order_relaxed)); | |||
} | |||
uint64_t fetch_xor(uint64_t value) | |||
{ | |||
return join(lobits_.fetch_xor(lo(value), std::memory_order_relaxed), | |||
hibits_.fetch_xor(hi(value), std::memory_order_relaxed)); | |||
} | |||
void operator|=(uint64_t value) | |||
{ | |||
fetch_or(value); | |||
} | |||
void operator&=(uint64_t value) | |||
{ | |||
fetch_and(value); | |||
} | |||
void operator^=(uint64_t value) | |||
{ | |||
fetch_xor(value); | |||
} | |||
private: | |||
static constexpr uint64_t join(uint32_t lo, uint32_t hi) | |||
{ | |||
return (uint64_t)lo | ((uint64_t)hi << 32); | |||
} | |||
static constexpr uint32_t lo(uint64_t value) | |||
{ | |||
return (uint32_t)(value & 0xFFFFFFFFu); | |||
} | |||
static constexpr uint32_t hi(uint64_t value) | |||
{ | |||
return (uint32_t)(value >> 32); | |||
} | |||
private: | |||
std::atomic<uint32_t> lobits_{0}; | |||
std::atomic<uint32_t> hibits_{0}; | |||
}; | |||
} // namespace ysfx |
@@ -0,0 +1,192 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#include "ysfx_midi.hpp" | |||
#include "ysfx_parse.hpp" | |||
#include "ysfx_api_eel.hpp" | |||
#include "ysfx_api_reaper.hpp" | |||
#include "ysfx_api_file.hpp" | |||
#include "ysfx_api_gfx.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include "utility/sync_bitset.hpp" | |||
#include "WDL/eel2/ns-eel.h" | |||
#include "WDL/eel2/ns-eel-int.h" | |||
#include <unordered_map> | |||
#include <atomic> | |||
YSFX_DEFINE_AUTO_PTR(NSEEL_VMCTX_u, void, NSEEL_VM_free); // NOTE: `NSEEL_VMCTX` is `void *` | |||
YSFX_DEFINE_AUTO_PTR(NSEEL_CODEHANDLE_u, void, NSEEL_code_free); // NOTE: `NSEEL_CODEHANDLE` is `void *` | |||
struct ysfx_source_unit_t { | |||
ysfx_toplevel_t toplevel; | |||
ysfx_header_t header; | |||
}; | |||
using ysfx_source_unit_u = std::unique_ptr< ysfx_source_unit_t>; | |||
enum ysfx_file_type_t { | |||
ysfx_file_type_none, | |||
ysfx_file_type_txt, | |||
ysfx_file_type_raw, | |||
ysfx_file_type_audio, | |||
}; | |||
enum ysfx_thread_id_t { | |||
ysfx_thread_id_none, | |||
ysfx_thread_id_dsp, | |||
ysfx_thread_id_gfx, | |||
}; | |||
struct ysfx_s { | |||
ysfx_config_u config; | |||
eel_string_context_state_u string_ctx; | |||
ysfx::mutex string_mutex; | |||
ysfx::mutex atomic_mutex; | |||
NSEEL_VMCTX_u vm; | |||
// some default values, these are not standard, just arbitrary | |||
uint32_t block_size = 128; | |||
ysfx_real sample_rate = 44100; | |||
uint32_t valid_input_channels = 2; | |||
bool is_freshly_compiled = false; | |||
bool must_compute_init = false; | |||
bool must_compute_slider = false; | |||
std::unordered_map<ysfx_real *, uint32_t> slider_of_var; | |||
// source | |||
struct { | |||
std::string main_file_path; | |||
std::string bank_path; | |||
ysfx_source_unit_u main; | |||
std::vector<ysfx_source_unit_u> imports; | |||
std::unordered_map<std::string, uint32_t> slider_alias; | |||
} source; | |||
// compilation | |||
struct { | |||
bool compiled = false; | |||
std::vector<NSEEL_CODEHANDLE_u> init; | |||
NSEEL_CODEHANDLE_u slider; | |||
NSEEL_CODEHANDLE_u block; | |||
NSEEL_CODEHANDLE_u sample; | |||
NSEEL_CODEHANDLE_u gfx; | |||
NSEEL_CODEHANDLE_u serialize; | |||
} code; | |||
// VM variables | |||
struct { | |||
EEL_F *spl[ysfx_max_channels] = {}; | |||
EEL_F *slider[ysfx_max_sliders] = {}; | |||
EEL_F *srate = nullptr; | |||
EEL_F *num_ch = nullptr; | |||
EEL_F *samplesblock = nullptr; | |||
EEL_F *trigger = nullptr; | |||
EEL_F *tempo = nullptr; | |||
EEL_F *play_state = nullptr; | |||
EEL_F *play_position = nullptr; | |||
EEL_F *beat_position = nullptr; | |||
EEL_F *ts_num = nullptr; | |||
EEL_F *ts_denom = nullptr; | |||
EEL_F *ext_noinit = nullptr; | |||
EEL_F *ext_nodenorm = nullptr; | |||
EEL_F *ext_midi_bus = nullptr; | |||
EEL_F *midi_bus = nullptr; | |||
EEL_F *pdc_delay = nullptr; | |||
EEL_F *pdc_bot_ch = nullptr; | |||
EEL_F *pdc_top_ch = nullptr; | |||
EEL_F *pdc_midi = nullptr; | |||
// gfx variables | |||
EEL_F *gfx_r = nullptr; | |||
EEL_F *gfx_g = nullptr; | |||
EEL_F *gfx_b = nullptr; | |||
EEL_F *gfx_a = nullptr; | |||
EEL_F *gfx_a2 = nullptr; | |||
EEL_F *gfx_w = nullptr; | |||
EEL_F *gfx_h = nullptr; | |||
EEL_F *gfx_x = nullptr; | |||
EEL_F *gfx_y = nullptr; | |||
EEL_F *gfx_mode = nullptr; | |||
EEL_F *gfx_clear = nullptr; | |||
EEL_F *gfx_texth = nullptr; | |||
EEL_F *gfx_dest = nullptr; | |||
EEL_F *gfx_ext_retina = nullptr; | |||
EEL_F *mouse_x = nullptr; | |||
EEL_F *mouse_y = nullptr; | |||
EEL_F *mouse_cap = nullptr; | |||
EEL_F *mouse_wheel = nullptr; | |||
EEL_F *mouse_hwheel = nullptr; | |||
// other | |||
EEL_F ret_temp = 0; | |||
} var; | |||
// MIDI | |||
struct { | |||
ysfx_midi_buffer_u in; | |||
ysfx_midi_buffer_u out; | |||
} midi; | |||
// Slider | |||
struct { | |||
ysfx::sync_bitset64 automate_mask; | |||
ysfx::sync_bitset64 change_mask; | |||
ysfx::sync_bitset64 visible_mask; | |||
} slider; | |||
// Triggers | |||
uint32_t triggers = 0; | |||
// Files | |||
struct { | |||
std::vector<ysfx_file_u> list; | |||
ysfx::mutex list_mutex; | |||
} file; | |||
#if !defined(YSFX_NO_GFX) | |||
// Graphics | |||
struct { | |||
ysfx_gfx_state_u state; | |||
ysfx::mutex mutex; | |||
volatile bool ready = false; | |||
volatile bool wants_retina = false; | |||
std::atomic<bool> must_init{false}; | |||
} gfx; | |||
#endif | |||
std::atomic<uint32_t> ref_count{1}; | |||
}; | |||
ysfx_thread_id_t ysfx_get_thread_id(); | |||
void ysfx_set_thread_id(ysfx_thread_id_t id); | |||
void ysfx_unload_source(ysfx_t *fx); | |||
void ysfx_unload_code(ysfx_t *fx); | |||
void ysfx_first_init(ysfx_t *fx); | |||
void ysfx_update_slider_visibility_mask(ysfx_t *fx); | |||
void ysfx_fill_file_enums(ysfx_t *fx); | |||
void ysfx_fix_invalid_enums(ysfx_t *fx); | |||
ysfx_section_t *ysfx_search_section(ysfx_t *fx, uint32_t type, ysfx_toplevel_t **origin = nullptr); | |||
std::string ysfx_resolve_import_path(ysfx_t *fx, const std::string &name, const std::string &origin); | |||
uint32_t ysfx_current_midi_bus(ysfx_t *fx); | |||
void ysfx_clear_files(ysfx_t *fx); | |||
ysfx_file_t *ysfx_get_file(ysfx_t *fx, uint32_t handle, std::unique_lock<ysfx::mutex> &lock, std::unique_lock<ysfx::mutex> *list_lock = nullptr); | |||
int32_t ysfx_insert_file(ysfx_t *fx, ysfx_file_t *file); | |||
void ysfx_serialize(ysfx_t *fx); | |||
uint32_t ysfx_get_slider_of_var(ysfx_t *fx, EEL_F *var); | |||
bool ysfx_find_data_file(ysfx_t *fx, EEL_F *file, std::string &result); | |||
ysfx_file_type_t ysfx_detect_file_type(ysfx_t *fx, const char *path, void **fmtobj); |
@@ -0,0 +1,144 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx.hpp" | |||
#include "ysfx_api_eel.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include <cstring> | |||
#include <cstdlib> | |||
#include <cstddef> | |||
#include "WDL/ptrlist.h" | |||
#include "WDL/assocarray.h" | |||
#include "WDL/mutex.h" | |||
#ifndef EELSCRIPT_NO_STDIO | |||
# define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); } | |||
#endif | |||
//TODO: thread-safety considerations with strings | |||
#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT ysfx_string_max_length | |||
static ysfx::mutex atomic_mutex; | |||
#define EEL_ATOMIC_SET_SCOPE(opaque) ysfx::mutex *mutex = ((opaque) ? &((ysfx_t *)(opaque))->atomic_mutex : &atomic_mutex); | |||
#define EEL_ATOMIC_ENTER mutex->lock() | |||
#define EEL_ATOMIC_LEAVE mutex->unlock() | |||
#include "WDL/eel2/eel_strings.h" | |||
#include "WDL/eel2/eel_misc.h" | |||
#include "WDL/eel2/eel_fft.h" | |||
#include "WDL/eel2/eel_mdct.h" | |||
#include "WDL/eel2/eel_atomic.h" | |||
//------------------------------------------------------------------------------ | |||
void ysfx_api_init_eel() | |||
{ | |||
EEL_string_register(); | |||
EEL_fft_register(); | |||
EEL_mdct_register(); | |||
EEL_string_register(); | |||
EEL_misc_register(); | |||
EEL_atomic_register(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void ysfx_eel_string_initvm(NSEEL_VMCTX vm) | |||
{ | |||
eel_string_initvm(vm); | |||
} | |||
//------------------------------------------------------------------------------ | |||
eel_string_context_state *ysfx_eel_string_context_new() | |||
{ | |||
return new eel_string_context_state; | |||
} | |||
void ysfx_eel_string_context_free(eel_string_context_state *state) | |||
{ | |||
delete state; | |||
} | |||
void ysfx_eel_string_context_update_named_vars(eel_string_context_state *state, NSEEL_VMCTX vm) | |||
{ | |||
state->update_named_vars(vm); | |||
} | |||
//------------------------------------------------------------------------------ | |||
static_assert( | |||
ysfx_string_max_length == EEL_STRING_MAXUSERSTRING_LENGTH_HINT, | |||
"string max lengths do not match"); | |||
bool ysfx_string_access(ysfx_t *fx, ysfx_real id, bool for_write, void (*access)(void *, WDL_FastString &), void *userdata) | |||
{ | |||
void *opaque = fx; | |||
eel_string_context_state *ctx = EEL_STRING_GET_CONTEXT_POINTER(opaque); | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
EEL_STRING_STORAGECLASS *wr = nullptr; | |||
ctx->GetStringForIndex(id, &wr, for_write); | |||
if (!wr) | |||
return false; | |||
access(userdata, *wr); | |||
return true; | |||
} | |||
bool ysfx_string_get(ysfx_t *fx, ysfx_real id, std::string &txt) | |||
{ | |||
return ysfx_string_access(fx, id, false, [](void *ud, WDL_FastString &str) { | |||
((std::string *)ud)->assign(str.Get(), (uint32_t)str.GetLength()); | |||
}, &txt); | |||
} | |||
bool ysfx_string_set(ysfx_t *fx, ysfx_real id, const std::string &txt) | |||
{ | |||
return ysfx_string_access(fx, id, true, [](void *ud, WDL_FastString &str) { | |||
const std::string *txt = (const std::string *)ud; | |||
size_t size = txt->size(); | |||
if (size > ysfx_string_max_length) | |||
size = ysfx_string_max_length; | |||
str.SetRaw(txt->data(), (int)size); | |||
}, (void *)&txt); | |||
} | |||
void ysfx_string_lock(ysfx_t *fx) | |||
{ | |||
fx->string_mutex.lock(); | |||
} | |||
void ysfx_string_unlock(ysfx_t *fx) | |||
{ | |||
fx->string_mutex.unlock(); | |||
} | |||
const char *ysfx_string_access_unlocked(ysfx_t *fx, ysfx_real id, WDL_FastString **fs, bool for_write) | |||
{ | |||
return fx->string_ctx->GetStringForIndex(id, fs, for_write); | |||
} | |||
//------------------------------------------------------------------------------ | |||
// NOTE(jpc) implement this? I guess probably not. | |||
// DSP and UI should not mutex each other. | |||
void NSEEL_HOSTSTUB_EnterMutex() | |||
{ | |||
} | |||
void NSEEL_HOSTSTUB_LeaveMutex() | |||
{ | |||
} |
@@ -0,0 +1,57 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#include <string> | |||
typedef void *NSEEL_VMCTX; | |||
class WDL_FastString; | |||
//------------------------------------------------------------------------------ | |||
void ysfx_api_init_eel(); | |||
//------------------------------------------------------------------------------ | |||
void ysfx_eel_string_initvm(NSEEL_VMCTX vm); | |||
//------------------------------------------------------------------------------ | |||
class eel_string_context_state; | |||
eel_string_context_state *ysfx_eel_string_context_new(); | |||
void ysfx_eel_string_context_free(eel_string_context_state *state); | |||
void ysfx_eel_string_context_update_named_vars(eel_string_context_state *state, NSEEL_VMCTX vm); | |||
YSFX_DEFINE_AUTO_PTR(eel_string_context_state_u, eel_string_context_state, ysfx_eel_string_context_free); | |||
//------------------------------------------------------------------------------ | |||
enum { ysfx_string_max_length = 1 << 16 }; | |||
bool ysfx_string_access(ysfx_t *fx, ysfx_real id, bool for_write, void (*access)(void *, WDL_FastString &), void *userdata); | |||
bool ysfx_string_get(ysfx_t *fx, ysfx_real id, std::string &txt); | |||
bool ysfx_string_set(ysfx_t *fx, ysfx_real id, const std::string &txt); | |||
void ysfx_string_lock(ysfx_t *fx); | |||
void ysfx_string_unlock(ysfx_t *fx); | |||
const char *ysfx_string_access_unlocked(ysfx_t *fx, ysfx_real id, WDL_FastString **fs, bool for_write); | |||
struct ysfx_string_scoped_lock { | |||
ysfx_string_scoped_lock(ysfx_t *fx) : m_fx(fx) { ysfx_string_lock(fx); } | |||
~ysfx_string_scoped_lock() { ysfx_string_unlock(m_fx); } | |||
private: | |||
ysfx_t *m_fx = nullptr; | |||
}; | |||
#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((ysfx_t *)(opaque))->string_ctx.get()) | |||
#define EEL_STRING_GET_FOR_INDEX(x, wr) (ysfx_string_access_unlocked((ysfx_t *)(opaque), x, wr, false)) | |||
#define EEL_STRING_GET_FOR_WRITE(x, wr) (ysfx_string_access_unlocked((ysfx_t *)(opaque), x, wr, true)) | |||
#define EEL_STRING_MUTEXLOCK_SCOPE ysfx_string_scoped_lock lock{(ysfx_t *)(opaque)}; |
@@ -0,0 +1,574 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx.hpp" | |||
#include "ysfx_config.hpp" | |||
#include "ysfx_api_file.hpp" | |||
#include "ysfx_eel_utils.hpp" | |||
#include <cstring> | |||
#include <cstdio> | |||
#include <cassert> | |||
ysfx_raw_file_t::ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename) | |||
: m_vm(vm), | |||
m_stream(ysfx::fopen_utf8(filename, "rb")) | |||
{ | |||
} | |||
int32_t ysfx_raw_file_t::avail() | |||
{ | |||
if (!m_stream) | |||
return 0; | |||
int64_t cur_off = ysfx::ftell_lfs(m_stream.get()); | |||
if (cur_off == -1) | |||
return 0; | |||
if (ysfx::fseek_lfs(m_stream.get(), 0, SEEK_END) == -1) | |||
return 0; | |||
int64_t end_off = ysfx::ftell_lfs(m_stream.get()); | |||
if (end_off == -1) | |||
return 0; | |||
if (ysfx::fseek_lfs(m_stream.get(), cur_off, SEEK_SET) == -1) | |||
return 0; | |||
if ((uint64_t)end_off < (uint64_t)cur_off) | |||
return 0; | |||
uint64_t byte_count = (uint64_t)end_off - (uint64_t)cur_off; | |||
uint64_t f32_count = byte_count / 4; | |||
return (f32_count > 0x7fffffff) ? 0x7fffffff : (uint32_t)f32_count; | |||
} | |||
void ysfx_raw_file_t::rewind() | |||
{ | |||
if (!m_stream) | |||
return; | |||
::rewind(m_stream.get()); | |||
} | |||
bool ysfx_raw_file_t::var(ysfx_real *var) | |||
{ | |||
if (!m_stream) | |||
return false; | |||
uint8_t data[4]; | |||
if (fread(data, 1, 4, m_stream.get()) != 4) | |||
return false; | |||
*var = (EEL_F)ysfx::unpack_f32le(data); | |||
return true; | |||
} | |||
uint32_t ysfx_raw_file_t::mem(uint32_t offset, uint32_t length) | |||
{ | |||
if (!m_stream) | |||
return 0; | |||
ysfx_eel_ram_writer writer{m_vm, offset}; | |||
uint32_t read; | |||
for (read = 0; read < length; ++read) { | |||
ysfx_real value; | |||
if (!var(&value)) | |||
break; | |||
writer.write_next(value); | |||
} | |||
return read; | |||
} | |||
uint32_t ysfx_raw_file_t::string(std::string &str) | |||
{ | |||
if (!m_stream) | |||
return 0; | |||
uint8_t data[4]; | |||
if (fread(data, 1, 4, m_stream.get()) != 4) | |||
return 0; | |||
str.clear(); | |||
uint32_t srclen = ysfx::unpack_u32le(data); | |||
str.reserve((srclen < ysfx_string_max_length) ? srclen : ysfx_string_max_length); | |||
uint32_t count = 0; | |||
for (int byte; count < srclen && (byte = fgetc(m_stream.get())) != EOF; ) { | |||
if (str.size() < ysfx_string_max_length) | |||
str.push_back((unsigned char)byte); | |||
++count; | |||
} | |||
return count; | |||
} | |||
//------------------------------------------------------------------------------ | |||
ysfx_text_file_t::ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename) | |||
: m_vm(vm), | |||
m_stream(ysfx::fopen_utf8(filename, "rb")) | |||
{ | |||
m_buf.reserve(256); | |||
} | |||
int32_t ysfx_text_file_t::avail() | |||
{ | |||
if (!m_stream || ferror(m_stream.get())) | |||
return -1; | |||
return (feof(m_stream.get()) == 0) ? 0 : 1; | |||
} | |||
void ysfx_text_file_t::rewind() | |||
{ | |||
if (!m_stream) | |||
return; | |||
::rewind(m_stream.get()); | |||
} | |||
bool ysfx_text_file_t::var(ysfx_real *var) | |||
{ | |||
if (!m_stream) | |||
return false; | |||
//TODO support the expression language for arithmetic | |||
int ch; | |||
do { | |||
// get the next number separated by newline or comma | |||
// but skip invalid lines | |||
m_buf.clear(); | |||
while ((ch = fgetc(m_stream.get())) != EOF && ch != '\n' && ch != ',') | |||
m_buf.push_back((unsigned char)ch); | |||
const char *startp = m_buf.c_str(); | |||
const char *endp = (char *)startp; | |||
double value = ysfx::dot_strtod(startp, (char **)&endp); | |||
if (endp != startp) { | |||
*var = (EEL_F)value; | |||
return true; | |||
} | |||
} while (ch != EOF); | |||
return false; | |||
} | |||
uint32_t ysfx_text_file_t::mem(uint32_t offset, uint32_t length) | |||
{ | |||
if (!m_stream) | |||
return 0; | |||
ysfx_eel_ram_writer writer{m_vm, offset}; | |||
uint32_t read; | |||
for (read = 0; read < length; ++read) { | |||
ysfx_real value; | |||
if (!var(&value)) | |||
break; | |||
writer.write_next(value); | |||
} | |||
return read; | |||
} | |||
uint32_t ysfx_text_file_t::string(std::string &str) | |||
{ | |||
if (!m_stream) | |||
return 0; | |||
str.clear(); | |||
str.reserve(256); | |||
int ch; | |||
do { | |||
ch = fgetc(m_stream.get()); | |||
if (ch != EOF && str.size() < ysfx_string_max_length) | |||
str.push_back((unsigned char)ch); | |||
} while (ch != EOF && ch != '\n'); | |||
return (uint32_t)str.size(); | |||
} | |||
//------------------------------------------------------------------------------ | |||
ysfx_audio_file_t::ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename) | |||
: m_vm(vm), | |||
m_fmt(fmt), | |||
m_reader(fmt.open(filename), fmt.close) | |||
{ | |||
} | |||
int32_t ysfx_audio_file_t::avail() | |||
{ | |||
if (!m_reader) | |||
return -1; | |||
uint64_t avail = m_fmt.avail(m_reader.get()); | |||
return (avail > 0x7fffffff) ? 0x7fffffff : (int32_t)avail; | |||
} | |||
void ysfx_audio_file_t::rewind() | |||
{ | |||
if (!m_reader) | |||
return; | |||
m_fmt.rewind(m_reader.get()); | |||
} | |||
bool ysfx_audio_file_t::var(ysfx_real *var) | |||
{ | |||
if (!m_reader) | |||
return false; | |||
return m_fmt.read(m_reader.get(), var, 1) == 1; | |||
} | |||
uint32_t ysfx_audio_file_t::mem(uint32_t offset, uint32_t length) | |||
{ | |||
if (!m_reader) | |||
return 0; | |||
uint32_t numread = 0; | |||
ysfx_real *buf = m_buf.get(); | |||
ysfx_eel_ram_writer writer(m_vm, offset); | |||
while (numread < length) { | |||
uint32_t n = length - numread; | |||
if (n > buffer_size) | |||
n = buffer_size; | |||
uint32_t m = (uint32_t)m_fmt.read(m_reader.get(), buf, n); | |||
for (uint32_t i = 0; i < m; ++i) | |||
writer.write_next(buf[i]); | |||
numread += m; | |||
if (m < n) | |||
break; | |||
} | |||
return numread; | |||
} | |||
uint32_t ysfx_audio_file_t::string(std::string &str) | |||
{ | |||
(void)str; | |||
return 0; | |||
} | |||
bool ysfx_audio_file_t::riff(uint32_t &nch, ysfx_real &samplerate) | |||
{ | |||
if (!m_reader) | |||
return false; | |||
ysfx_audio_file_info_t info = m_fmt.info(m_reader.get()); | |||
nch = info.channels; | |||
samplerate = info.sample_rate; | |||
return true; | |||
} | |||
//------------------------------------------------------------------------------ | |||
ysfx_serializer_t::ysfx_serializer_t(NSEEL_VMCTX vm) | |||
: m_vm(vm) | |||
{ | |||
} | |||
void ysfx_serializer_t::begin(bool write, std::string &buffer) | |||
{ | |||
m_write = (int)write; | |||
m_buffer = &buffer; | |||
m_pos = 0; | |||
} | |||
void ysfx_serializer_t::end() | |||
{ | |||
m_write = -1; | |||
m_buffer = nullptr; | |||
} | |||
int32_t ysfx_serializer_t::avail() | |||
{ | |||
if (m_write) | |||
return -1; | |||
else | |||
return 0; | |||
} | |||
void ysfx_serializer_t::rewind() | |||
{ | |||
} | |||
bool ysfx_serializer_t::var(ysfx_real *var) | |||
{ | |||
if (m_write == 1) { | |||
uint8_t buf[4]; | |||
ysfx::pack_f32le((float)*var, buf); | |||
m_buffer->append((char *)buf, 4); | |||
return true; | |||
} | |||
else if (m_write == 0) { | |||
if (m_pos + 4 > m_buffer->size()) { | |||
m_pos = m_buffer->size(); | |||
*var = 0; | |||
return false; | |||
} | |||
*var = (EEL_F)ysfx::unpack_f32le((uint8_t *)&(*m_buffer)[m_pos]); | |||
m_pos += 4; | |||
return true; | |||
} | |||
return false; | |||
} | |||
uint32_t ysfx_serializer_t::mem(uint32_t offset, uint32_t length) | |||
{ | |||
if (m_write == 1) { | |||
ysfx_eel_ram_reader reader{m_vm, offset}; | |||
for (uint32_t i = 0; i < length; ++i) { | |||
ysfx_real value = reader.read_next(); | |||
if (!var(&value)) | |||
return i; | |||
} | |||
return length; | |||
} | |||
else if (m_write == 0) { | |||
ysfx_eel_ram_writer writer{m_vm, offset}; | |||
for (uint32_t i = 0; i < length; ++i) { | |||
ysfx_real value{}; | |||
if (!var(&value)) | |||
return i; | |||
writer.write_next(value); | |||
} | |||
return length; | |||
} | |||
return 0; | |||
} | |||
uint32_t ysfx_serializer_t::string(std::string &str) | |||
{ | |||
// TODO implement me; docs claim support in Reaper 4.59+ but it seems | |||
// non-working (as of Reaper 6.40) | |||
return 0; | |||
} | |||
//------------------------------------------------------------------------------ | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_open(void *opaque, EEL_F *file_) | |||
{ | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::string filepath; | |||
if (!ysfx_find_data_file(fx, file_, filepath)) | |||
return -1; | |||
void *fmtobj = nullptr; | |||
ysfx_file_type_t ftype = ysfx_detect_file_type(fx, filepath.c_str(), &fmtobj); | |||
ysfx_file_u file; | |||
switch (ftype) { | |||
case ysfx_file_type_txt: | |||
file.reset(new ysfx_text_file_t(fx->vm.get(), filepath.c_str())); | |||
break; | |||
case ysfx_file_type_raw: | |||
file.reset(new ysfx_raw_file_t(fx->vm.get(), filepath.c_str())); | |||
break; | |||
case ysfx_file_type_audio: | |||
file.reset(new ysfx_audio_file_t(fx->vm.get(), *(ysfx_audio_format_t *)fmtobj, filepath.c_str())); | |||
break; | |||
case ysfx_file_type_none: | |||
break; | |||
default: | |||
assert(false); | |||
} | |||
if (file) { | |||
int32_t handle = ysfx_insert_file(fx, file.get()); | |||
if (handle == -1) | |||
return -1; | |||
(void)file.release(); | |||
return (EEL_F)(uint32_t)handle; | |||
} | |||
return -1; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_close(void *opaque, EEL_F *handle_) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
if (handle <= 0) //NOTE: cannot close the serializer handle (0) | |||
return -1; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_ptr<ysfx::mutex> file_mutex; | |||
std::unique_lock<ysfx::mutex> lock; | |||
std::unique_lock<ysfx::mutex> list_lock; | |||
// hold both locks to protect file and list access during removal | |||
if (!ysfx_get_file(fx, (uint32_t)handle, lock, &list_lock)) | |||
return -1; | |||
// preserve the locked mutex of the object being removed | |||
file_mutex = std::move(fx->file.list[(uint32_t)handle]->m_mutex); | |||
fx->file.list[(uint32_t)handle].reset(); | |||
return 0; | |||
} | |||
static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_rewind(void *opaque, EEL_F *handle_) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
if (handle < 0) | |||
return handle_; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_lock<ysfx::mutex> lock; | |||
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); | |||
if (!file) | |||
return 0; | |||
file->rewind(); | |||
return handle_; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_var(void *opaque, EEL_F *handle_, EEL_F *var) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
if (handle < 0) | |||
return 0; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_lock<ysfx::mutex> lock; | |||
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); | |||
if (!file) | |||
return 0; | |||
if (!file->var(var)) | |||
return 0; | |||
return 1; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_mem(void *opaque, EEL_F *handle_, EEL_F *offset_, EEL_F *length_) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
int32_t offset = ysfx_eel_round<int32_t>(*offset_); | |||
int32_t length = ysfx_eel_round<int32_t>(*length_); | |||
if (handle < 0 || offset < 0 || length <= 0) | |||
return 0; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_lock<ysfx::mutex> lock; | |||
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); | |||
if (!file) | |||
return 0; | |||
return (EEL_F)file->mem((uint32_t)offset, (uint32_t)length); | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_avail(void *opaque, EEL_F *handle_) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
if (handle < 0) | |||
return 0; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_lock<ysfx::mutex> lock; | |||
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); | |||
if (!file) | |||
return 0; | |||
return file->avail(); | |||
} | |||
static EEL_F *NSEEL_CGEN_CALL ysfx_api_file_riff(void *opaque, EEL_F *handle_, EEL_F *nch_, EEL_F *samplerate_) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
if (handle < 0) | |||
return 0; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_lock<ysfx::mutex> lock; | |||
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); | |||
if (!file) { | |||
*nch_ = 0; | |||
*samplerate_ = 0; | |||
return nch_; | |||
} | |||
uint32_t nch = 0; | |||
ysfx_real samplerate = 0; | |||
if (!file->riff(nch, samplerate)) { | |||
*nch_ = 0; | |||
*samplerate_ = 0; | |||
return nch_; | |||
} | |||
*nch_ = (EEL_F)nch; | |||
*samplerate_ = samplerate; | |||
return nch_; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_text(void *opaque, EEL_F *handle_) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
if (handle < 0) | |||
return 0; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_lock<ysfx::mutex> lock; | |||
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); | |||
if (!file) | |||
return 0; | |||
return (EEL_F)file->is_text(); | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_file_string(void *opaque, EEL_F *handle_, EEL_F *string_) | |||
{ | |||
int32_t handle = ysfx_eel_round<int32_t>(*handle_); | |||
if (handle < 0) | |||
return 0; | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::unique_lock<ysfx::mutex> lock; | |||
ysfx_file_t *file = ysfx_get_file(fx, (uint32_t)handle, lock); | |||
if (!file) | |||
return 0; | |||
std::string txt; | |||
uint32_t count; | |||
if (!file->is_in_write_mode()) { | |||
count = file->string(txt); | |||
ysfx_string_set(fx, *string_, txt); | |||
} | |||
else { | |||
ysfx_string_get(fx, *string_, txt); | |||
count = file->string(txt); | |||
} | |||
return (EEL_F)count; | |||
} | |||
void ysfx_api_init_file() | |||
{ | |||
NSEEL_addfunc_retval("file_open", 1, NSEEL_PProc_THIS, &ysfx_api_file_open); | |||
NSEEL_addfunc_retval("file_close", 1, NSEEL_PProc_THIS, &ysfx_api_file_close); | |||
NSEEL_addfunc_retptr("file_rewind", 1, NSEEL_PProc_THIS, &ysfx_api_file_rewind); | |||
NSEEL_addfunc_retval("file_var", 2, NSEEL_PProc_THIS, &ysfx_api_file_var); | |||
NSEEL_addfunc_retval("file_mem", 3, NSEEL_PProc_THIS, &ysfx_api_file_mem); | |||
NSEEL_addfunc_retval("file_avail", 1, NSEEL_PProc_THIS, &ysfx_api_file_avail); | |||
NSEEL_addfunc_retptr("file_riff", 3, NSEEL_PProc_THIS, &ysfx_api_file_riff); | |||
NSEEL_addfunc_retval("file_text", 1, NSEEL_PProc_THIS, &ysfx_api_file_text); | |||
NSEEL_addfunc_retval("file_string", 2, NSEEL_PProc_THIS, &ysfx_api_file_string); | |||
} |
@@ -0,0 +1,127 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#include "ysfx_utils.hpp" | |||
#include "WDL/eel2/ns-eel.h" | |||
#include "WDL/eel2/ns-eel-int.h" | |||
#include <vector> | |||
#include <memory> | |||
struct ysfx_file_t { | |||
virtual ~ysfx_file_t() {} | |||
virtual int32_t avail() = 0; | |||
virtual void rewind() = 0; | |||
virtual bool var(ysfx_real *var) = 0; | |||
virtual uint32_t mem(uint32_t offset, uint32_t length) = 0; | |||
virtual uint32_t string(std::string &str) = 0; | |||
virtual bool riff(uint32_t &nch, ysfx_real &samplerate) = 0; | |||
virtual bool is_text() = 0; | |||
virtual bool is_in_write_mode() = 0; | |||
std::unique_ptr<ysfx::mutex> m_mutex{new ysfx::mutex}; | |||
}; | |||
using ysfx_file_u = std::unique_ptr<ysfx_file_t>; | |||
//------------------------------------------------------------------------------ | |||
struct ysfx_raw_file_t final : ysfx_file_t { | |||
ysfx_raw_file_t(NSEEL_VMCTX vm, const char *filename); | |||
int32_t avail() override; | |||
void rewind() override; | |||
bool var(ysfx_real *var) override; | |||
uint32_t mem(uint32_t offset, uint32_t length) override; | |||
uint32_t string(std::string &str) override; | |||
bool riff(uint32_t &, ysfx_real &) override { return false; } | |||
bool is_text() override { return false; } | |||
bool is_in_write_mode() override { return false; } | |||
NSEEL_VMCTX m_vm = nullptr; | |||
ysfx::FILE_u m_stream; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
struct ysfx_text_file_t final : ysfx_file_t { | |||
ysfx_text_file_t(NSEEL_VMCTX vm, const char *filename); | |||
int32_t avail() override; | |||
void rewind() override; | |||
bool var(ysfx_real *var) override; | |||
uint32_t mem(uint32_t offset, uint32_t length) override; | |||
uint32_t string(std::string &str) override; | |||
bool riff(uint32_t &, ysfx_real &) override { return false; } | |||
bool is_text() override { return true; } | |||
bool is_in_write_mode() override { return false; } | |||
NSEEL_VMCTX m_vm = nullptr; | |||
ysfx::FILE_u m_stream; | |||
std::string m_buf; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
struct ysfx_audio_file_t final : ysfx_file_t { | |||
ysfx_audio_file_t(NSEEL_VMCTX vm, const ysfx_audio_format_t &fmt, const char *filename); | |||
int32_t avail() override; | |||
void rewind() override; | |||
bool var(ysfx_real *var) override; | |||
uint32_t mem(uint32_t offset, uint32_t length) override; | |||
uint32_t string(std::string &str) override; | |||
bool riff(uint32_t &nch, ysfx_real &samplerate) override; | |||
bool is_text() override { return false; } | |||
bool is_in_write_mode() override { return false; } | |||
NSEEL_VMCTX m_vm = nullptr; | |||
ysfx_audio_format_t m_fmt{}; | |||
std::unique_ptr<ysfx_audio_reader_t, void (*)(ysfx_audio_reader_t *)> m_reader; | |||
enum { buffer_size = 256 }; | |||
std::unique_ptr<ysfx_real[]> m_buf{new ysfx_real[buffer_size]}; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
struct ysfx_serializer_t final : ysfx_file_t { | |||
explicit ysfx_serializer_t(NSEEL_VMCTX vm); | |||
void begin(bool write, std::string &buffer); | |||
void end(); | |||
int32_t avail() override; | |||
void rewind() override; | |||
bool var(ysfx_real *var) override; | |||
uint32_t mem(uint32_t offset, uint32_t length) override; | |||
uint32_t string(std::string &str) override; | |||
bool riff(uint32_t &, ysfx_real &) override { return false; } | |||
bool is_text() override { return false; } | |||
bool is_in_write_mode() override { return m_write == 1; } | |||
NSEEL_VMCTX m_vm{}; | |||
int m_write = -1; | |||
std::string *m_buffer = nullptr; | |||
size_t m_pos = 0; | |||
}; | |||
using ysfx_serializer_u = std::unique_ptr<ysfx_serializer_t>; | |||
//------------------------------------------------------------------------------ | |||
void ysfx_api_init_file(); |
@@ -0,0 +1,470 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx.hpp" | |||
#include "ysfx_config.hpp" | |||
#include "ysfx_api_gfx.hpp" | |||
#include "ysfx_eel_utils.hpp" | |||
#if !defined(YSFX_NO_GFX) | |||
# include "lice_stb/lice_stb_loaders.hpp" | |||
# define WDL_NO_DEFINE_MINMAX | |||
# include "WDL/swell/swell.h" | |||
# include "WDL/lice/lice.h" | |||
# include "WDL/lice/lice_text.h" | |||
# include "WDL/wdlstring.h" | |||
#endif | |||
#include <vector> | |||
#include <queue> | |||
#include <unordered_set> | |||
#include <memory> | |||
#include <atomic> | |||
#include <cassert> | |||
#if !defined(YSFX_NO_GFX) | |||
#define GFX_GET_CONTEXT(opaque) (((opaque)) ? ysfx_gfx_get_context((ysfx_t *)(opaque)) : nullptr) | |||
enum { | |||
ysfx_gfx_max_images = 1024, | |||
ysfx_gfx_max_fonts = 128, | |||
ysfx_gfx_max_input = 1024, | |||
}; | |||
class eel_lice_state; | |||
struct ysfx_gfx_state_t { | |||
ysfx_gfx_state_t(ysfx_t *fx); | |||
~ysfx_gfx_state_t(); | |||
std::unique_ptr<eel_lice_state> lice; | |||
std::queue<uint32_t> input_queue; | |||
std::unordered_set<uint32_t> keys_pressed; | |||
ysfx_real scale = 0.0; | |||
void *callback_data = nullptr; | |||
int (*show_menu)(void *, const char *, int32_t, int32_t) = nullptr; | |||
void (*set_cursor)(void *, int32_t) = nullptr; | |||
const char *(*get_drop_file)(void *user_data, int32_t index) = nullptr; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
#if !defined(YSFX_NO_GFX) | |||
static bool eel_lice_get_filename_for_string(void *opaque, EEL_F idx, WDL_FastString *fs, int iswrite) | |||
{ | |||
if (iswrite) | |||
return false; // this is neither supported nor used | |||
ysfx_t *fx = (ysfx_t *)opaque; | |||
std::string filepath; | |||
if (!ysfx_find_data_file(fx, &idx, filepath)) | |||
return false; | |||
if (fs) fs->Set(filepath.data(), (uint32_t)filepath.size()); | |||
return true; | |||
} | |||
#define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) \ | |||
eel_lice_get_filename_for_string(opaque, (idx), (fs), (p)) | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
#if !defined(YSFX_NO_GFX) | |||
# include "ysfx_api_gfx_lice.hpp" | |||
#else | |||
# include "ysfx_api_gfx_dummy.hpp" | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
#if !defined(YSFX_NO_GFX) | |||
static bool translate_special_key(uint32_t uni_key, uint32_t &jsfx_key) | |||
{ | |||
auto key_c = [](uint8_t a, uint8_t b, uint8_t c, uint8_t d) -> uint32_t { | |||
return a | (b << 8) | (c << 16) | (d << 24); | |||
}; | |||
switch (uni_key) { | |||
default: return false; | |||
case ysfx_key_delete: jsfx_key = key_c('d', 'e', 'l', 0); break; | |||
case ysfx_key_f1: jsfx_key = key_c('f', '1', 0, 0); break; | |||
case ysfx_key_f2: jsfx_key = key_c('f', '2', 0, 0); break; | |||
case ysfx_key_f3: jsfx_key = key_c('f', '3', 0, 0); break; | |||
case ysfx_key_f4: jsfx_key = key_c('f', '4', 0, 0); break; | |||
case ysfx_key_f5: jsfx_key = key_c('f', '5', 0, 0); break; | |||
case ysfx_key_f6: jsfx_key = key_c('f', '6', 0, 0); break; | |||
case ysfx_key_f7: jsfx_key = key_c('f', '7', 0, 0); break; | |||
case ysfx_key_f8: jsfx_key = key_c('f', '8', 0, 0); break; | |||
case ysfx_key_f9: jsfx_key = key_c('f', '9', 0, 0); break; | |||
case ysfx_key_f10: jsfx_key = key_c('f', '1', '0', 0); break; | |||
case ysfx_key_f11: jsfx_key = key_c('f', '1', '1', 0); break; | |||
case ysfx_key_f12: jsfx_key = key_c('f', '1', '2', 0); break; | |||
case ysfx_key_left: jsfx_key = key_c('l', 'e', 'f', 't'); break; | |||
case ysfx_key_up: jsfx_key = key_c('u', 'p', 0, 0); break; | |||
case ysfx_key_right: jsfx_key = key_c('r', 'g', 'h', 't'); break; | |||
case ysfx_key_down: jsfx_key = key_c('d', 'o', 'w', 'n'); break; | |||
case ysfx_key_page_up: jsfx_key = key_c('p', 'g', 'u', 'p'); break; | |||
case ysfx_key_page_down: jsfx_key = key_c('p', 'g', 'd', 'n'); break; | |||
case ysfx_key_home: jsfx_key = key_c('h', 'o', 'm', 'e'); break; | |||
case ysfx_key_end: jsfx_key = key_c('e', 'n', 'd', 0); break; | |||
case ysfx_key_insert: jsfx_key = key_c('i', 'n', 's', 0); break; | |||
} | |||
return true; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getchar(void *opaque, EEL_F *p) | |||
{ | |||
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); | |||
if (!state) | |||
return 0; | |||
if (*p >= 1/*2*/) { // NOTE(jpc) this is 2.0 originally, which seems wrong | |||
if (*p == 65536) { | |||
// TODO implement window flags | |||
return 0; | |||
} | |||
// current key down status | |||
uint32_t key = (uint32_t)*p; | |||
uint32_t key_id; | |||
if (translate_special_key(key, key)) | |||
key_id = key; | |||
else if (key < 256) | |||
key_id = ysfx::latin1_tolower(key); | |||
else // support the Latin-1 character set only | |||
return 0; | |||
return (EEL_F)(state->keys_pressed.find(key_id) != state->keys_pressed.end()); | |||
} | |||
if (!state->input_queue.empty()) { | |||
uint32_t key = state->input_queue.front(); | |||
state->input_queue.pop(); | |||
return (EEL_F)key; | |||
} | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_showmenu(void *opaque, INT_PTR nparms, EEL_F **parms) | |||
{ | |||
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); | |||
if (!state || !state->show_menu) | |||
return 0; | |||
ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx; | |||
std::string desc; | |||
if (!ysfx_string_get(fx, *parms[0], desc) || desc.empty()) | |||
return 0; | |||
int32_t x = (int32_t)*fx->var.gfx_x; | |||
int32_t y = (int32_t)*fx->var.gfx_y; | |||
return state->show_menu(state->callback_data, desc.c_str(), x, y); | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_setcursor(void *opaque, INT_PTR nparms, EEL_F **parms) | |||
{ | |||
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); | |||
if (!state || !state->set_cursor) | |||
return 0; | |||
int32_t id = (int32_t)*parms[0]; | |||
state->set_cursor(state->callback_data, id); | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_getdropfile(void *opaque, INT_PTR np, EEL_F **parms) | |||
{ | |||
ysfx_gfx_state_t *state = GFX_GET_CONTEXT(opaque); | |||
if (!state || !state->get_drop_file) | |||
return 0; | |||
const int32_t idx = (int)*parms[0]; | |||
if (idx < 0) { | |||
state->get_drop_file(state->callback_data, -1); | |||
return 0; | |||
} | |||
const char *file = state->get_drop_file(state->callback_data, idx); | |||
if (!file) | |||
return 0; | |||
if (np > 1) { | |||
ysfx_t *fx = (ysfx_t *)state->lice->m_user_ctx; | |||
ysfx_string_set(fx, *parms[1], file); | |||
} | |||
return 1; | |||
} | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
#if !defined(YSFX_NO_GFX) | |||
ysfx_gfx_state_t::ysfx_gfx_state_t(ysfx_t *fx) | |||
: lice{new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts}} | |||
{ | |||
lice->m_framebuffer = new LICE_WrapperBitmap{nullptr, 0, 0, 0, false}; | |||
} | |||
ysfx_gfx_state_t::~ysfx_gfx_state_t() | |||
{ | |||
} | |||
#endif | |||
ysfx_gfx_state_t *ysfx_gfx_state_new(ysfx_t *fx) | |||
{ | |||
return new ysfx_gfx_state_t{fx}; | |||
} | |||
void ysfx_gfx_state_free(ysfx_gfx_state_t *state) | |||
{ | |||
delete state; | |||
} | |||
void ysfx_gfx_state_set_bitmap(ysfx_gfx_state_t *state, uint8_t *data, uint32_t w, uint32_t h, uint32_t stride) | |||
{ | |||
if (stride == 0) | |||
stride = 4 * w; | |||
eel_lice_state *lice = state->lice.get(); | |||
assert(stride % 4 == 0); | |||
*static_cast<LICE_WrapperBitmap *>(lice->m_framebuffer) = LICE_WrapperBitmap{(LICE_pixel *)data, (int)w, (int)h, (int)(stride / 4), false}; | |||
} | |||
void ysfx_gfx_state_set_scale_factor(ysfx_gfx_state_t *state, ysfx_real scale) | |||
{ | |||
state->scale = scale; | |||
} | |||
void ysfx_gfx_state_set_callback_data(ysfx_gfx_state_t *state, void *callback_data) | |||
{ | |||
state->callback_data = callback_data; | |||
} | |||
void ysfx_gfx_state_set_show_menu_callback(ysfx_gfx_state_t *state, int (*callback)(void *, const char *, int32_t, int32_t)) | |||
{ | |||
state->show_menu = callback; | |||
} | |||
void ysfx_gfx_state_set_set_cursor_callback(ysfx_gfx_state_t *state, void (*callback)(void *, int32_t)) | |||
{ | |||
state->set_cursor = callback; | |||
} | |||
void ysfx_gfx_state_set_get_drop_file_callback(ysfx_gfx_state_t *state, const char *(*callback)(void *, int32_t)) | |||
{ | |||
state->get_drop_file = callback; | |||
} | |||
bool ysfx_gfx_state_is_dirty(ysfx_gfx_state_t *state) | |||
{ | |||
return state->lice->m_framebuffer_dirty; | |||
} | |||
void ysfx_gfx_state_add_key(ysfx_gfx_state_t *state, uint32_t mods, uint32_t key, bool press) | |||
{ | |||
if (key < 1) | |||
return; | |||
uint32_t key_id; | |||
if (translate_special_key(key, key)) | |||
key_id = key; | |||
else if (key < 256) | |||
key_id = ysfx::latin1_tolower(key); | |||
else // support the Latin-1 character set only | |||
return; | |||
uint32_t key_with_mod = key; | |||
if (key_id >= 'a' && key_id <= 'z') { | |||
uint32_t off = (uint32_t)(key_id - 'a'); | |||
if (mods & (ysfx_mod_ctrl|ysfx_mod_alt)) | |||
key_with_mod = off + 257; | |||
else if (mods & ysfx_mod_ctrl) | |||
key_with_mod = off + 1; | |||
else if (mods & ysfx_mod_alt) | |||
key_with_mod = off + 321; | |||
} | |||
if (press && key_with_mod > 0) { | |||
while (state->input_queue.size() >= ysfx_gfx_max_input) | |||
state->input_queue.pop(); | |||
state->input_queue.push(key_with_mod); | |||
} | |||
if (press) | |||
state->keys_pressed.insert(key_id); | |||
else | |||
state->keys_pressed.erase(key_id); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void ysfx_gfx_enter(ysfx_t *fx, bool doinit) | |||
{ | |||
fx->gfx.mutex.lock(); | |||
ysfx_gfx_state_t *state = fx->gfx.state.get(); | |||
if (doinit) { | |||
if (fx->gfx.must_init.exchange(false, std::memory_order_acquire)) { | |||
*fx->var.gfx_r = 1.0; | |||
*fx->var.gfx_g = 1.0; | |||
*fx->var.gfx_b = 1.0; | |||
*fx->var.gfx_a = 1.0; | |||
*fx->var.gfx_a2 = 1.0; | |||
*fx->var.gfx_dest = -1.0; | |||
*fx->var.mouse_wheel = 0.0; | |||
*fx->var.mouse_hwheel = 0.0; | |||
// NOTE the above are according to eel_lice.h `resetVarsToStock` | |||
// it helps to reset a few more, especially for clearing | |||
*fx->var.gfx_mode = 0; | |||
*fx->var.gfx_clear = 0; | |||
*fx->var.gfx_texth = 0; | |||
*fx->var.mouse_cap = 0; | |||
// reset key state | |||
state->input_queue = {}; | |||
state->keys_pressed = {}; | |||
// reset lice | |||
eel_lice_state *lice = state->lice.get(); | |||
LICE_WrapperBitmap framebuffer = *static_cast<LICE_WrapperBitmap *>(lice->m_framebuffer); | |||
state->lice.reset(); | |||
lice = new eel_lice_state{fx->vm.get(), fx, ysfx_gfx_max_images, ysfx_gfx_max_fonts}; | |||
state->lice.reset(lice); | |||
lice->m_framebuffer = new LICE_WrapperBitmap(framebuffer); | |||
// load images from filenames | |||
uint32_t numfiles = (uint32_t)fx->source.main->header.filenames.size(); | |||
for (uint32_t i = 0; i < numfiles; ++i) | |||
lice->gfx_loadimg(fx, (int32_t)i, (EEL_F)i); | |||
fx->gfx.ready = true; | |||
} | |||
} | |||
ysfx_set_thread_id(ysfx_thread_id_gfx); | |||
} | |||
void ysfx_gfx_leave(ysfx_t *fx) | |||
{ | |||
ysfx_set_thread_id(ysfx_thread_id_none); | |||
fx->gfx.mutex.unlock(); | |||
} | |||
ysfx_gfx_state_t *ysfx_gfx_get_context(ysfx_t *fx) | |||
{ | |||
if (!fx) | |||
return nullptr; | |||
// NOTE: make sure that this will be used from the @gfx thread only | |||
if (ysfx_get_thread_id() != ysfx_thread_id_gfx) | |||
return nullptr; | |||
return fx->gfx.state.get(); | |||
} | |||
void ysfx_gfx_prepare(ysfx_t *fx) | |||
{ | |||
ysfx_gfx_state_t *state = ysfx_gfx_get_context(fx); | |||
eel_lice_state *lice = state->lice.get(); | |||
lice->m_framebuffer_dirty = false; | |||
// set variables `gfx_w` and `gfx_h` | |||
ysfx_real gfx_w = (ysfx_real)lice->m_framebuffer->getWidth(); | |||
ysfx_real gfx_h = (ysfx_real)lice->m_framebuffer->getHeight(); | |||
if (state->scale > 1.0) { | |||
gfx_w *= state->scale; | |||
gfx_h *= state->scale; | |||
*fx->var.gfx_ext_retina = state->scale; | |||
} | |||
*fx->var.gfx_w = gfx_w; | |||
*fx->var.gfx_h = gfx_h; | |||
} | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
void ysfx_api_init_gfx() | |||
{ | |||
#if !defined(YSFX_NO_GFX) | |||
lice_stb_install_loaders(); | |||
#endif | |||
NSEEL_addfunc_retptr("gfx_lineto", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto); | |||
NSEEL_addfunc_retptr("gfx_lineto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_lineto2); | |||
NSEEL_addfunc_retptr("gfx_rectto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_rectto); | |||
NSEEL_addfunc_varparm("gfx_rect", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_rect); | |||
NSEEL_addfunc_varparm("gfx_line", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_line); | |||
NSEEL_addfunc_varparm("gfx_gradrect", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_gradrect); | |||
NSEEL_addfunc_varparm("gfx_muladdrect", 7, NSEEL_PProc_THIS, &ysfx_api_gfx_muladdrect); | |||
NSEEL_addfunc_varparm("gfx_deltablit", 9, NSEEL_PProc_THIS, &ysfx_api_gfx_deltablit); | |||
NSEEL_addfunc_exparms("gfx_transformblit", 8, NSEEL_PProc_THIS, &ysfx_api_gfx_transformblit); | |||
NSEEL_addfunc_varparm("gfx_circle", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_circle); | |||
NSEEL_addfunc_varparm("gfx_triangle", 6, NSEEL_PProc_THIS, &ysfx_api_gfx_triangle); | |||
NSEEL_addfunc_varparm("gfx_roundrect", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_roundrect); | |||
NSEEL_addfunc_varparm("gfx_arc", 5, NSEEL_PProc_THIS, &ysfx_api_gfx_arc); | |||
NSEEL_addfunc_retptr("gfx_blurto", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_blurto); | |||
NSEEL_addfunc_exparms("gfx_showmenu", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_showmenu); | |||
NSEEL_addfunc_varparm("gfx_setcursor", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setcursor); | |||
NSEEL_addfunc_retptr("gfx_drawnumber", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_drawnumber); | |||
NSEEL_addfunc_retptr("gfx_drawchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawchar); | |||
NSEEL_addfunc_varparm("gfx_drawstr", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_drawstr); | |||
NSEEL_addfunc_retptr("gfx_measurestr", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurestr); | |||
NSEEL_addfunc_retptr("gfx_measurechar", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_measurechar); | |||
NSEEL_addfunc_varparm("gfx_printf", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_printf); | |||
NSEEL_addfunc_retptr("gfx_setpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setpixel); | |||
NSEEL_addfunc_retptr("gfx_getpixel", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getpixel); | |||
NSEEL_addfunc_retptr("gfx_getimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_getimgdim); | |||
NSEEL_addfunc_retval("gfx_setimgdim", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_setimgdim); | |||
NSEEL_addfunc_retval("gfx_loadimg", 2, NSEEL_PProc_THIS, &ysfx_api_gfx_loadimg); | |||
NSEEL_addfunc_retptr("gfx_blit", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blit); | |||
NSEEL_addfunc_retptr("gfx_blitext", 3, NSEEL_PProc_THIS, &ysfx_api_gfx_blitext); | |||
NSEEL_addfunc_varparm("gfx_blit", 4, NSEEL_PProc_THIS, &ysfx_api_gfx_blit2); | |||
NSEEL_addfunc_varparm("gfx_setfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_setfont); | |||
NSEEL_addfunc_varparm("gfx_getfont", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getfont); | |||
NSEEL_addfunc_varparm("gfx_set", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_set); | |||
NSEEL_addfunc_varparm("gfx_getdropfile", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getdropfile); | |||
NSEEL_addfunc_varparm("gfx_getsyscol", 0, NSEEL_PProc_THIS, &ysfx_api_gfx_getsyscol); | |||
NSEEL_addfunc_retval("gfx_getchar", 1, NSEEL_PProc_THIS, &ysfx_api_gfx_getchar); | |||
} | |||
//------------------------------------------------------------------------------ | |||
// SWELL helpers | |||
#if !defined(YSFX_NO_GFX) | |||
// implement these GL functions on SWELL targets where they are missing | |||
#if !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX) | |||
void SWELL_SetViewGL(HWND h, char wantGL) | |||
{ | |||
} | |||
bool SWELL_GetViewGL(HWND h) | |||
{ | |||
return false; | |||
} | |||
bool SWELL_SetGLContextToView(HWND h) | |||
{ | |||
return false; | |||
} | |||
#endif // !defined(SWELL_TARGET_GDK) && !defined(SWELL_TARGET_OSX) | |||
#endif |
@@ -0,0 +1,50 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#if !defined(YSFX_NO_GFX) | |||
struct ysfx_gfx_state_t; | |||
ysfx_gfx_state_t *ysfx_gfx_state_new(ysfx_t *fx); | |||
void ysfx_gfx_state_free(ysfx_gfx_state_t *state); | |||
YSFX_DEFINE_AUTO_PTR(ysfx_gfx_state_u, ysfx_gfx_state_t, ysfx_gfx_state_free); | |||
void ysfx_gfx_state_set_bitmap(ysfx_gfx_state_t *state, uint8_t *data, uint32_t w, uint32_t h, uint32_t stride); | |||
void ysfx_gfx_state_set_scale_factor(ysfx_gfx_state_t *state, ysfx_real scale); | |||
void ysfx_gfx_state_set_callback_data(ysfx_gfx_state_t *state, void *callback_data); | |||
void ysfx_gfx_state_set_show_menu_callback(ysfx_gfx_state_t *state, int (*callback)(void *, const char *, int32_t, int32_t)); | |||
void ysfx_gfx_state_set_set_cursor_callback(ysfx_gfx_state_t *state, void (*callback)(void *, int32_t)); | |||
void ysfx_gfx_state_set_get_drop_file_callback(ysfx_gfx_state_t *state, const char *(*callback)(void *, int32_t)); | |||
bool ysfx_gfx_state_is_dirty(ysfx_gfx_state_t *state); | |||
void ysfx_gfx_state_add_key(ysfx_gfx_state_t *state, uint32_t mods, uint32_t key, bool press); | |||
void ysfx_gfx_state_update_mouse(ysfx_gfx_state_t *state, uint32_t mods, int xpos, int ypos, uint32_t buttons, int wheel, int hwheel); | |||
//------------------------------------------------------------------------------ | |||
void ysfx_gfx_enter(ysfx_t *fx, bool doinit); | |||
void ysfx_gfx_leave(ysfx_t *fx); | |||
ysfx_gfx_state_t *ysfx_gfx_get_context(ysfx_t *fx); | |||
void ysfx_gfx_prepare(ysfx_t *fx); | |||
struct ysfx_scoped_gfx_t { | |||
ysfx_scoped_gfx_t(ysfx_t *fx, bool doinit) : m_fx(fx) { ysfx_gfx_enter(fx, doinit); } | |||
~ysfx_scoped_gfx_t() { ysfx_gfx_leave(m_fx); } | |||
ysfx_t *m_fx = nullptr; | |||
}; | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
void ysfx_api_init_gfx(); |
@@ -0,0 +1,91 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx_eel_utils.hpp" | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_varparm(void *, INT_PTR, EEL_F **) | |||
{ | |||
return 0; | |||
} | |||
static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr1(void *, EEL_F *arg) | |||
{ | |||
return arg; | |||
} | |||
static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr2(void *, EEL_F *arg, EEL_F *) | |||
{ | |||
return arg; | |||
} | |||
static EEL_F *NSEEL_CGEN_CALL ysfx_api_gfx_stub_retptr3(void *, EEL_F *arg, EEL_F *, EEL_F *) | |||
{ | |||
return arg; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval1(void *, EEL_F *) | |||
{ | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval2(void *, EEL_F *, EEL_F *) | |||
{ | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_gfx_stub_retval3(void *, EEL_F *, EEL_F *, EEL_F *) | |||
{ | |||
return 0; | |||
} | |||
#define ysfx_api_gfx_lineto ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_lineto2 ysfx_api_gfx_stub_retptr2 | |||
#define ysfx_api_gfx_rectto ysfx_api_gfx_stub_retptr2 | |||
#define ysfx_api_gfx_rect ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_line ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_gradrect ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_muladdrect ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_deltablit ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_transformblit ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_circle ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_triangle ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_roundrect ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_arc ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_blurto ysfx_api_gfx_stub_retptr2 | |||
#define ysfx_api_gfx_showmenu ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_setcursor ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_drawnumber ysfx_api_gfx_stub_retptr2 | |||
#define ysfx_api_gfx_drawchar ysfx_api_gfx_stub_retptr1 | |||
#define ysfx_api_gfx_drawstr ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_measurestr ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_measurechar ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_printf ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_setpixel ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_getpixel ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_getimgdim ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_setimgdim ysfx_api_gfx_stub_retval3 | |||
#define ysfx_api_gfx_loadimg ysfx_api_gfx_stub_retval2 | |||
#define ysfx_api_gfx_blit ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_blitext ysfx_api_gfx_stub_retptr3 | |||
#define ysfx_api_gfx_blit2 ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_setfont ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_getfont ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_set ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_getdropfile ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_getsyscol ysfx_api_gfx_stub_varparm | |||
#define ysfx_api_gfx_getchar ysfx_api_gfx_stub_retval1 |
@@ -0,0 +1,439 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx.hpp" | |||
#include "ysfx_api_reaper.hpp" | |||
#include "ysfx_api_eel.hpp" | |||
#include "ysfx_eel_utils.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include <cmath> | |||
#include <cstring> | |||
#include <cassert> | |||
#include "WDL/wdlstring.h" | |||
#define REAPER_GET_INTERFACE(opaque) ((opaque) ? (ysfx_t *)(opaque) : nullptr) | |||
static EEL_F *NSEEL_CGEN_CALL ysfx_api_spl(void *opaque, EEL_F *n_) | |||
{ | |||
//NOTE: callable from @gfx thread | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
int32_t n = ysfx_eel_round<int32_t>(*n_); | |||
if (n < 0 || n >= ysfx_max_channels) { | |||
fx->var.ret_temp = 0; | |||
return &fx->var.ret_temp; | |||
} | |||
return fx->var.spl[(uint32_t)n]; | |||
} | |||
static EEL_F *NSEEL_CGEN_CALL ysfx_api_slider(void *opaque, EEL_F *n_) | |||
{ | |||
//NOTE: callable from @gfx thread | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
int32_t n = ysfx_eel_round<int32_t>(*n_); | |||
if (n < 1 || n > ysfx_max_sliders) { | |||
fx->var.ret_temp = 0; | |||
return &fx->var.ret_temp; | |||
} | |||
n -= 1; | |||
return fx->var.slider[(uint32_t)n]; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_next_chg(void *opaque, EEL_F *index_, EEL_F *val_) | |||
{ | |||
//TODO frame-accurate slider changes | |||
(void)opaque; | |||
(void)index_; | |||
(void)val_; | |||
return -1; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_automate(void *opaque, EEL_F *mask_or_slider_) | |||
{ | |||
//NOTE: callable from @gfx thread | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_); | |||
uint64_t mask; | |||
if (slider < ysfx_max_sliders) | |||
mask = (uint64_t)1 << slider; | |||
else | |||
mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_)); | |||
fx->slider.automate_mask |= mask; | |||
fx->slider.change_mask |= mask; | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_sliderchange(void *opaque, EEL_F *mask_or_slider_) | |||
{ | |||
//NOTE: callable from @gfx thread | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_); | |||
uint64_t mask; | |||
if (slider < ysfx_max_sliders) | |||
mask = (uint64_t)1 << slider; | |||
else | |||
mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_)); | |||
fx->slider.change_mask |= mask; | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_slider_show(void *opaque, EEL_F *mask_or_slider_, EEL_F *value_) | |||
{ | |||
//NOTE: callable from @gfx thread | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
uint32_t slider = ysfx_get_slider_of_var(fx, mask_or_slider_); | |||
uint64_t mask; | |||
if (slider < ysfx_max_sliders) | |||
mask = (uint64_t)1 << slider; | |||
else | |||
mask = ysfx_eel_round<uint64_t>(std::fabs(*mask_or_slider_)); | |||
if (*value_ >= (EEL_F)+0.5) { | |||
// show | |||
fx->slider.visible_mask |= mask; | |||
} | |||
else if (*value_ >= (EEL_F)-0.5) { | |||
// hide | |||
mask = ~mask; | |||
fx->slider.visible_mask &= mask; | |||
} | |||
else { | |||
// toggle | |||
mask = fx->slider.visible_mask.fetch_xor(mask) ^ mask; | |||
} | |||
return (EEL_F)mask; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend(void *opaque, INT_PTR np, EEL_F **parms) | |||
{ | |||
if (ysfx_get_thread_id() != ysfx_thread_id_dsp) | |||
return 0; | |||
int32_t offset; | |||
uint8_t msg1; | |||
uint8_t msg2; | |||
uint8_t msg3; | |||
switch (np) { | |||
case 3: | |||
{ | |||
offset = ysfx_eel_round<int32_t>(*parms[0]); | |||
msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]); | |||
const uint32_t msg23 = ysfx_eel_round<int32_t>(*parms[2]); | |||
msg2 = (uint8_t)(msg23 & 0xff); | |||
msg3 = (uint8_t)(msg23 >> 8); | |||
break; | |||
} | |||
case 4: | |||
offset = ysfx_eel_round<int32_t>(*parms[0]); | |||
msg1 = (uint8_t)ysfx_eel_round<int32_t>(*parms[1]); | |||
msg2 = (uint8_t)ysfx_eel_round<int32_t>(*parms[2]); | |||
msg3 = (uint8_t)ysfx_eel_round<int32_t>(*parms[3]); | |||
break; | |||
default: | |||
assert(false); | |||
return 0; | |||
} | |||
if (offset < 0) | |||
offset = 0; | |||
// NOTE(jpc) correct the length of the message | |||
// in case it should be less than 3 bytes | |||
uint32_t length = ysfx_midi_sizeof(msg1); | |||
if (length == 0) // don't know what message this is | |||
length = 3; | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
ysfx_midi_event_t event; | |||
const uint8_t data[] = {msg1, msg2, msg3}; | |||
event.bus = ysfx_current_midi_bus(fx); | |||
event.offset = (uint32_t)offset; | |||
event.size = length; | |||
event.data = data; | |||
if (!ysfx_midi_push(fx->midi.out.get(), &event)) | |||
return 0; | |||
return msg1; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_) | |||
{ | |||
if (ysfx_get_thread_id() != ysfx_thread_id_dsp) | |||
return 0; | |||
int32_t offset = ysfx_eel_round<int32_t>(*offset_); | |||
int32_t buf = ysfx_eel_round<int32_t>(*buf_); | |||
int32_t len = ysfx_eel_round<int32_t>(*len_); | |||
if (len <= 0) | |||
return 0; | |||
if (offset < 0) | |||
offset = 0; | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
ysfx_midi_push_t mp; | |||
if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp)) | |||
return 0; | |||
ysfx_eel_ram_reader reader{fx->vm.get(), buf}; | |||
for (uint32_t i = 0; i < (uint32_t)len; ++i) { | |||
uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next()); | |||
if (!ysfx_midi_push_data(&mp, &byte, 1)) | |||
break; | |||
} | |||
if (!ysfx_midi_push_end(&mp)) | |||
return 0; | |||
return len; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_midisend_str(void *opaque, EEL_F *offset_, EEL_F *str_) | |||
{ | |||
if (ysfx_get_thread_id() != ysfx_thread_id_dsp) | |||
return 0; | |||
int32_t offset = ysfx_eel_round<int32_t>(*offset_); | |||
if (offset < 0) | |||
offset = 0; | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
/// | |||
struct process_data { | |||
ysfx_t *fx = nullptr; | |||
uint32_t offset = 0; | |||
uint32_t result = 0; | |||
}; | |||
process_data pdata; | |||
pdata.offset = (uint32_t)offset; | |||
pdata.fx = fx; | |||
auto process_str = [](void *userdata, WDL_FastString &str) { | |||
process_data *pdata = (process_data *)userdata; | |||
ysfx_t *fx = pdata->fx; | |||
ysfx_midi_event_t event; | |||
event.bus = ysfx_current_midi_bus(fx); | |||
event.offset = pdata->offset; | |||
event.size = (uint32_t)str.GetLength(); | |||
event.data = (const uint8_t *)str.Get(); | |||
pdata->result = ysfx_midi_push(fx->midi.out.get(), &event) ? event.size : 0; | |||
}; | |||
/// | |||
if (!ysfx_string_access(fx, *str_, false, +process_str, &pdata)) | |||
return 0; | |||
return pdata.result; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_midisyx(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *len_) | |||
{ | |||
if (ysfx_get_thread_id() != ysfx_thread_id_dsp) | |||
return 0; | |||
int32_t offset = ysfx_eel_round<int32_t>(*offset_); | |||
int32_t buf = ysfx_eel_round<int32_t>(*buf_); | |||
int32_t len = ysfx_eel_round<int32_t>(*len_); | |||
if (len <= 0) | |||
return 0; | |||
if (offset < 0) | |||
offset = 0; | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
ysfx_midi_push_t mp; | |||
if (!ysfx_midi_push_begin(fx->midi.out.get(), ysfx_current_midi_bus(fx), (uint32_t)offset, &mp)) | |||
return 0; | |||
ysfx_eel_ram_reader reader{fx->vm.get(), buf}; | |||
for (uint32_t i = 0; i < (uint32_t)len; ++i) { | |||
uint8_t byte = (uint8_t)ysfx_eel_round<int32_t>(reader.read_next()); | |||
const uint8_t head = 0xf0; | |||
const uint8_t tail = 0xf7; | |||
if (i == 0 && byte != head) { | |||
if (!ysfx_midi_push_data(&mp, &head, 1)) | |||
break; | |||
} | |||
if (!ysfx_midi_push_data(&mp, &byte, 1)) | |||
break; | |||
if (i + 1 == (uint32_t)len && byte != tail) { | |||
if (!ysfx_midi_push_data(&mp, &tail, 1)) | |||
break; | |||
} | |||
} | |||
if (!ysfx_midi_push_end(&mp)) | |||
return 0; | |||
return len; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv(void *opaque, INT_PTR np, EEL_F **parms) | |||
{ | |||
if (ysfx_get_thread_id() != ysfx_thread_id_dsp) | |||
return 0; | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
uint32_t bus = ysfx_current_midi_bus(fx); | |||
ysfx_midi_event_t event; | |||
bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); | |||
// pass through the sysex events | |||
while (have_event && event.size > 3) { | |||
ysfx_midi_push(fx->midi.out.get(), &event); | |||
have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); | |||
} | |||
if (!have_event) | |||
return 0; | |||
uint8_t msg1 = 0; | |||
uint8_t msg2 = 0; | |||
uint8_t msg3 = 0; | |||
switch (event.size) { | |||
case 3: msg3 = event.data[2]; // fall through | |||
case 2: msg2 = event.data[1]; // fall through | |||
case 1: msg1 = event.data[0]; break; | |||
} | |||
*parms[0] = (EEL_F)event.offset; | |||
*parms[1] = (EEL_F)msg1; | |||
switch (np) { | |||
case 4: | |||
*parms[2] = (EEL_F)msg2; | |||
*parms[3] = (EEL_F)msg3; | |||
break; | |||
case 3: | |||
*parms[2] = (EEL_F)(msg2 + (msg3 << 8)); | |||
break; | |||
default: | |||
assert(false); | |||
return 0; | |||
} | |||
return 1; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_buf(void *opaque, EEL_F *offset_, EEL_F *buf_, EEL_F *recvlen_) | |||
{ | |||
if (ysfx_get_thread_id() != ysfx_thread_id_dsp) | |||
return 0; | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
NSEEL_VMCTX vm = fx->vm.get(); | |||
int32_t buf = ysfx_eel_round<int32_t>(*buf_); | |||
int32_t recvlen = ysfx_eel_round<int32_t>(*recvlen_); | |||
if (recvlen < 0) | |||
recvlen = 0; | |||
uint32_t bus = ysfx_current_midi_bus(fx); | |||
ysfx_midi_event_t event; | |||
bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); | |||
// pass through the events larger than the buffer | |||
while (have_event && event.size > (uint32_t)recvlen) { | |||
ysfx_midi_push(fx->midi.out.get(), &event); | |||
have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); | |||
} | |||
if (!have_event) | |||
return 0; | |||
*offset_ = (EEL_F)event.offset; | |||
ysfx_eel_ram_writer writer{vm, buf}; | |||
for (uint32_t i = 0; i < event.size; ++i) | |||
writer.write_next(event.data[i]); | |||
return event.size; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL ysfx_api_midirecv_str(void *opaque, EEL_F *offset_, EEL_F *str_) | |||
{ | |||
if (ysfx_get_thread_id() != ysfx_thread_id_dsp) | |||
return 0; | |||
ysfx_t *fx = REAPER_GET_INTERFACE(opaque); | |||
uint32_t bus = ysfx_current_midi_bus(fx); | |||
ysfx_midi_event_t event; | |||
bool have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); | |||
// pass through the events larger than the maximum string | |||
while (have_event && event.size > ysfx_string_max_length) { | |||
ysfx_midi_push(fx->midi.out.get(), &event); | |||
have_event = ysfx_midi_get_next_from_bus(fx->midi.in.get(), bus, &event); | |||
} | |||
if (!have_event) | |||
return 0; | |||
auto process_str = [](void *userdata, WDL_FastString &str) { | |||
ysfx_midi_event_t *event = (ysfx_midi_event_t *)userdata; | |||
str.SetRaw((const char *)event->data, (int32_t)event->size); | |||
}; | |||
/// | |||
if (!ysfx_string_access(fx, *str_, true, +process_str, &event)) | |||
return 0; | |||
*offset_ = (EEL_F)event.offset; | |||
return event.size; | |||
} | |||
//------------------------------------------------------------------------------ | |||
void ysfx_api_init_reaper() | |||
{ | |||
NSEEL_addfunc_retptr("spl", 1, NSEEL_PProc_THIS, &ysfx_api_spl); | |||
NSEEL_addfunc_retptr("slider", 1, NSEEL_PProc_THIS, &ysfx_api_slider); | |||
NSEEL_addfunc_retval("slider_next_chg", 2, NSEEL_PProc_THIS, &ysfx_api_slider_next_chg); | |||
NSEEL_addfunc_retval("slider_automate", 1, NSEEL_PProc_THIS, &ysfx_api_slider_automate); | |||
NSEEL_addfunc_retval("sliderchange", 1, NSEEL_PProc_THIS, &ysfx_api_sliderchange); | |||
NSEEL_addfunc_retval("slider_show", 2, NSEEL_PProc_THIS, &ysfx_api_slider_show); | |||
NSEEL_addfunc_exparms("midisend", 3, NSEEL_PProc_THIS, &ysfx_api_midisend); | |||
NSEEL_addfunc_exparms("midisend", 4, NSEEL_PProc_THIS, &ysfx_api_midisend); | |||
NSEEL_addfunc_retval("midisend_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midisend_buf); | |||
NSEEL_addfunc_retval("midisend_str", 2, NSEEL_PProc_THIS, &ysfx_api_midisend_str); | |||
NSEEL_addfunc_exparms("midirecv", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv); | |||
NSEEL_addfunc_exparms("midirecv", 4, NSEEL_PProc_THIS, &ysfx_api_midirecv); | |||
NSEEL_addfunc_retval("midirecv_buf", 3, NSEEL_PProc_THIS, &ysfx_api_midirecv_buf); | |||
NSEEL_addfunc_retval("midirecv_str", 2, NSEEL_PProc_THIS, &ysfx_api_midirecv_str); | |||
NSEEL_addfunc_retval("midisyx", 3, NSEEL_PProc_THIS, &ysfx_api_midisyx); | |||
} |
@@ -0,0 +1,20 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
void ysfx_api_init_reaper(); |
@@ -0,0 +1,166 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_audio_flac.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include <memory> | |||
#include <cstring> | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wunused-function" | |||
#endif | |||
#define DR_FLAC_IMPLEMENTATION | |||
#define DRFLAC_API static | |||
#define DRFLAC_PRIVATE static | |||
#include "dr_flac.h" | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic pop | |||
#endif | |||
struct drflac_u_deleter { | |||
void operator()(drflac *x) const noexcept { drflac_close(x); } | |||
}; | |||
using drflac_u = std::unique_ptr<drflac, drflac_u_deleter>; | |||
/// | |||
struct ysfx_flac_reader_t { | |||
drflac_u flac; | |||
uint32_t nbuff = 0; | |||
std::unique_ptr<float[]> buff; | |||
}; | |||
static bool ysfx_flac_can_handle(const char *path) | |||
{ | |||
return ysfx::path_has_suffix(path, "flac"); | |||
} | |||
static ysfx_audio_reader_t *ysfx_flac_open(const char *path) | |||
{ | |||
#if !defined(_WIN32) | |||
drflac_u flac{drflac_open_file(path, NULL)}; | |||
#else | |||
drflac_u flac{drflac_open_file_w(ysfx::widen(path).c_str(), NULL)}; | |||
#endif | |||
if (!flac) | |||
return nullptr; | |||
std::unique_ptr<ysfx_flac_reader_t> reader{new ysfx_flac_reader_t}; | |||
reader->flac = std::move(flac); | |||
reader->buff.reset(new float[reader->flac->channels]); | |||
return (ysfx_audio_reader_t *)reader.release(); | |||
} | |||
static void ysfx_flac_close(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; | |||
delete reader; | |||
} | |||
static ysfx_audio_file_info_t ysfx_flac_info(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; | |||
ysfx_audio_file_info_t info; | |||
info.channels = reader->flac->channels; | |||
info.sample_rate = (ysfx_real)reader->flac->sampleRate; | |||
return info; | |||
} | |||
static uint64_t ysfx_flac_avail(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; | |||
return reader->nbuff + reader->flac->channels * (reader->flac->totalPCMFrameCount - reader->flac->currentPCMFrame); | |||
} | |||
static void ysfx_flac_rewind(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; | |||
drflac_seek_to_pcm_frame(reader->flac.get(), 0); | |||
reader->nbuff = 0; | |||
} | |||
static uint64_t ysfx_flac_unload_buffer(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) | |||
{ | |||
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; | |||
uint32_t nbuff = reader->nbuff; | |||
if (nbuff > count) | |||
nbuff = (uint32_t)count; | |||
if (nbuff == 0) | |||
return 0; | |||
const float *src = &reader->buff[reader->flac->channels - reader->nbuff]; | |||
for (uint32_t i = 0; i < nbuff; ++i) | |||
samples[i] = src[i]; | |||
reader->nbuff -= nbuff; | |||
return nbuff; | |||
} | |||
static uint64_t ysfx_flac_read(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) | |||
{ | |||
ysfx_flac_reader_t *reader = (ysfx_flac_reader_t *)reader_; | |||
uint32_t channels = reader->flac->channels; | |||
uint64_t readtotal = 0; | |||
if (count == 0) | |||
return readtotal; | |||
else { | |||
uint64_t copied = ysfx_flac_unload_buffer(reader_, samples, count); | |||
samples += copied; | |||
count -= copied; | |||
readtotal += copied; | |||
} | |||
if (count == 0) | |||
return readtotal; | |||
else { | |||
float *f32buf = (float *)samples; | |||
uint64_t readframes = drflac_read_pcm_frames_f32(reader->flac.get(), count / channels, f32buf); | |||
uint64_t readsamples = channels * readframes; | |||
// f32->f64 | |||
for (uint64_t i = readsamples; i-- > 0; ) | |||
samples[i] = f32buf[i]; | |||
samples += readsamples; | |||
count -= readsamples; | |||
readtotal += readsamples; | |||
} | |||
if (count == 0) | |||
return readtotal; | |||
else if (drflac_read_pcm_frames_f32(reader->flac.get(), 1, reader->buff.get()) == 1) { | |||
reader->nbuff = channels; | |||
uint64_t copied = ysfx_flac_unload_buffer(reader_, samples, count); | |||
samples += copied; | |||
count -= copied; | |||
readtotal += copied; | |||
} | |||
return readtotal; | |||
} | |||
const ysfx_audio_format_t ysfx_audio_format_flac = { | |||
&ysfx_flac_can_handle, | |||
&ysfx_flac_open, | |||
&ysfx_flac_close, | |||
&ysfx_flac_info, | |||
&ysfx_flac_avail, | |||
&ysfx_flac_rewind, | |||
&ysfx_flac_read, | |||
}; |
@@ -0,0 +1,21 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
extern const ysfx_audio_format_t ysfx_audio_format_flac; |
@@ -0,0 +1,162 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_audio_wav.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include <memory> | |||
#include <cstring> | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic push | |||
# pragma GCC diagnostic ignored "-Wunused-function" | |||
#endif | |||
#define DR_WAV_IMPLEMENTATION | |||
#define DRWAV_API static | |||
#define DRWAV_PRIVATE static | |||
#include "dr_wav.h" | |||
#if defined(__GNUC__) | |||
# pragma GCC diagnostic pop | |||
#endif | |||
struct ysfx_wav_reader_t { | |||
~ysfx_wav_reader_t() { drwav_uninit(wav.get()); } | |||
std::unique_ptr<drwav> wav; | |||
uint32_t nbuff = 0; | |||
std::unique_ptr<float[]> buff; | |||
}; | |||
static bool ysfx_wav_can_handle(const char *path) | |||
{ | |||
return ysfx::path_has_suffix(path, "wav"); | |||
} | |||
static ysfx_audio_reader_t *ysfx_wav_open(const char *path) | |||
{ | |||
std::unique_ptr<drwav> wav{new drwav}; | |||
#if !defined(_WIN32) | |||
drwav_bool32 initok = drwav_init_file(wav.get(), path, nullptr); | |||
#else | |||
drwav_bool32 initok = drwav_init_file_w(wav.get(), ysfx::widen(path).c_str(), nullptr); | |||
#endif | |||
if (!initok) | |||
return nullptr; | |||
std::unique_ptr<ysfx_wav_reader_t> reader{new ysfx_wav_reader_t}; | |||
reader->wav = std::move(wav); | |||
reader->buff.reset(new float[reader->wav->channels]); | |||
return (ysfx_audio_reader_t *)reader.release(); | |||
} | |||
static void ysfx_wav_close(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; | |||
delete reader; | |||
} | |||
static ysfx_audio_file_info_t ysfx_wav_info(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; | |||
ysfx_audio_file_info_t info; | |||
info.channels = reader->wav->channels; | |||
info.sample_rate = (ysfx_real)reader->wav->sampleRate; | |||
return info; | |||
} | |||
static uint64_t ysfx_wav_avail(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; | |||
return reader->nbuff + reader->wav->channels * (reader->wav->totalPCMFrameCount - reader->wav->readCursorInPCMFrames); | |||
} | |||
static void ysfx_wav_rewind(ysfx_audio_reader_t *reader_) | |||
{ | |||
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; | |||
drwav_seek_to_pcm_frame(reader->wav.get(), 0); | |||
reader->nbuff = 0; | |||
} | |||
static uint64_t ysfx_wav_unload_buffer(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) | |||
{ | |||
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; | |||
uint32_t nbuff = reader->nbuff; | |||
if (nbuff > count) | |||
nbuff = (uint32_t)count; | |||
if (nbuff == 0) | |||
return 0; | |||
const float *src = &reader->buff[reader->wav->channels - reader->nbuff]; | |||
for (uint32_t i = 0; i < nbuff; ++i) | |||
samples[i] = src[i]; | |||
reader->nbuff -= nbuff; | |||
return nbuff; | |||
} | |||
static uint64_t ysfx_wav_read(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) | |||
{ | |||
ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; | |||
uint32_t channels = reader->wav->channels; | |||
uint64_t readtotal = 0; | |||
if (count == 0) | |||
return readtotal; | |||
else { | |||
uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count); | |||
samples += copied; | |||
count -= copied; | |||
readtotal += copied; | |||
} | |||
if (count == 0) | |||
return readtotal; | |||
else { | |||
float *f32buf = (float *)samples; | |||
uint64_t readframes = drwav_read_pcm_frames_f32(reader->wav.get(), count / channels, f32buf); | |||
uint64_t readsamples = channels * readframes; | |||
// f32->f64 | |||
for (uint64_t i = readsamples; i-- > 0; ) | |||
samples[i] = f32buf[i]; | |||
samples += readsamples; | |||
count -= readsamples; | |||
readtotal += readsamples; | |||
} | |||
if (count == 0) | |||
return readtotal; | |||
else if (drwav_read_pcm_frames_f32(reader->wav.get(), 1, reader->buff.get()) == 1) { | |||
reader->nbuff = channels; | |||
uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count); | |||
samples += copied; | |||
count -= copied; | |||
readtotal += copied; | |||
} | |||
return readtotal; | |||
} | |||
const ysfx_audio_format_t ysfx_audio_format_wav = { | |||
&ysfx_wav_can_handle, | |||
&ysfx_wav_open, | |||
&ysfx_wav_close, | |||
&ysfx_wav_info, | |||
&ysfx_wav_avail, | |||
&ysfx_wav_rewind, | |||
&ysfx_wav_read, | |||
}; |
@@ -0,0 +1,21 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
extern const ysfx_audio_format_t ysfx_audio_format_wav; |
@@ -0,0 +1,159 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_config.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include "ysfx_audio_wav.hpp" | |||
#include "ysfx_audio_flac.hpp" | |||
#include <cassert> | |||
ysfx_config_t *ysfx_config_new() | |||
{ | |||
return new ysfx_config_t; | |||
} | |||
void ysfx_config_free(ysfx_config_t *config) | |||
{ | |||
if (!config) | |||
return; | |||
if (config->ref_count.fetch_sub(1, std::memory_order_acq_rel) == 1) | |||
delete config; | |||
} | |||
void ysfx_config_add_ref(ysfx_config_t *config) | |||
{ | |||
config->ref_count.fetch_add(1, std::memory_order_relaxed); | |||
} | |||
void ysfx_set_import_root(ysfx_config_t *config, const char *root) | |||
{ | |||
config->import_root = ysfx::path_ensure_final_separator(root ? root : ""); | |||
} | |||
void ysfx_set_data_root(ysfx_config_t *config, const char *root) | |||
{ | |||
config->data_root = ysfx::path_ensure_final_separator(root ? root : ""); | |||
} | |||
const char *ysfx_get_import_root(ysfx_config_t *config) | |||
{ | |||
return config->import_root.c_str(); | |||
} | |||
const char *ysfx_get_data_root(ysfx_config_t *config) | |||
{ | |||
return config->data_root.c_str(); | |||
} | |||
void ysfx_guess_file_roots(ysfx_config_t *config, const char *sourcepath) | |||
{ | |||
if (config->import_root.empty()) { | |||
bool stop = false; | |||
const std::string sourcedir = ysfx::path_directory(sourcepath); | |||
std::string cur_dir = sourcedir + "../"; | |||
ysfx::file_uid cur_uid; | |||
if (!ysfx::get_file_uid(cur_dir.c_str(), cur_uid)) | |||
stop = true; | |||
while (!stop) { | |||
bool match = | |||
ysfx::exists((cur_dir + "Effects/").c_str()) && | |||
ysfx::exists((cur_dir + "Data/").c_str()); | |||
if (match) { | |||
stop = true; | |||
config->import_root = cur_dir + "Effects/"; | |||
} | |||
else { | |||
cur_dir += "../"; | |||
ysfx::file_uid old_uid = cur_uid; | |||
if (!ysfx::get_file_uid(cur_dir.c_str(), cur_uid) || old_uid == cur_uid) | |||
stop = true; | |||
} | |||
} | |||
} | |||
if (config->data_root.empty() && !config->import_root.empty()) { | |||
const std::string datadir = config->import_root + "../Data/"; | |||
bool match = ysfx::exists(datadir.c_str()); | |||
if (match) | |||
config->data_root = datadir; | |||
} | |||
} | |||
void ysfx_register_audio_format(ysfx_config_t *config, ysfx_audio_format_t *afmt) | |||
{ | |||
config->audio_formats.push_back(*afmt); | |||
} | |||
void ysfx_register_builtin_audio_formats(ysfx_config_t *config) | |||
{ | |||
config->audio_formats.push_back(ysfx_audio_format_wav); | |||
config->audio_formats.push_back(ysfx_audio_format_flac); | |||
} | |||
void ysfx_set_log_reporter(ysfx_config_t *config, ysfx_log_reporter_t *reporter) | |||
{ | |||
config->log_reporter = reporter; | |||
} | |||
void ysfx_set_user_data(ysfx_config_t *config, intptr_t userdata) | |||
{ | |||
config->userdata = userdata; | |||
} | |||
//------------------------------------------------------------------------------ | |||
const char *ysfx_log_level_string(ysfx_log_level level) | |||
{ | |||
switch (level) { | |||
case ysfx_log_info: | |||
return "info"; | |||
case ysfx_log_warning: | |||
return "warning"; | |||
case ysfx_log_error: | |||
return "error"; | |||
default: | |||
assert(false); | |||
return "?"; | |||
} | |||
} | |||
void ysfx_log(ysfx_config_t &conf, ysfx_log_level level, const char *message) | |||
{ | |||
if (conf.log_reporter) | |||
conf.log_reporter(conf.userdata, level, message); | |||
else | |||
fprintf(stderr, "[ysfx] %s: %s\n", ysfx_log_level_string(level), message); | |||
} | |||
void ysfx_logfv(ysfx_config_t &conf, ysfx_log_level level, const char *format, va_list ap) | |||
{ | |||
char buf[256]; | |||
vsnprintf(buf, sizeof(buf), format, ap); | |||
buf[sizeof(buf)-1] = '\0'; | |||
ysfx_log(conf, level, buf); | |||
} | |||
void ysfx_logf(ysfx_config_t &conf, ysfx_log_level level, const char *format, ...) | |||
{ | |||
va_list ap; | |||
va_start(ap, format); | |||
ysfx_logfv(conf, level, format, ap); | |||
va_end(ap); | |||
} |
@@ -0,0 +1,39 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#include <vector> | |||
#include <string> | |||
#include <atomic> | |||
#include <cstdarg> | |||
struct ysfx_config_s { | |||
std::string import_root; | |||
std::string data_root; | |||
std::vector<ysfx_audio_format_t> audio_formats; | |||
ysfx_log_reporter_t *log_reporter = nullptr; | |||
intptr_t userdata = 0; | |||
std::atomic<uint32_t> ref_count{1}; | |||
}; | |||
void ysfx_log(ysfx_config_t &conf, ysfx_log_level level, const char *message); | |||
void ysfx_logfv(ysfx_config_t &conf, ysfx_log_level level, const char *format, va_list ap); | |||
#if defined(__GNUC__) | |||
__attribute__((format(printf, 3, 4))) | |||
#endif | |||
void ysfx_logf(ysfx_config_t &conf, ysfx_log_level level, const char *format, ...); |
@@ -0,0 +1,66 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_eel_utils.hpp" | |||
ysfx_eel_ram_reader::ysfx_eel_ram_reader(NSEEL_VMCTX vm, int64_t addr) | |||
: m_vm(vm), | |||
m_addr(addr) | |||
{ | |||
} | |||
EEL_F ysfx_eel_ram_reader::read_next() | |||
{ | |||
if (m_block_avail == 0) { | |||
m_block = (m_addr < 0 || m_addr > 0xFFFFFFFFu) ? nullptr : | |||
NSEEL_VM_getramptr_noalloc(m_vm, (uint32_t)m_addr, (int32_t *)&m_block_avail); | |||
if (m_block) | |||
m_addr += m_block_avail; | |||
else { | |||
m_addr += 1; | |||
m_block_avail = 1; | |||
} | |||
} | |||
EEL_F value = m_block ? *m_block++ : 0; | |||
m_block_avail -= 1; | |||
return value; | |||
} | |||
//------------------------------------------------------------------------------ | |||
ysfx_eel_ram_writer::ysfx_eel_ram_writer(NSEEL_VMCTX vm, int64_t addr) | |||
: m_vm(vm), | |||
m_addr(addr) | |||
{ | |||
} | |||
bool ysfx_eel_ram_writer::write_next(EEL_F value) | |||
{ | |||
if (m_block_avail == 0) { | |||
m_block = (m_addr < 0 || m_addr > 0xFFFFFFFFu) ? nullptr : | |||
NSEEL_VM_getramptr(m_vm, (uint32_t)m_addr, (int32_t *)&m_block_avail); | |||
if (m_block) | |||
m_addr += m_block_avail; | |||
else { | |||
m_addr += 1; | |||
m_block_avail = 1; | |||
} | |||
} | |||
if (m_block) | |||
*m_block++ = value; | |||
m_block_avail -= 1; | |||
return true; | |||
} |
@@ -0,0 +1,57 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "WDL/eel2/ns-eel.h" | |||
#include "WDL/eel2/ns-eel-int.h" | |||
#include <type_traits> | |||
#include <cstdint> | |||
template <class T> | |||
inline typename std::enable_if<std::is_integral<T>::value, T>::type | |||
ysfx_eel_round(EEL_F value) | |||
{ | |||
return (T)(value + (EEL_F)0.0001); // same one as used in eel2 everywhere | |||
} | |||
//------------------------------------------------------------------------------ | |||
class ysfx_eel_ram_reader { | |||
public: | |||
ysfx_eel_ram_reader() = default; | |||
ysfx_eel_ram_reader(NSEEL_VMCTX vm, int64_t addr); | |||
EEL_F read_next(); | |||
private: | |||
NSEEL_VMCTX m_vm{}; | |||
int64_t m_addr = 0; | |||
const EEL_F *m_block = nullptr; | |||
uint32_t m_block_avail = 0; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
class ysfx_eel_ram_writer { | |||
public: | |||
ysfx_eel_ram_writer() = default; | |||
ysfx_eel_ram_writer(NSEEL_VMCTX vm, int64_t addr); | |||
bool write_next(EEL_F value); | |||
private: | |||
NSEEL_VMCTX m_vm{}; | |||
int64_t m_addr = 0; | |||
EEL_F *m_block = nullptr; | |||
uint32_t m_block_avail = 0; | |||
}; |
@@ -0,0 +1,214 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_midi.hpp" | |||
#include <cstring> | |||
#include <cassert> | |||
void ysfx_midi_reserve(ysfx_midi_buffer_t *midi, uint32_t capacity, bool extensible) | |||
{ | |||
std::vector<uint8_t> data; | |||
data.reserve(capacity); | |||
std::swap(data, midi->data); | |||
midi->extensible = extensible; | |||
ysfx_midi_rewind(midi); | |||
} | |||
void ysfx_midi_clear(ysfx_midi_buffer_t *midi) | |||
{ | |||
midi->data.clear(); | |||
ysfx_midi_rewind(midi); | |||
} | |||
bool ysfx_midi_push(ysfx_midi_buffer_t *midi, const ysfx_midi_event_t *event) | |||
{ | |||
if (event->size > ysfx_midi_message_max_size) | |||
return false; | |||
if (event->bus >= ysfx_max_midi_buses) | |||
return false; | |||
ysfx_midi_header_t header; | |||
if (!midi->extensible) { | |||
size_t writable = midi->data.capacity() - midi->data.size(); | |||
if (writable < sizeof(header) + event->size) | |||
return false; | |||
} | |||
const uint8_t *data = event->data; | |||
const uint8_t *headp = (const uint8_t *)&header; | |||
header.bus = event->bus; | |||
header.offset = event->offset; | |||
header.size = event->size; | |||
midi->data.insert(midi->data.end(), headp, headp + sizeof(header)); | |||
midi->data.insert(midi->data.end(), data, data + header.size); | |||
return true; | |||
} | |||
void ysfx_midi_rewind(ysfx_midi_buffer_t *midi) | |||
{ | |||
midi->read_pos = 0; | |||
for (uint32_t i = 0; i < ysfx_max_midi_buses; ++i) | |||
midi->read_pos_for_bus[i] = 0; | |||
} | |||
bool ysfx_midi_get_next(ysfx_midi_buffer_t *midi, ysfx_midi_event_t *event) | |||
{ | |||
size_t *pos_ptr = &midi->read_pos; | |||
size_t pos = *pos_ptr; | |||
size_t avail = midi->data.size() - pos; | |||
ysfx_midi_header_t header; | |||
if (avail == 0) | |||
return false; | |||
assert(avail >= sizeof(header)); | |||
memcpy(&header, &midi->data[pos], sizeof(header)); | |||
assert(avail >= sizeof(header) + header.size); | |||
event->bus = header.bus; | |||
event->offset = header.offset; | |||
event->size = header.size; | |||
event->data = &midi->data[pos + sizeof(header)]; | |||
*pos_ptr = pos + (sizeof(header) + header.size); | |||
return true; | |||
} | |||
bool ysfx_midi_get_next_from_bus(ysfx_midi_buffer_t *midi, uint32_t bus, ysfx_midi_event_t *event) | |||
{ | |||
if (bus >= ysfx_max_midi_buses) | |||
return false; | |||
size_t *pos_ptr = &midi->read_pos_for_bus[bus]; | |||
size_t pos = *pos_ptr; | |||
size_t avail = midi->data.size() - pos; | |||
ysfx_midi_header_t header; | |||
bool found = false; | |||
while (!found && avail > 0) { | |||
assert(avail >= sizeof(header)); | |||
memcpy(&header, &midi->data[pos], sizeof(header)); | |||
assert(avail >= sizeof(header) + header.size); | |||
found = header.bus == bus; | |||
if (!found) { | |||
pos += sizeof(header) + header.size; | |||
avail -= sizeof(header) + header.size; | |||
} | |||
} | |||
if (!found) { | |||
*pos_ptr = pos; | |||
return false; | |||
} | |||
event->bus = header.bus; | |||
event->offset = header.offset; | |||
event->size = header.size; | |||
event->data = &midi->data[pos + sizeof(header)]; | |||
*pos_ptr = pos + (sizeof(header) + header.size); | |||
return true; | |||
} | |||
bool ysfx_midi_push_begin(ysfx_midi_buffer_t *midi, uint32_t bus, uint32_t offset, ysfx_midi_push_t *mp) | |||
{ | |||
ysfx_midi_header_t header; | |||
mp->midi = midi; | |||
mp->start = midi->data.size(); | |||
mp->count = 0; | |||
mp->eob = false; | |||
if (!midi->extensible) { | |||
size_t writable = midi->data.capacity() - midi->data.size(); | |||
if (writable < sizeof(header)) { | |||
mp->eob = true; | |||
return false; | |||
} | |||
} | |||
header.bus = bus; | |||
header.offset = offset; | |||
header.size = 0; | |||
const uint8_t *headp = (const uint8_t *)&header; | |||
midi->data.insert(midi->data.end(), headp, headp + sizeof(header)); | |||
return true; | |||
} | |||
bool ysfx_midi_push_data(ysfx_midi_push_t *mp, const uint8_t *data, uint32_t size) | |||
{ | |||
if (mp->eob) | |||
return false; | |||
if (size > ysfx_midi_message_max_size || mp->count + size > ysfx_midi_message_max_size) { | |||
mp->eob = true; | |||
return false; | |||
} | |||
ysfx_midi_buffer_t *midi = mp->midi; | |||
if (!midi->extensible) { | |||
size_t writable = midi->data.capacity() - midi->data.size(); | |||
if (writable < size) { | |||
mp->eob = true; | |||
return false; | |||
} | |||
} | |||
midi->data.insert(midi->data.end(), data, data + size); | |||
mp->count += size; | |||
return true; | |||
} | |||
bool ysfx_midi_push_end(ysfx_midi_push_t *mp) | |||
{ | |||
if (mp->eob) { | |||
mp->midi->data.resize(mp->start); | |||
return false; | |||
} | |||
ysfx_midi_header_t header; | |||
uint8_t *headp = &mp->midi->data[mp->start]; | |||
memcpy(&header, headp, sizeof(header)); | |||
header.size = mp->count; | |||
memcpy(headp, &header, sizeof(header)); | |||
return true; | |||
} | |||
//------------------------------------------------------------------------------ | |||
uint32_t ysfx_midi_sizeof(uint8_t id) | |||
{ | |||
if ((id >> 7) == 0) { | |||
return 0; | |||
} | |||
else if ((id >> 4) != 0b1111) { | |||
static const uint8_t sizetable[8] = { | |||
3, 3, 3, 3, 2, 2, 3, | |||
}; | |||
return sizetable[(id >> 4) & 0b111]; | |||
} | |||
else { | |||
static const uint8_t sizetable[16] = { | |||
0, 2, 3, 2, 1, 1, 1, 0, | |||
1, 1, 1, 1, 1, 1, 1, 1, | |||
}; | |||
return sizetable[id & 0b1111]; | |||
} | |||
} |
@@ -0,0 +1,72 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#include <vector> | |||
#include <memory> | |||
struct ysfx_midi_header_t { | |||
uint32_t bus; | |||
uint32_t offset; | |||
uint32_t size; | |||
}; | |||
struct ysfx_midi_buffer_t { | |||
std::vector<uint8_t> data; | |||
size_t read_pos = 0; | |||
size_t read_pos_for_bus[ysfx_max_midi_buses] = {}; | |||
bool extensible = false; | |||
}; | |||
using ysfx_midi_buffer_u = std::unique_ptr<ysfx_midi_buffer_t>; | |||
enum { | |||
ysfx_midi_message_max_size = 1 << 24, | |||
}; | |||
// NOTE: regarding buses, | |||
// The buffer keeps 2 kinds of read positions: global, and per-bus. | |||
// | |||
// These are tracked separately, so use either global/per-bus reading API, | |||
// but not both mixed in the same piece of code. | |||
// | |||
// The JSFX API `midi*` implementations should always use per-bus access: | |||
// if `ext_midi_bus` is true, use the bus defined by `midi_bus`, otherwise 0. | |||
void ysfx_midi_reserve(ysfx_midi_buffer_t *midi, uint32_t capacity, bool extensible); | |||
void ysfx_midi_clear(ysfx_midi_buffer_t *midi); | |||
bool ysfx_midi_push(ysfx_midi_buffer_t *midi, const ysfx_midi_event_t *event); | |||
void ysfx_midi_rewind(ysfx_midi_buffer_t *midi); | |||
bool ysfx_midi_get_next(ysfx_midi_buffer_t *midi, ysfx_midi_event_t *event); | |||
bool ysfx_midi_get_next_from_bus(ysfx_midi_buffer_t *midi, uint32_t bus, ysfx_midi_event_t *event); | |||
// incremental writer into a midi buffer | |||
struct ysfx_midi_push_t { | |||
ysfx_midi_buffer_t *midi = nullptr; | |||
size_t start = 0; | |||
uint32_t count = 0; | |||
bool eob = false; | |||
}; | |||
bool ysfx_midi_push_begin(ysfx_midi_buffer_t *midi, uint32_t bus, uint32_t offset, ysfx_midi_push_t *mp); | |||
bool ysfx_midi_push_data(ysfx_midi_push_t *mp, const uint8_t *data, uint32_t size); | |||
bool ysfx_midi_push_end(ysfx_midi_push_t *mp); | |||
//------------------------------------------------------------------------------ | |||
// determine the length of a midi message according to its status byte | |||
// if length is dynamic, returns 0 | |||
uint32_t ysfx_midi_sizeof(uint8_t id); |
@@ -0,0 +1,380 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_parse.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include <cstdlib> | |||
#include <cstring> | |||
bool ysfx_parse_toplevel(ysfx::text_reader &reader, ysfx_toplevel_t &toplevel, ysfx_parse_error *error) | |||
{ | |||
toplevel = ysfx_toplevel_t{}; | |||
ysfx_section_t *current = new ysfx_section_t; | |||
toplevel.header.reset(current); | |||
std::string line; | |||
uint32_t lineno = 0; | |||
line.reserve(256); | |||
while (reader.read_next_line(line)) { | |||
const char *linep = line.c_str(); | |||
if (linep[0] == '@') { | |||
// a new section starts | |||
ysfx::string_list tokens = ysfx::split_strings_noempty(linep, &ysfx::ascii_isspace); | |||
current = new ysfx_section_t; | |||
if (tokens[0] == "@init") | |||
toplevel.init.reset(current); | |||
else if (tokens[0] == "@slider") | |||
toplevel.slider.reset(current); | |||
else if (tokens[0] == "@block") | |||
toplevel.block.reset(current); | |||
else if (tokens[0] == "@sample") | |||
toplevel.sample.reset(current); | |||
else if (tokens[0] == "@serialize") | |||
toplevel.serialize.reset(current); | |||
else if (tokens[0] == "@gfx") { | |||
toplevel.gfx.reset(current); | |||
long gfx_w = 0; | |||
long gfx_h = 0; | |||
if (tokens.size() > 1) | |||
gfx_w = (long)ysfx::dot_atof(tokens[1].c_str()); | |||
if (tokens.size() > 2) | |||
gfx_h = (long)ysfx::dot_atof(tokens[2].c_str()); | |||
toplevel.gfx_w = (gfx_w > 0) ? (uint32_t)gfx_w : 0; | |||
toplevel.gfx_h = (gfx_h > 0) ? (uint32_t)gfx_h : 0; | |||
} | |||
else { | |||
delete current; | |||
if (error) { | |||
error->line = lineno; | |||
error->message = std::string("Invalid section: ") + line; | |||
} | |||
return false; | |||
} | |||
current->line_offset = lineno + 1; | |||
} | |||
else { | |||
current->text.append(line); | |||
current->text.push_back('\n'); | |||
} | |||
++lineno; | |||
} | |||
return true; | |||
} | |||
void ysfx_parse_header(ysfx_section_t *section, ysfx_header_t &header) | |||
{ | |||
header = ysfx_header_t{}; | |||
ysfx::string_text_reader reader(section->text.c_str()); | |||
std::string line; | |||
//uint32_t lineno = section->line_offset; | |||
line.reserve(256); | |||
/// | |||
auto unprefix = [](const char *text, const char **restp, const char *prefix) -> bool { | |||
size_t len = strlen(prefix); | |||
if (strncmp(text, prefix, len)) | |||
return false; | |||
if (restp) | |||
*restp = text + len; | |||
return true; | |||
}; | |||
//-------------------------------------------------------------------------- | |||
// pass 1: regular metadata | |||
while (reader.read_next_line(line)) { | |||
const char *linep = line.c_str(); | |||
const char *rest = nullptr; | |||
ysfx_slider_t slider; | |||
ysfx_parsed_filename_t filename; | |||
/// | |||
if (unprefix(linep, &rest, "desc:")) { | |||
if (header.desc.empty()) | |||
header.desc = ysfx::trim(rest, &ysfx::ascii_isspace); | |||
} | |||
else if (unprefix(linep, &rest, "author:")) { | |||
if (header.author.empty()) | |||
header.author = ysfx::trim(rest, &ysfx::ascii_isspace); | |||
} | |||
else if (unprefix(linep, &rest, "tags:")) { | |||
if (header.tags.empty()) { | |||
for (const std::string &tag : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace)) | |||
header.tags.push_back(tag); | |||
} | |||
} | |||
else if (unprefix(linep, &rest, "in_pin:")) { | |||
header.explicit_pins = true; | |||
header.in_pins.push_back(ysfx::trim(rest, &ysfx::ascii_isspace)); | |||
} | |||
else if (unprefix(linep, &rest, "out_pin:")) { | |||
header.explicit_pins = true; | |||
header.out_pins.push_back(ysfx::trim(rest, &ysfx::ascii_isspace)); | |||
} | |||
else if (unprefix(linep, &rest, "options:")) { | |||
for (const std::string &opt : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace)) { | |||
size_t pos = opt.find('='); | |||
std::string name = (pos == opt.npos) ? opt : opt.substr(0, pos); | |||
std::string value = (pos == opt.npos) ? std::string{} : opt.substr(pos + 1); | |||
if (name == "gmem") | |||
header.options.gmem = value; | |||
else if (name == "maxmem") { | |||
int32_t maxmem = (int32_t)ysfx::dot_atof(value.c_str()); | |||
header.options.maxmem = (maxmem < 0) ? 0 : (uint32_t)maxmem; | |||
} | |||
else if (name == "want_all_kb") | |||
header.options.want_all_kb = true; | |||
else if (name == "no_meter") | |||
header.options.no_meter = true; | |||
} | |||
} | |||
else if (unprefix(linep, &rest, "import") && ysfx::ascii_isspace(rest[0])) | |||
header.imports.push_back(ysfx::trim(rest + 1, &ysfx::ascii_isspace)); | |||
else if (ysfx_parse_slider(linep, slider)) { | |||
if (slider.id >= ysfx_max_sliders) | |||
continue; | |||
slider.exists = true; | |||
header.sliders[slider.id] = slider; | |||
} | |||
else if (ysfx_parse_filename(linep, filename)) { | |||
if (filename.index != header.filenames.size()) | |||
continue; | |||
header.filenames.push_back(std::move(filename.filename)); | |||
} | |||
//++lineno; | |||
} | |||
//-------------------------------------------------------------------------- | |||
// pass 2: comments | |||
reader = ysfx::string_text_reader{section->text.c_str()}; | |||
while (reader.read_next_line(line)) { | |||
const char *linep = line.c_str(); | |||
const char *rest = nullptr; | |||
// some files contain metadata in the form of comments | |||
// this is not part of spec, but we'll take this info regardless | |||
if (unprefix(linep, &rest, "//author:")) { | |||
if (header.author.empty()) | |||
header.author = ysfx::trim(rest, &ysfx::ascii_isspace); | |||
} | |||
else if (unprefix(linep, &rest, "//tags:")) { | |||
if (header.tags.empty()) { | |||
for (const std::string &tag : ysfx::split_strings_noempty(rest, &ysfx::ascii_isspace)) | |||
header.tags.push_back(tag); | |||
} | |||
} | |||
} | |||
//-------------------------------------------------------------------------- | |||
if (header.in_pins.size() == 1 && !ysfx::ascii_casecmp(header.in_pins.front().c_str(), "none")) | |||
header.in_pins.clear(); | |||
if (header.out_pins.size() == 1 && !ysfx::ascii_casecmp(header.out_pins.front().c_str(), "none")) | |||
header.out_pins.clear(); | |||
if (header.in_pins.size() > ysfx_max_channels) | |||
header.in_pins.resize(ysfx_max_channels); | |||
if (header.out_pins.size() > ysfx_max_channels) | |||
header.out_pins.resize(ysfx_max_channels); | |||
} | |||
bool ysfx_parse_slider(const char *line, ysfx_slider_t &slider) | |||
{ | |||
// NOTE this parser is intentionally very permissive, | |||
// in order to match the reference behavior | |||
slider = ysfx_slider_t{}; | |||
#define PARSE_FAIL do { \ | |||
/*fprintf(stderr, "parse error (line %d): `%s`\n", __LINE__, line);*/ \ | |||
return false; \ | |||
} while (0) | |||
const char *cur = line; | |||
for (const char *p = "slider"; *p; ++p) { | |||
if (*cur++ != *p) | |||
PARSE_FAIL; | |||
} | |||
// parse ID (1-index) | |||
unsigned long id = strtoul(cur, (char **)&cur, 10); | |||
if (id < 1 || id > ysfx_max_sliders) | |||
PARSE_FAIL; | |||
slider.id = (uint32_t)--id; | |||
// semicolon | |||
if (*cur++ != ':') | |||
PARSE_FAIL; | |||
// search if there is an '=' sign prior to any '<' or ',' | |||
// if there is, it's a custom variable | |||
{ | |||
const char *pos = cur; | |||
for (char c; pos && (c = *pos) && c != '='; ) | |||
pos = (c == '<' || c == ',') ? nullptr : (pos + 1); | |||
if (pos && *pos) { | |||
slider.var.assign(cur, pos); | |||
cur = pos + 1; | |||
} | |||
else | |||
slider.var = "slider" + std::to_string(id + 1); | |||
} | |||
// a regular slider | |||
if (*cur != '/') { | |||
slider.def = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); | |||
while (*cur && *cur != ',' && *cur != '<') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
// no range specification | |||
if (*cur == ',') | |||
++cur; | |||
// range specification | |||
else if (*cur == '<') { | |||
++cur; | |||
// min | |||
slider.min = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); | |||
while (*cur && *cur != ',' && *cur != '>') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
// max | |||
if (*cur == ',') { | |||
++cur; | |||
slider.max = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); | |||
while (*cur && *cur != ',' && *cur != '>') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
} | |||
// inc | |||
if (*cur == ',') { | |||
++cur; | |||
slider.inc = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); | |||
while (*cur && *cur != '{' && *cur != ',' && *cur != '>') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
// enumeration values | |||
if (*cur == '{') { | |||
const char *pos = ++cur; | |||
while (*cur && *cur != '}' && *cur != '>') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
slider.is_enum = true; | |||
slider.enum_names = ysfx::split_strings_noempty( | |||
std::string(pos, cur).c_str(), | |||
[](char c) -> bool { return c == ','; }); | |||
for (std::string &name : slider.enum_names) | |||
name = ysfx::trim(name.c_str(), &ysfx::ascii_isspace); | |||
} | |||
} | |||
while (*cur && *cur != '>') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
++cur; | |||
} | |||
else | |||
PARSE_FAIL; | |||
// NOTE: skip ',' and whitespace. not sure why, it's how it is | |||
while (*cur && (*cur == ',' || ysfx::ascii_isspace(*cur))) ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
} | |||
// a path slider | |||
else | |||
{ | |||
const char *pos = cur; | |||
while (*cur && *cur != ':') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
slider.path.assign(pos, cur); | |||
++cur; | |||
slider.def = (ysfx_real)ysfx::dot_strtod(cur, (char **)&cur); | |||
slider.inc = 1; | |||
slider.is_enum = true; | |||
while (*cur && *cur != ':') ++cur; | |||
if (!*cur) PARSE_FAIL; | |||
++cur; | |||
} | |||
// description and visibility | |||
while (ysfx::ascii_isspace(*cur)) | |||
++cur; | |||
slider.initially_visible = true; | |||
if (*cur == '-') { | |||
++cur; | |||
slider.initially_visible = false; | |||
} | |||
slider.desc = ysfx::trim(cur, &ysfx::ascii_isspace); | |||
if (slider.desc.empty()) | |||
PARSE_FAIL; | |||
// | |||
#undef PARSE_FAIL | |||
return true; | |||
} | |||
bool ysfx_parse_filename(const char *line, ysfx_parsed_filename_t &filename) | |||
{ | |||
filename = ysfx_parsed_filename_t{}; | |||
const char *cur = line; | |||
for (const char *p = "filename:"; *p; ++p) { | |||
if (*cur++ != *p) | |||
return false; | |||
} | |||
int64_t index = (int64_t)ysfx::dot_strtod(cur, (char **)&cur); | |||
if (index < 0 || index > ~(uint32_t)0) | |||
return false; | |||
while (*cur && *cur != ',') ++cur; | |||
if (!*cur) return false;; | |||
++cur; | |||
filename.index = (uint32_t)index; | |||
filename.filename.assign(cur); | |||
return true; | |||
} |
@@ -0,0 +1,101 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#include "ysfx_reader.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include <string> | |||
#include <vector> | |||
#include <cstdint> | |||
struct ysfx_section_t; | |||
struct ysfx_toplevel_t; | |||
struct ysfx_slider_t; | |||
struct ysfx_header_t; | |||
using ysfx_section_u = std::unique_ptr<ysfx_section_t>; | |||
using ysfx_toplevel_u = std::unique_ptr<ysfx_toplevel_t>; | |||
using ysfx_slider_u = std::unique_ptr<ysfx_slider_t>; | |||
using ysfx_header_u = std::unique_ptr<ysfx_header_t>; | |||
struct ysfx_section_t { | |||
uint32_t line_offset = 0; | |||
std::string text; | |||
}; | |||
struct ysfx_toplevel_t { | |||
ysfx_section_u header; | |||
ysfx_section_u init; | |||
ysfx_section_u slider; | |||
ysfx_section_u block; | |||
ysfx_section_u sample; | |||
ysfx_section_u serialize; | |||
ysfx_section_u gfx; | |||
uint32_t gfx_w = 0; | |||
uint32_t gfx_h = 0; | |||
}; | |||
struct ysfx_parse_error { | |||
uint32_t line = 0; | |||
std::string message; | |||
explicit operator bool() { return !message.empty(); } | |||
}; | |||
struct ysfx_slider_t { | |||
uint32_t id = 0; | |||
bool exists = false; | |||
ysfx_real def = 0; | |||
ysfx_real min = 0; | |||
ysfx_real max = 0; | |||
ysfx_real inc = 0; | |||
std::string var; | |||
std::string path; | |||
bool is_enum = false; | |||
ysfx::string_list enum_names; | |||
std::string desc; | |||
bool initially_visible = false; | |||
}; | |||
struct ysfx_options_t { | |||
std::string gmem; | |||
uint32_t maxmem = 0; | |||
bool want_all_kb = false; | |||
bool no_meter = false; | |||
}; | |||
struct ysfx_header_t { | |||
std::string desc; | |||
std::string author; | |||
ysfx::string_list tags; | |||
ysfx::string_list imports; | |||
ysfx::string_list in_pins; | |||
ysfx::string_list out_pins; | |||
bool explicit_pins = false; | |||
ysfx::string_list filenames; | |||
ysfx_options_t options; | |||
ysfx_slider_t sliders[ysfx_max_sliders]; | |||
}; | |||
struct ysfx_parsed_filename_t { | |||
uint32_t index; | |||
std::string filename; | |||
}; | |||
bool ysfx_parse_toplevel(ysfx::text_reader &reader, ysfx_toplevel_t &toplevel, ysfx_parse_error *error); | |||
bool ysfx_parse_slider(const char *line, ysfx_slider_t &slider); | |||
bool ysfx_parse_filename(const char *line, ysfx_parsed_filename_t &filename); | |||
void ysfx_parse_header(ysfx_section_t *section, ysfx_header_t &header); |
@@ -0,0 +1,171 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx.h" | |||
#include "ysfx_preset.hpp" | |||
#include "ysfx_utils.hpp" | |||
#include <vector> | |||
#include <string> | |||
#include <cstring> | |||
#include "WDL/lineparse.h" | |||
static void ysfx_preset_clear(ysfx_preset_t *preset); | |||
static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text); | |||
static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data); | |||
ysfx_bank_t *ysfx_load_bank(const char *path) | |||
{ | |||
ysfx::FILE_u stream{fopen(path, "rb")}; | |||
if (!stream) | |||
return nullptr; | |||
std::string input; | |||
constexpr uint32_t max_input = 1u << 24; | |||
input.reserve(1u << 16); | |||
for (int ch; input.size() < max_input && (ch = fgetc(stream.get())) != EOF; ) { | |||
ch = (ch == '\r' || ch == '\n') ? ' ' : ch; | |||
input.push_back((unsigned char)ch); | |||
} | |||
if (ferror(stream.get())) | |||
return nullptr; | |||
stream.reset(); | |||
return ysfx_load_bank_from_rpl_text(input); | |||
} | |||
void ysfx_bank_free(ysfx_bank_t *bank) | |||
{ | |||
if (!bank) | |||
return; | |||
delete[] bank->name; | |||
if (ysfx_preset_t *presets = bank->presets) { | |||
uint32_t count = bank->preset_count; | |||
for (uint32_t i = 0; i < count; ++i) | |||
ysfx_preset_clear(&presets[i]); | |||
delete[] presets; | |||
} | |||
delete bank; | |||
} | |||
static void ysfx_preset_clear(ysfx_preset_t *preset) | |||
{ | |||
delete[] preset->name; | |||
preset->name = nullptr; | |||
ysfx_state_free(preset->state); | |||
preset->state = nullptr; | |||
} | |||
static ysfx_bank_t *ysfx_load_bank_from_rpl_text(const std::string &text) | |||
{ | |||
LineParser parser; | |||
if (parser.parse(text.c_str()) < 0) | |||
return nullptr; | |||
/// | |||
std::vector<ysfx_preset_t> preset_list; | |||
preset_list.reserve(256); | |||
auto list_cleanup = ysfx::defer([&preset_list]() { | |||
for (ysfx_preset_t &pst : preset_list) | |||
ysfx_preset_clear(&pst); | |||
}); | |||
/// | |||
int ntok = parser.getnumtokens(); | |||
int itok = 0; | |||
if (strcmp("<REAPER_PRESET_LIBRARY", parser.gettoken_str(itok++)) != 0) | |||
return nullptr; | |||
const char *bank_name = parser.gettoken_str(itok++); | |||
std::string base64; | |||
base64.reserve(1024); | |||
while (itok < ntok) { | |||
if (strcmp("<PRESET", parser.gettoken_str(itok++)) == 0) { | |||
const char *preset_name = parser.gettoken_str(itok++); | |||
base64.clear(); | |||
for (const char *part; itok < ntok && | |||
strcmp(">", (part = parser.gettoken_str(itok++))) != 0; ) | |||
base64.append(part); | |||
preset_list.emplace_back(); | |||
ysfx_preset_t &preset = preset_list.back(); | |||
ysfx_parse_preset_from_rpl_blob( | |||
&preset, preset_name, | |||
ysfx::decode_base64(base64.data(), base64.size())); | |||
} | |||
} | |||
/// | |||
ysfx_bank_u bank{new ysfx_bank_t{}}; | |||
bank->name = ysfx::strdup_using_new(bank_name); | |||
bank->presets = new ysfx_preset_t[(uint32_t)preset_list.size()]{}; | |||
bank->preset_count = (uint32_t)preset_list.size(); | |||
for (uint32_t i = (uint32_t)preset_list.size(); i-- > 0; ) { | |||
bank->presets[i] = preset_list[i]; | |||
preset_list.pop_back(); | |||
} | |||
return bank.release(); | |||
} | |||
static void ysfx_parse_preset_from_rpl_blob(ysfx_preset_t *preset, const char *name, const std::vector<uint8_t> &data) | |||
{ | |||
ysfx_state_t state{}; | |||
std::vector<ysfx_state_slider_t> sliders; | |||
size_t len = data.size(); | |||
size_t pos = 0; | |||
while (pos < len && data[pos] != 0) ++pos; | |||
if (pos++ < len) { | |||
state.data = const_cast<uint8_t *>(&data[pos]); | |||
state.data_size = len - pos; | |||
LineParser parser; | |||
if (parser.parse((const char *)data.data()) >= 0) { | |||
sliders.reserve(ysfx_max_sliders); | |||
for (uint32_t i = 0; i < 64; ++i) { | |||
int success = false; | |||
ysfx_state_slider_t slider{}; | |||
slider.index = i; | |||
slider.value = (ysfx_real)parser.gettoken_float(i, &success); | |||
if (success) | |||
sliders.push_back(slider); | |||
} | |||
state.sliders = sliders.data(); | |||
state.slider_count = (uint32_t)sliders.size(); | |||
} | |||
} | |||
preset->name = ysfx::strdup_using_new(name); | |||
preset->state = ysfx_state_dup(&state); | |||
} |
@@ -0,0 +1,18 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once |
@@ -0,0 +1,98 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_reader.hpp" | |||
namespace ysfx { | |||
//------------------------------------------------------------------------------ | |||
bool text_reader::read_next_line(std::string &line) | |||
{ | |||
line.clear(); | |||
char next = read_next_char(); | |||
if (next == '\0') | |||
return false; | |||
while (next != '\0' && next != '\r' && next != '\n') { | |||
line.push_back(next); | |||
next = read_next_char(); | |||
} | |||
if (next == '\r') { | |||
next = peek_next_char(); | |||
if (next == '\n') | |||
read_next_char(); | |||
} | |||
return true; | |||
} | |||
//------------------------------------------------------------------------------ | |||
char string_text_reader::read_next_char() | |||
{ | |||
const char *ptr = m_char_ptr; | |||
if (!ptr || *ptr == '\0') | |||
return '\0'; | |||
char next = *ptr; | |||
m_char_ptr = ptr + 1; | |||
return next; | |||
} | |||
char string_text_reader::peek_next_char() | |||
{ | |||
const char *ptr = m_char_ptr; | |||
if (!ptr) | |||
return '\0'; | |||
return *ptr; | |||
} | |||
//------------------------------------------------------------------------------ | |||
char stdio_text_reader::read_next_char() | |||
{ | |||
FILE *stream = m_stream; | |||
if (!stream) | |||
return '\0'; | |||
int next = fgetc(stream); | |||
if (next == EOF) | |||
return '\0'; | |||
return (unsigned char)next; | |||
} | |||
char stdio_text_reader::peek_next_char() | |||
{ | |||
FILE *stream = m_stream; | |||
if (!stream) | |||
return '\0'; | |||
int next = fgetc(stream); | |||
if (next == EOF) | |||
return '\0'; | |||
ungetc(next, stream); | |||
return (unsigned char)next; | |||
} | |||
} // namespace ysfx |
@@ -0,0 +1,56 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include <string> | |||
#include <cstdio> | |||
#include <cstddef> | |||
namespace ysfx { | |||
class text_reader | |||
{ | |||
public: | |||
virtual ~text_reader() = default; | |||
virtual char read_next_char() = 0; | |||
virtual char peek_next_char() = 0; | |||
bool read_next_line(std::string &line); | |||
}; | |||
//------------------------------------------------------------------------------ | |||
class string_text_reader : public text_reader | |||
{ | |||
public: | |||
explicit string_text_reader(const char *text) : m_char_ptr(text) {} | |||
char read_next_char() override; | |||
char peek_next_char() override; | |||
private: | |||
const char *m_char_ptr = nullptr; | |||
}; | |||
//------------------------------------------------------------------------------ | |||
class stdio_text_reader : public text_reader | |||
{ | |||
public: | |||
explicit stdio_text_reader(FILE *stream) : m_stream(stream) {} | |||
char read_next_char() override; | |||
char peek_next_char() override; | |||
private: | |||
FILE *m_stream = nullptr; | |||
}; | |||
} // namespace ysfx |
@@ -0,0 +1,730 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#include "ysfx_utils.hpp" | |||
#include "base64/Base64.hpp" | |||
#include <system_error> | |||
#include <algorithm> | |||
#include <deque> | |||
#include <clocale> | |||
#include <cstring> | |||
#include <cassert> | |||
#if !defined(_WIN32) | |||
# include <sys/stat.h> | |||
# include <sys/types.h> | |||
# include <unistd.h> | |||
# include <dirent.h> | |||
# include <fcntl.h> | |||
#else | |||
# include <windows.h> | |||
# include <io.h> | |||
#endif | |||
namespace ysfx { | |||
#if !defined(_WIN32) | |||
static_assert(sizeof(off_t) == 8, "64-bit large file support is not enabled"); | |||
#endif | |||
FILE *fopen_utf8(const char *path, const char *mode) | |||
{ | |||
#if defined(_WIN32) | |||
return _wfopen(widen(path).c_str(), widen(mode).c_str()); | |||
#else | |||
return fopen(path, mode); | |||
#endif | |||
} | |||
int64_t fseek_lfs(FILE *stream, int64_t off, int whence) | |||
{ | |||
#if defined(_WIN32) | |||
return _fseeki64(stream, off, whence); | |||
#else | |||
return fseeko(stream, off, whence); | |||
#endif | |||
} | |||
int64_t ftell_lfs(FILE *stream) | |||
{ | |||
#if defined(_WIN32) | |||
return _ftelli64(stream); | |||
#else | |||
return ftello(stream); | |||
#endif | |||
} | |||
//------------------------------------------------------------------------------ | |||
namespace { | |||
struct scoped_c_locale | |||
{ | |||
scoped_c_locale(int lc, const char *name); | |||
~scoped_c_locale(); | |||
c_locale_t m_loc{}; | |||
scoped_c_locale(const scoped_c_locale &) = delete; | |||
scoped_c_locale &operator=(const scoped_c_locale &) = delete; | |||
}; | |||
scoped_c_locale::scoped_c_locale(int lc, const char *name) | |||
{ | |||
#if defined(_WIN32) | |||
m_loc = _create_locale(lc, name); | |||
#else | |||
switch (lc) { | |||
case LC_ALL: | |||
m_loc = newlocale(LC_ALL_MASK, name, c_locale_t{}); | |||
break; | |||
case LC_CTYPE: | |||
m_loc = newlocale(LC_CTYPE_MASK, name, c_locale_t{}); | |||
break; | |||
case LC_COLLATE: | |||
m_loc = newlocale(LC_COLLATE_MASK, name, c_locale_t{}); | |||
break; | |||
case LC_MONETARY: | |||
m_loc = newlocale(LC_MONETARY_MASK, name, c_locale_t{}); | |||
break; | |||
case LC_NUMERIC: | |||
m_loc = newlocale(LC_NUMERIC_MASK, name, c_locale_t{}); | |||
break; | |||
case LC_TIME: | |||
m_loc = newlocale(LC_TIME_MASK, name, c_locale_t{}); | |||
break; | |||
case LC_MESSAGES: | |||
m_loc = newlocale(LC_MESSAGES_MASK, name, c_locale_t{}); | |||
break; | |||
default: | |||
errno = EINVAL; | |||
break; | |||
} | |||
#endif | |||
if (!m_loc) | |||
throw std::system_error(errno, std::generic_category()); | |||
} | |||
scoped_c_locale::~scoped_c_locale() | |||
{ | |||
if (!m_loc) return; | |||
#if !defined(_WIN32) | |||
freelocale(m_loc); | |||
#else | |||
_free_locale(m_loc); | |||
#endif | |||
} | |||
#if !defined(_WIN32) | |||
struct scoped_posix_uselocale { | |||
explicit scoped_posix_uselocale(c_locale_t loc); | |||
~scoped_posix_uselocale(); | |||
c_locale_t m_loc{}; | |||
c_locale_t m_old{}; | |||
scoped_posix_uselocale(const scoped_posix_uselocale &) = delete; | |||
scoped_posix_uselocale &operator=(const scoped_posix_uselocale &) = delete; | |||
}; | |||
scoped_posix_uselocale::scoped_posix_uselocale(c_locale_t loc) | |||
{ | |||
if (loc) | |||
{ | |||
m_loc = loc; | |||
m_old = uselocale(loc); | |||
} | |||
} | |||
scoped_posix_uselocale::~scoped_posix_uselocale() | |||
{ | |||
if (m_loc) | |||
uselocale(m_old); | |||
} | |||
#endif | |||
} // namespace | |||
//------------------------------------------------------------------------------ | |||
c_locale_t c_numeric_locale() | |||
{ | |||
static scoped_c_locale loc(LC_NUMERIC, "C"); | |||
return loc.m_loc; | |||
} | |||
//------------------------------------------------------------------------------ | |||
double c_atof(const char *text, c_locale_t loc) | |||
{ | |||
#if defined(_WIN32) | |||
return _atof_l(text, loc); | |||
#else | |||
scoped_posix_uselocale use(loc); | |||
return atof(text); | |||
#endif | |||
} | |||
double c_strtod(const char *text, char **endp, c_locale_t loc) | |||
{ | |||
#if defined(_WIN32) | |||
return _strtod_l(text, endp, loc); | |||
#else | |||
scoped_posix_uselocale use(loc); | |||
return strtod(text, endp); | |||
#endif | |||
} | |||
double dot_atof(const char *text) | |||
{ | |||
return c_atof(text, c_numeric_locale()); | |||
} | |||
double dot_strtod(const char *text, char **endp) | |||
{ | |||
return c_strtod(text, endp, c_numeric_locale()); | |||
} | |||
bool ascii_isspace(char c) | |||
{ | |||
switch (c) { | |||
case ' ': case '\f': case '\n': case '\r': case '\t': case '\v': | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
bool ascii_isalpha(char c) | |||
{ | |||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); | |||
} | |||
char ascii_tolower(char c) | |||
{ | |||
return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; | |||
} | |||
char ascii_toupper(char c) | |||
{ | |||
return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; | |||
} | |||
int ascii_casecmp(const char *a, const char *b) | |||
{ | |||
for (char ca, cb; (ca = *a++) | (cb = *b++); ) { | |||
ca = ascii_tolower(ca); | |||
cb = ascii_tolower(cb); | |||
if (ca < cb) return -1; | |||
if (ca > cb) return +1; | |||
} | |||
return 0; | |||
} | |||
uint32_t latin1_toupper(uint32_t c) | |||
{ | |||
if (c >= 'a' && c <= 'z') | |||
return c - 'a' + 'A'; | |||
if ((c >= 0xe0 && c <= 0xf6) || (c >= 0xf8 && c <= 0xfe)) | |||
return c - 0x20; | |||
return c; | |||
} | |||
uint32_t latin1_tolower(uint32_t c) | |||
{ | |||
if (c >= 'A' && c <= 'Z') | |||
return c - 'A' + 'a'; | |||
if ((c >= 0xc0 && c <= 0xd6) || (c >= 0xd8 && c <= 0xde)) | |||
return c + 0x20; | |||
return c; | |||
} | |||
char *strdup_using_new(const char *src) | |||
{ | |||
size_t len = strlen(src); | |||
char *dst = new char[len + 1]; | |||
memcpy(dst, src, len + 1); | |||
return dst; | |||
} | |||
string_list split_strings_noempty(const char *input, bool(*pred)(char)) | |||
{ | |||
string_list list; | |||
if (input) { | |||
std::string acc; | |||
acc.reserve(256); | |||
for (char c; (c = *input++) != '\0'; ) { | |||
if (!pred(c)) | |||
acc.push_back(c); | |||
else { | |||
if (!acc.empty()) { | |||
list.push_back(acc); | |||
acc.clear(); | |||
} | |||
} | |||
} | |||
if (!acc.empty()) | |||
list.push_back(acc); | |||
} | |||
return list; | |||
} | |||
std::string trim(const char *input, bool(*pred)(char)) | |||
{ | |||
const char *start = input; | |||
while (*start != '\0' && pred(*start)) | |||
++start; | |||
const char *end = start + strlen(start); | |||
while (end > start && pred(*(end - 1))) | |||
--end; | |||
return std::string(start, end); | |||
} | |||
//------------------------------------------------------------------------------ | |||
void pack_u32le(uint32_t value, uint8_t data[4]) | |||
{ | |||
data[0] = value & 0xff; | |||
data[1] = (value >> 8) & 0xff; | |||
data[2] = (value >> 16) & 0xff; | |||
data[3] = value >> 24; | |||
} | |||
void pack_f32le(float value, uint8_t data[4]) | |||
{ | |||
uint32_t u; | |||
memcpy(&u, &value, 4); | |||
pack_u32le(u, data); | |||
} | |||
uint32_t unpack_u32le(const uint8_t data[4]) | |||
{ | |||
return data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); | |||
} | |||
float unpack_f32le(const uint8_t data[4]) | |||
{ | |||
float value; | |||
uint32_t u = unpack_u32le(data); | |||
memcpy(&value, &u, 4); | |||
return value; | |||
} | |||
//------------------------------------------------------------------------------ | |||
std::vector<uint8_t> decode_base64(const char *text, size_t len) | |||
{ | |||
return d_getChunkFromBase64String(text, len); | |||
} | |||
//------------------------------------------------------------------------------ | |||
bool get_file_uid(const char *path, file_uid &uid) | |||
{ | |||
#ifdef _WIN32 | |||
HANDLE handle = CreateFileW(widen(path).c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); | |||
if (handle == INVALID_HANDLE_VALUE) | |||
return false; | |||
bool success = get_handle_file_uid((void *)handle, uid); | |||
CloseHandle(handle); | |||
return success; | |||
#else | |||
int fd = open(path, O_RDONLY); | |||
if (fd == -1) | |||
return false; | |||
bool success = get_descriptor_file_uid(fd, uid); | |||
close(fd); | |||
return success; | |||
#endif | |||
} | |||
bool get_stream_file_uid(FILE *stream, file_uid &uid) | |||
{ | |||
#if !defined(_WIN32) | |||
int fd = fileno(stream); | |||
if (fd == -1) | |||
return false; | |||
#else | |||
int fd = _fileno(stream); | |||
if (fd == -1) | |||
return false; | |||
#endif | |||
return get_descriptor_file_uid(fd, uid); | |||
} | |||
bool get_descriptor_file_uid(int fd, file_uid &uid) | |||
{ | |||
#if !defined(_WIN32) | |||
struct stat st; | |||
if (fstat(fd, &st) != 0) | |||
return false; | |||
uid.first = (uint64_t)st.st_dev; | |||
uid.second = (uint64_t)st.st_ino; | |||
return true; | |||
#else | |||
HANDLE handle = (HANDLE)_get_osfhandle(fd); | |||
if (handle == INVALID_HANDLE_VALUE) | |||
return false; | |||
return get_handle_file_uid((void *)handle, uid); | |||
#endif | |||
} | |||
#if defined(_WIN32) | |||
bool get_handle_file_uid(void *handle, file_uid &uid) | |||
{ | |||
BY_HANDLE_FILE_INFORMATION info; | |||
if (!GetFileInformationByHandle((HANDLE)handle, &info)) | |||
return false; | |||
uid.first = info.dwVolumeSerialNumber; | |||
uid.second = (uint64_t)info.nFileIndexLow | ((uint64_t)info.nFileIndexHigh << 32); | |||
return true; | |||
} | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
bool is_path_separator(char ch) | |||
{ | |||
#if !defined(_WIN32) | |||
return ch == '/'; | |||
#else | |||
return ch == '/' || ch == '\\'; | |||
#endif | |||
} | |||
split_path_t split_path(const char *path) | |||
{ | |||
split_path_t sp; | |||
#if !defined(_WIN32) | |||
size_t npos = ~(size_t)0; | |||
size_t pos = npos; | |||
for (size_t i = 0; path[i] != '\0'; ++i) { | |||
if (is_path_separator(path[i])) | |||
pos = i; | |||
} | |||
if (pos == npos) | |||
sp.file.assign(path); | |||
else { | |||
sp.dir.assign(path, pos + 1); | |||
sp.file.assign(path + pos + 1); | |||
} | |||
#else | |||
std::wstring wpath = widen(path); | |||
std::unique_ptr<wchar_t[]> drive{new wchar_t[wpath.size() + 1]{}}; | |||
std::unique_ptr<wchar_t[]> dir{new wchar_t[wpath.size() + 1]{}}; | |||
std::unique_ptr<wchar_t[]> fname{new wchar_t[wpath.size() + 1]{}}; | |||
std::unique_ptr<wchar_t[]> ext{new wchar_t[wpath.size() + 1]{}}; | |||
_wsplitpath(wpath.c_str(), drive.get(), dir.get(), fname.get(), ext.get()); | |||
sp.drive = narrow(drive.get()); | |||
if (!drive[0] || dir[0] == L'/' || dir[0] == L'\\') | |||
sp.dir = narrow(dir.get()); | |||
else | |||
sp.dir = narrow(L'\\' + std::wstring(dir.get())); | |||
sp.file = narrow(std::wstring(fname.get()) + std::wstring(ext.get())); | |||
#endif | |||
return sp; | |||
} | |||
std::string path_file_name(const char *path) | |||
{ | |||
return split_path(path).file; | |||
} | |||
std::string path_directory(const char *path) | |||
{ | |||
split_path_t sp = split_path(path); | |||
return sp.dir.empty() ? std::string("./") : (sp.drive + sp.dir); | |||
} | |||
std::string path_ensure_final_separator(const char *path) | |||
{ | |||
std::string result(path); | |||
if (!result.empty() && !is_path_separator(result.back())) | |||
result.push_back('/'); | |||
return result; | |||
} | |||
bool path_has_suffix(const char *path, const char *suffix) | |||
{ | |||
if (*suffix == '.') | |||
++suffix; | |||
size_t plen = strlen(path); | |||
size_t slen = strlen(suffix); | |||
if (plen < slen + 2) | |||
return false; | |||
return path[plen - slen - 1] == '.' && | |||
ascii_casecmp(suffix, &path[plen - slen]) == 0; | |||
} | |||
bool path_is_relative(const char *path) | |||
{ | |||
#if !defined(_WIN32) | |||
return !is_path_separator(path[0]); | |||
#else | |||
return !is_path_separator(split_path(path).dir.c_str()[0]); | |||
#endif | |||
} | |||
//------------------------------------------------------------------------------ | |||
#if !defined(_WIN32) | |||
bool exists(const char *path) | |||
{ | |||
return access(path, F_OK) == 0; | |||
} | |||
string_list list_directory(const char *path) | |||
{ | |||
string_list list; | |||
DIR *dir = opendir(path); | |||
if (!dir) | |||
return list; | |||
auto dir_cleanup = defer([dir]() { closedir(dir); }); | |||
list.reserve(256); | |||
std::string pathbuf; | |||
pathbuf.reserve(1024); | |||
while (dirent *ent = readdir(dir)) { | |||
const char *name = ent->d_name; | |||
if (!strcmp(name, ".") || !strcmp(name, "..")) | |||
continue; | |||
pathbuf.assign(name); | |||
if (ent->d_type == DT_DIR) | |||
pathbuf.push_back('/'); | |||
list.push_back(pathbuf); | |||
} | |||
std::sort(list.begin(), list.end()); | |||
return list; | |||
} | |||
// void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data); | |||
// NOTE: implemented in separate file `ysfx_utils_fts.cpp` | |||
#else | |||
bool exists(const char *path) | |||
{ | |||
return _waccess(widen(path).c_str(), 0) == 0; | |||
} | |||
string_list list_directory(const char *path) | |||
{ | |||
string_list list; | |||
std::wstring pattern = widen(path) + L"\\*"; | |||
WIN32_FIND_DATAW fd; | |||
HANDLE handle = FindFirstFileW(pattern.c_str(), &fd); | |||
if (handle == INVALID_HANDLE_VALUE) | |||
return list; | |||
auto handle_cleanup = defer([handle]() { FindClose(handle); }); | |||
list.reserve(256); | |||
do { | |||
const wchar_t *name = fd.cFileName; | |||
if (!wcscmp(name, L".") || !wcscmp(name, L"..")) | |||
continue; | |||
std::string entry = narrow(name); | |||
if ((fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && !(fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) | |||
entry.push_back('/'); | |||
list.push_back(std::move(entry)); | |||
} while (FindNextFileW(handle, &fd)); | |||
std::sort(list.begin(), list.end()); | |||
return list; | |||
} | |||
void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data) | |||
{ | |||
std::deque<std::wstring> dirs; | |||
dirs.push_back(widen(path_ensure_final_separator(rootpath))); | |||
std::wstring pathbuf; | |||
pathbuf.reserve(1024); | |||
std::vector<std::wstring> entries; | |||
entries.reserve(256); | |||
while (!dirs.empty()) { | |||
std::wstring dir = std::move(dirs.front()); | |||
dirs.pop_front(); | |||
if (!visit(narrow(dir), data)) | |||
return; | |||
pathbuf.assign(dir); | |||
pathbuf.append(L"\\*"); | |||
WIN32_FIND_DATAW fd; | |||
HANDLE handle = FindFirstFileW(pathbuf.c_str(), &fd); | |||
if (handle == INVALID_HANDLE_VALUE) | |||
continue; | |||
auto handle_cleanup = defer([handle]() { FindClose(handle); }); | |||
entries.clear(); | |||
do { | |||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) | |||
continue; | |||
if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { | |||
const wchar_t *name = fd.cFileName; | |||
if (!wcscmp(name, L".") || !wcscmp(name, L"..")) | |||
continue; | |||
pathbuf.assign(dir); | |||
pathbuf.append(name); | |||
pathbuf.push_back(L'\\'); | |||
entries.push_back(pathbuf); | |||
} | |||
} while (FindNextFileW(handle, &fd)); | |||
std::sort(entries.begin(), entries.end()); | |||
for (size_t n = entries.size(); n-- > 0; ) | |||
dirs.push_front(std::move(entries[n])); | |||
} | |||
} | |||
#endif | |||
int case_resolve(const char *root_, const char *fragment, std::string &result) | |||
{ | |||
if (fragment[0] == '\0') | |||
return 0; | |||
std::string root = path_ensure_final_separator(root_); | |||
std::string pathbuf; | |||
pathbuf.reserve(1024); | |||
pathbuf.assign(root); | |||
pathbuf.append(fragment); | |||
if (exists(pathbuf.c_str())) { | |||
result = std::move(pathbuf); | |||
return 1; | |||
} | |||
struct Item { | |||
std::string root; | |||
string_list components; | |||
}; | |||
std::deque<Item> worklist; | |||
{ | |||
Item item; | |||
item.root = root; | |||
item.components = split_strings_noempty(fragment, &is_path_separator); | |||
if (item.components.empty()) | |||
return 0; | |||
for (size_t i = 0; i + 1 < item.components.size(); ++i) | |||
item.components[i].push_back('/'); | |||
if (is_path_separator(fragment[strlen(fragment) - 1])) | |||
item.components.back().push_back('/'); | |||
worklist.push_back(std::move(item)); | |||
} | |||
while (!worklist.empty()) { | |||
Item item = std::move(worklist.front()); | |||
worklist.pop_front(); | |||
for (const std::string &entry : list_directory(item.root.c_str())) { | |||
if (ascii_casecmp(entry.c_str(), item.components[0].c_str()) != 0) | |||
continue; | |||
if (item.components.size() == 1) { | |||
pathbuf.assign(item.root); | |||
pathbuf.append(entry); | |||
if (exists(pathbuf.c_str())) { | |||
result = std::move(pathbuf); | |||
return 2; | |||
} | |||
} | |||
else { | |||
assert(item.components.size() > 1); | |||
Item newitem; | |||
newitem.root = item.root + entry; | |||
newitem.components.assign(item.components.begin() + 1, item.components.end()); | |||
worklist.push_front(std::move(newitem)); | |||
} | |||
} | |||
} | |||
return 0; | |||
} | |||
//------------------------------------------------------------------------------ | |||
#if defined(_WIN32) | |||
std::wstring widen(const std::string &u8str) | |||
{ | |||
return widen(u8str.data(), u8str.size()); | |||
} | |||
std::wstring widen(const char *u8data, size_t u8len) | |||
{ | |||
if (u8len == ~(size_t)0) | |||
u8len = strlen(u8data); | |||
std::wstring wstr; | |||
int wch = MultiByteToWideChar(CP_UTF8, 0, u8data, (int)u8len, nullptr, 0); | |||
if (wch != 0) { | |||
wstr.resize((size_t)wch); | |||
MultiByteToWideChar(CP_UTF8, 0, u8data, (int)u8len, &wstr[0], wch); | |||
} | |||
return wstr; | |||
} | |||
std::string narrow(const std::wstring &wstr) | |||
{ | |||
return narrow(wstr.data(), wstr.size()); | |||
} | |||
std::string narrow(const wchar_t *wdata, size_t wlen) | |||
{ | |||
if (wlen == ~(size_t)0) | |||
wlen = wcslen(wdata); | |||
std::string u8str; | |||
int u8ch = WideCharToMultiByte(CP_UTF8, 0, wdata, (int)wlen, nullptr, 0, nullptr, nullptr); | |||
if (u8ch != 0) { | |||
u8str.resize((size_t)u8ch); | |||
WideCharToMultiByte(CP_UTF8, 0, wdata, (int)wlen, &u8str[0], u8ch, nullptr, nullptr); | |||
} | |||
return u8str; | |||
} | |||
#endif | |||
} // namespace ysfx | |||
//------------------------------------------------------------------------------ | |||
// WDL helpers | |||
// our replacement `atof` for WDL, which is unaffected by current locale | |||
extern "C" double ysfx_wdl_atof(const char *text) | |||
{ | |||
return ysfx::dot_atof(text); | |||
} |
@@ -0,0 +1,185 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#pragma once | |||
#include "ysfx.h" | |||
#include <string> | |||
#include <vector> | |||
#include <mutex> | |||
#include <cstdio> | |||
#include <cstdint> | |||
#include <clocale> | |||
#if defined(__APPLE__) | |||
# include <xlocale.h> | |||
#endif | |||
#if !defined(_WIN32) | |||
# include <alloca.h> | |||
#else | |||
# include <malloc.h> | |||
#endif | |||
#if defined(YSFX_NO_STANDARD_MUTEX) | |||
# include "WDL/mutex.h" | |||
#endif | |||
namespace ysfx { | |||
YSFX_DEFINE_AUTO_PTR(FILE_u, FILE, fclose); | |||
FILE *fopen_utf8(const char *path, const char *mode); | |||
int64_t fseek_lfs(FILE *stream, int64_t off, int whence); | |||
int64_t ftell_lfs(FILE *stream); | |||
//------------------------------------------------------------------------------ | |||
#if !defined(_WIN32) | |||
using c_locale_t = locale_t; | |||
#else | |||
using c_locale_t = _locale_t; | |||
#endif | |||
c_locale_t c_numeric_locale(); | |||
//------------------------------------------------------------------------------ | |||
#if !defined(_WIN32) | |||
# define ysfx_alloca(n) alloca((n)) | |||
#else | |||
# define ysfx_alloca(n) _malloca((n)) | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
#if !defined(YSFX_NO_STANDARD_MUTEX) | |||
using mutex = std::mutex; | |||
#else | |||
class mutex | |||
{ | |||
public: | |||
void lock() { m_mutex.Enter(); } | |||
void unlock() { m_mutex.Leave(); } | |||
private: | |||
WDL_Mutex m_mutex; | |||
}; | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
using string_list = std::vector<std::string>; | |||
double c_atof(const char *text, c_locale_t loc); | |||
double c_strtod(const char *text, char **endp, c_locale_t loc); | |||
double dot_atof(const char *text); | |||
double dot_strtod(const char *text, char **endp); | |||
bool ascii_isspace(char c); | |||
bool ascii_isalpha(char c); | |||
char ascii_tolower(char c); | |||
char ascii_toupper(char c); | |||
int ascii_casecmp(const char *a, const char *b); | |||
uint32_t latin1_toupper(uint32_t c); | |||
uint32_t latin1_tolower(uint32_t c); | |||
char *strdup_using_new(const char *src); | |||
string_list split_strings_noempty(const char *input, bool(*pred)(char)); | |||
std::string trim(const char *input, bool(*pred)(char)); | |||
//------------------------------------------------------------------------------ | |||
void pack_u32le(uint32_t value, uint8_t data[4]); | |||
void pack_f32le(float value, uint8_t data[4]); | |||
uint32_t unpack_u32le(const uint8_t data[4]); | |||
float unpack_f32le(const uint8_t data[4]); | |||
//------------------------------------------------------------------------------ | |||
std::vector<uint8_t> decode_base64(const char *text, size_t len = ~(size_t)0); | |||
//------------------------------------------------------------------------------ | |||
using file_uid = std::pair<uint64_t, uint64_t>; | |||
bool get_file_uid(const char *path, file_uid &uid); | |||
bool get_stream_file_uid(FILE *stream, file_uid &uid); | |||
bool get_descriptor_file_uid(int fd, file_uid &uid); | |||
#if defined(_WIN32) | |||
bool get_handle_file_uid(void *handle, file_uid &uid); | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
struct split_path_t { | |||
std::string drive; | |||
std::string dir; | |||
std::string file; | |||
}; | |||
// check if the character is a path separator | |||
bool is_path_separator(char ch); | |||
// break down a path into individual components | |||
split_path_t split_path(const char *path); | |||
// get the file name part (all after the final '/' separator) | |||
std::string path_file_name(const char *path); | |||
// get the directory part (all up to the '/' separator, inclusive) | |||
std::string path_directory(const char *path); | |||
// add the final '/' separator if absent; if empty, does nothing | |||
std::string path_ensure_final_separator(const char *path); | |||
// compare the tail of the path with the suffix, case-insensitively | |||
bool path_has_suffix(const char *path, const char *suffix); | |||
// check whether the path is relative | |||
bool path_is_relative(const char *path); | |||
//------------------------------------------------------------------------------ | |||
// check whether a file exists on disk | |||
bool exists(const char *path); | |||
// list the elements of a directory; directories are distinguished with a final '/' | |||
string_list list_directory(const char *path); | |||
// visit the root and subdirectories in depth-first order | |||
void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data); | |||
// resolve a path which matches root/fragment, where fragment is case-insensitive (0=failed, 1=exact, 2=inexact) | |||
int case_resolve(const char *root, const char *fragment, std::string &result); | |||
//------------------------------------------------------------------------------ | |||
#if defined(_WIN32) | |||
std::wstring widen(const std::string &u8str); | |||
std::wstring widen(const char *u8data, size_t u8len = ~(size_t)0); | |||
std::string narrow(const std::wstring &wstr); | |||
std::string narrow(const wchar_t *wdata, size_t wlen = ~(size_t)0); | |||
#endif | |||
//------------------------------------------------------------------------------ | |||
template <class F> | |||
class scope_guard { | |||
public: | |||
explicit scope_guard(F &&f) : f(std::forward<F>(f)), a(true) {} | |||
scope_guard(scope_guard &&o) : f(std::move(o.f)), a(o.a) { o.a = false; } | |||
~scope_guard() { if (a) f(); } | |||
void disarm() { a = false; } | |||
private: | |||
F f; | |||
bool a; | |||
scope_guard(const scope_guard &) = delete; | |||
scope_guard &operator=(const scope_guard &) = delete; | |||
}; | |||
template <class F> scope_guard<F> defer(F &&f) | |||
{ | |||
return scope_guard<F>(std::forward<F>(f)); | |||
} | |||
} // namespace ysfx |
@@ -0,0 +1,60 @@ | |||
// Copyright 2021 Jean Pierre Cimalando | |||
// | |||
// Licensed under the Apache License, Version 2.0 (the "License"); | |||
// you may not use this file except in compliance with the License. | |||
// You may obtain a copy of the License at | |||
// | |||
// http://www.apache.org/licenses/LICENSE-2.0 | |||
// | |||
// Unless required by applicable law or agreed to in writing, software | |||
// distributed under the License is distributed on an "AS IS" BASIS, | |||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
// | |||
// SPDX-License-Identifier: Apache-2.0 | |||
// | |||
#if !defined(_WIN32) | |||
// fts lacks large file support in glibc < 2.23 | |||
#if defined(YSFX_FTS_LACKS_LFS_SUPPORT) | |||
# undef _FILE_OFFSET_BITS | |||
#endif | |||
#include "ysfx_utils.hpp" | |||
#include <fts.h> | |||
#include <string> | |||
#include <cstring> | |||
namespace ysfx { | |||
void visit_directories(const char *rootpath, bool (*visit)(const std::string &, void *), void *data) | |||
{ | |||
char *argv[] = {(char *)rootpath, nullptr}; | |||
auto compar = [](const FTSENT **a, const FTSENT **b) -> int { | |||
return strcmp((*a)->fts_name, (*b)->fts_name); | |||
}; | |||
FTS *fts = fts_open(argv, FTS_NOCHDIR|FTS_PHYSICAL, +compar); | |||
if (!fts) | |||
return; | |||
auto fts_cleanup = defer([fts]() { fts_close(fts); }); | |||
std::string pathbuf; | |||
pathbuf.reserve(1024); | |||
while (FTSENT *ent = fts_read(fts)) { | |||
if (ent->fts_info == FTS_D) { | |||
pathbuf.assign(ent->fts_path); | |||
pathbuf.push_back('/'); | |||
if (!visit(pathbuf, data)) | |||
return; | |||
} | |||
} | |||
} | |||
} // namespace ysfx | |||
#endif // !defined(_WIN32) |
@@ -0,0 +1,19 @@ | |||
Copyright (C) 2005 and later Cockos Incorporated | |||
Portions copyright other contributors, see each source file for more information | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. |
@@ -0,0 +1,482 @@ | |||
#ifndef _WDL_ASSOCARRAY_H_ | |||
#define _WDL_ASSOCARRAY_H_ | |||
#include "heapbuf.h" | |||
#include "mergesort.h" | |||
// on all of these, if valdispose is set, the array will dispose of values as needed. | |||
// if keydup/keydispose are set, copies of (any) key data will be made/destroyed as necessary | |||
// WDL_AssocArrayImpl can be used on its own, and can contain structs for keys or values | |||
template <class KEY, class VAL> class WDL_AssocArrayImpl | |||
{ | |||
WDL_AssocArrayImpl(const WDL_AssocArrayImpl &cp) { CopyContents(cp); } | |||
WDL_AssocArrayImpl &operator=(const WDL_AssocArrayImpl &cp) { CopyContents(cp); return *this; } | |||
public: | |||
explicit WDL_AssocArrayImpl(int (*keycmp)(KEY *k1, KEY *k2), KEY (*keydup)(KEY)=0, void (*keydispose)(KEY)=0, void (*valdispose)(VAL)=0) | |||
{ | |||
m_keycmp = keycmp; | |||
m_keydup = keydup; | |||
m_keydispose = keydispose; | |||
m_valdispose = valdispose; | |||
} | |||
~WDL_AssocArrayImpl() | |||
{ | |||
DeleteAll(); | |||
} | |||
VAL* GetPtr(KEY key, KEY *keyPtrOut=NULL) const | |||
{ | |||
bool ismatch = false; | |||
int i = LowerBound(key, &ismatch); | |||
if (ismatch) | |||
{ | |||
KeyVal* kv = m_data.Get()+i; | |||
if (keyPtrOut) *keyPtrOut = kv->key; | |||
return &(kv->val); | |||
} | |||
return 0; | |||
} | |||
bool Exists(KEY key) const | |||
{ | |||
bool ismatch = false; | |||
LowerBound(key, &ismatch); | |||
return ismatch; | |||
} | |||
int Insert(KEY key, VAL val) | |||
{ | |||
bool ismatch = false; | |||
int i = LowerBound(key, &ismatch); | |||
if (ismatch) | |||
{ | |||
KeyVal* kv = m_data.Get()+i; | |||
if (m_valdispose) m_valdispose(kv->val); | |||
kv->val = val; | |||
} | |||
else | |||
{ | |||
KeyVal* kv = m_data.Resize(m_data.GetSize()+1)+i; | |||
memmove(kv+1, kv, (m_data.GetSize()-i-1)*(unsigned int)sizeof(KeyVal)); | |||
if (m_keydup) key = m_keydup(key); | |||
kv->key = key; | |||
kv->val = val; | |||
} | |||
return i; | |||
} | |||
void Delete(KEY key) | |||
{ | |||
bool ismatch = false; | |||
int i = LowerBound(key, &ismatch); | |||
if (ismatch) | |||
{ | |||
KeyVal* kv = m_data.Get()+i; | |||
if (m_keydispose) m_keydispose(kv->key); | |||
if (m_valdispose) m_valdispose(kv->val); | |||
m_data.Delete(i); | |||
} | |||
} | |||
void DeleteByIndex(int idx) | |||
{ | |||
if (idx >= 0 && idx < m_data.GetSize()) | |||
{ | |||
KeyVal* kv = m_data.Get()+idx; | |||
if (m_keydispose) m_keydispose(kv->key); | |||
if (m_valdispose) m_valdispose(kv->val); | |||
m_data.Delete(idx); | |||
} | |||
} | |||
void DeleteAll(bool resizedown=false) | |||
{ | |||
if (m_keydispose || m_valdispose) | |||
{ | |||
int i; | |||
for (i = 0; i < m_data.GetSize(); ++i) | |||
{ | |||
KeyVal* kv = m_data.Get()+i; | |||
if (m_keydispose) m_keydispose(kv->key); | |||
if (m_valdispose) m_valdispose(kv->val); | |||
} | |||
} | |||
m_data.Resize(0, resizedown); | |||
} | |||
int GetSize() const | |||
{ | |||
return m_data.GetSize(); | |||
} | |||
VAL* EnumeratePtr(int i, KEY* key=0) const | |||
{ | |||
if (i >= 0 && i < m_data.GetSize()) | |||
{ | |||
KeyVal* kv = m_data.Get()+i; | |||
if (key) *key = kv->key; | |||
return &(kv->val); | |||
} | |||
return 0; | |||
} | |||
KEY* ReverseLookupPtr(VAL val) const | |||
{ | |||
int i; | |||
for (i = 0; i < m_data.GetSize(); ++i) | |||
{ | |||
KeyVal* kv = m_data.Get()+i; | |||
if (kv->val == val) return &kv->key; | |||
} | |||
return 0; | |||
} | |||
void ChangeKey(KEY oldkey, KEY newkey) | |||
{ | |||
bool ismatch=false; | |||
int i=LowerBound(oldkey, &ismatch); | |||
if (ismatch) ChangeKeyByIndex(i, newkey, true); | |||
} | |||
void ChangeKeyByIndex(int idx, KEY newkey, bool needsort) | |||
{ | |||
if (idx >= 0 && idx < m_data.GetSize()) | |||
{ | |||
KeyVal* kv=m_data.Get()+idx; | |||
if (!needsort) | |||
{ | |||
if (m_keydispose) m_keydispose(kv->key); | |||
if (m_keydup) newkey=m_keydup(newkey); | |||
kv->key=newkey; | |||
} | |||
else | |||
{ | |||
VAL val=kv->val; | |||
m_data.Delete(idx); | |||
Insert(newkey, val); | |||
} | |||
} | |||
} | |||
// fast add-block mode | |||
void AddUnsorted(KEY key, VAL val) | |||
{ | |||
int i=m_data.GetSize(); | |||
KeyVal* kv = m_data.Resize(i+1)+i; | |||
if (m_keydup) key = m_keydup(key); | |||
kv->key = key; | |||
kv->val = val; | |||
} | |||
void Resort(int (*new_keycmp)(KEY *k1, KEY *k2)=NULL) | |||
{ | |||
if (new_keycmp) m_keycmp = new_keycmp; | |||
if (m_data.GetSize() > 1 && m_keycmp) | |||
{ | |||
qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal), | |||
(int(*)(const void*, const void*))m_keycmp); | |||
if (!new_keycmp) | |||
RemoveDuplicateKeys(); | |||
} | |||
} | |||
void ResortStable() | |||
{ | |||
if (m_data.GetSize() > 1 && m_keycmp) | |||
{ | |||
char *tmp=(char*)malloc(m_data.GetSize()*sizeof(KeyVal)); | |||
if (WDL_NORMALLY(tmp)) | |||
{ | |||
WDL_mergesort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal), | |||
(int(*)(const void*, const void*))m_keycmp, tmp); | |||
free(tmp); | |||
} | |||
else | |||
{ | |||
qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal), | |||
(int(*)(const void*, const void*))m_keycmp); | |||
} | |||
RemoveDuplicateKeys(); | |||
} | |||
} | |||
int LowerBound(KEY key, bool* ismatch) const | |||
{ | |||
int a = 0; | |||
int c = m_data.GetSize(); | |||
while (a != c) | |||
{ | |||
int b = (a+c)/2; | |||
KeyVal* kv=m_data.Get()+b; | |||
int cmp = m_keycmp(&key, &kv->key); | |||
if (cmp > 0) a = b+1; | |||
else if (cmp < 0) c = b; | |||
else | |||
{ | |||
*ismatch = true; | |||
return b; | |||
} | |||
} | |||
*ismatch = false; | |||
return a; | |||
} | |||
int GetIdx(KEY key) const | |||
{ | |||
bool ismatch=false; | |||
int i = LowerBound(key, &ismatch); | |||
if (ismatch) return i; | |||
return -1; | |||
} | |||
void SetGranul(int gran) | |||
{ | |||
m_data.SetGranul(gran); | |||
} | |||
void CopyContents(const WDL_AssocArrayImpl &cp) | |||
{ | |||
m_data=cp.m_data; | |||
m_keycmp = cp.m_keycmp; | |||
m_keydup = cp.m_keydup; | |||
m_keydispose = m_keydup ? cp.m_keydispose : NULL; | |||
m_valdispose = NULL; // avoid disposing of values twice, since we don't have a valdup, we can't have a fully valid copy | |||
if (m_keydup) | |||
{ | |||
int x; | |||
const int n=m_data.GetSize(); | |||
for (x=0;x<n;x++) | |||
{ | |||
KeyVal *kv=m_data.Get()+x; | |||
if (kv->key) kv->key = m_keydup(kv->key); | |||
} | |||
} | |||
} | |||
void CopyContentsAsReference(const WDL_AssocArrayImpl &cp) | |||
{ | |||
DeleteAll(true); | |||
m_keycmp = cp.m_keycmp; | |||
m_keydup = NULL; // this no longer can own any data | |||
m_keydispose = NULL; | |||
m_valdispose = NULL; | |||
m_data=cp.m_data; | |||
} | |||
// private data, but exposed in case the caller wants to manipulate at its own risk | |||
struct KeyVal | |||
{ | |||
KEY key; | |||
VAL val; | |||
}; | |||
WDL_TypedBuf<KeyVal> m_data; | |||
protected: | |||
int (*m_keycmp)(KEY *k1, KEY *k2); | |||
KEY (*m_keydup)(KEY); | |||
void (*m_keydispose)(KEY); | |||
void (*m_valdispose)(VAL); | |||
private: | |||
void RemoveDuplicateKeys() // after resorting | |||
{ | |||
const int sz = m_data.GetSize(); | |||
int cnt = 1; | |||
KeyVal *rd = m_data.Get() + 1, *wr = rd; | |||
for (int x = 1; x < sz; x ++) | |||
{ | |||
if (m_keycmp(&rd->key, &wr[-1].key)) | |||
{ | |||
if (rd != wr) *wr=*rd; | |||
wr++; | |||
cnt++; | |||
} | |||
else | |||
{ | |||
if (m_keydispose) m_keydispose(rd->key); | |||
if (m_valdispose) m_valdispose(rd->val); | |||
} | |||
rd++; | |||
} | |||
if (cnt < sz) m_data.Resize(cnt,false); | |||
} | |||
}; | |||
// WDL_AssocArray adds useful functions but cannot contain structs for keys or values | |||
template <class KEY, class VAL> class WDL_AssocArray : public WDL_AssocArrayImpl<KEY, VAL> | |||
{ | |||
public: | |||
explicit WDL_AssocArray(int (*keycmp)(KEY *k1, KEY *k2), KEY (*keydup)(KEY)=0, void (*keydispose)(KEY)=0, void (*valdispose)(VAL)=0) | |||
: WDL_AssocArrayImpl<KEY, VAL>(keycmp, keydup, keydispose, valdispose) | |||
{ | |||
} | |||
VAL Get(KEY key, VAL notfound=0) const | |||
{ | |||
VAL* p = this->GetPtr(key); | |||
if (p) return *p; | |||
return notfound; | |||
} | |||
VAL Enumerate(int i, KEY* key=0, VAL notfound=0) const | |||
{ | |||
VAL* p = this->EnumeratePtr(i, key); | |||
if (p) return *p; | |||
return notfound; | |||
} | |||
KEY ReverseLookup(VAL val, KEY notfound=0) const | |||
{ | |||
KEY* p=this->ReverseLookupPtr(val); | |||
if (p) return *p; | |||
return notfound; | |||
} | |||
}; | |||
template <class VAL> class WDL_IntKeyedArray : public WDL_AssocArray<int, VAL> | |||
{ | |||
public: | |||
explicit WDL_IntKeyedArray(void (*valdispose)(VAL)=0) : WDL_AssocArray<int, VAL>(cmpint, NULL, NULL, valdispose) {} | |||
~WDL_IntKeyedArray() {} | |||
private: | |||
static int cmpint(int *i1, int *i2) { return *i1-*i2; } | |||
}; | |||
template <class VAL> class WDL_IntKeyedArray2 : public WDL_AssocArrayImpl<int, VAL> | |||
{ | |||
public: | |||
explicit WDL_IntKeyedArray2(void (*valdispose)(VAL)=0) : WDL_AssocArrayImpl<int, VAL>(cmpint, NULL, NULL, valdispose) {} | |||
~WDL_IntKeyedArray2() {} | |||
private: | |||
static int cmpint(int *i1, int *i2) { return *i1-*i2; } | |||
}; | |||
template <class VAL> class WDL_StringKeyedArray : public WDL_AssocArray<const char *, VAL> | |||
{ | |||
public: | |||
explicit WDL_StringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_AssocArray<const char*, VAL>(caseSensitive?cmpstr:cmpistr, dupstr, freestr, valdispose) {} | |||
~WDL_StringKeyedArray() { } | |||
static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe... | |||
static int cmpstr(const char **s1, const char **s2) { return strcmp(*s1, *s2); } | |||
static int cmpistr(const char **a, const char **b) { return stricmp(*a,*b); } | |||
static void freestr(const char* s) { free((void*)s); } | |||
static void freecharptr(char *p) { free(p); } | |||
}; | |||
template <class VAL> class WDL_StringKeyedArray2 : public WDL_AssocArrayImpl<const char *, VAL> | |||
{ | |||
public: | |||
explicit WDL_StringKeyedArray2(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_AssocArrayImpl<const char*, VAL>(caseSensitive?cmpstr:cmpistr, dupstr, freestr, valdispose) {} | |||
~WDL_StringKeyedArray2() { } | |||
static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe... | |||
static int cmpstr(const char **s1, const char **s2) { return strcmp(*s1, *s2); } | |||
static int cmpistr(const char **a, const char **b) { return stricmp(*a,*b); } | |||
static void freestr(const char* s) { free((void*)s); } | |||
static void freecharptr(char *p) { free(p); } | |||
}; | |||
// sorts text as text, sorts anything that looks like a number as a number | |||
template <class VAL> class WDL_LogicalSortStringKeyedArray : public WDL_StringKeyedArray<VAL> | |||
{ | |||
public: | |||
explicit WDL_LogicalSortStringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=0) : WDL_StringKeyedArray<VAL>(caseSensitive, valdispose) | |||
{ | |||
WDL_StringKeyedArray<VAL>::m_keycmp = caseSensitive?cmpstr:cmpistr; // override | |||
} | |||
~WDL_LogicalSortStringKeyedArray() { } | |||
static int cmpstr(const char **a, const char **b) { return _cmpstr(*a, *b, true); } | |||
static int cmpistr(const char **a, const char **b) { return _cmpstr(*a, *b, false); } | |||
private: | |||
static int _cmpstr(const char *s1, const char *s2, bool case_sensitive) | |||
{ | |||
// this also exists as WDL_strcmp_logical in wdlcstring.h | |||
for (;;) | |||
{ | |||
if (*s1 >= '0' && *s1 <= '9' && *s2 >= '0' && *s2 <= '9') | |||
{ | |||
int lzdiff=0, len1=0, len2=0; | |||
while (*s1 == '0') { s1++; lzdiff--; } | |||
while (*s2 == '0') { s2++; lzdiff++; } | |||
while (s1[len1] >= '0' && s1[len1] <= '9') len1++; | |||
while (s2[len2] >= '0' && s2[len2] <= '9') len2++; | |||
if (len1 != len2) return len1-len2; | |||
while (len1--) | |||
{ | |||
const int d = *s1++ - *s2++; | |||
if (d) return d; | |||
} | |||
if (lzdiff) return lzdiff; | |||
} | |||
else | |||
{ | |||
char c1 = *s1++, c2 = *s2++; | |||
if (c1 != c2) | |||
{ | |||
if (case_sensitive) return c1-c2; | |||
if (c1>='a' && c1<='z') c1+='A'-'a'; | |||
if (c2>='a' && c2<='z') c2+='A'-'a'; | |||
if (c1 != c2) return c1-c2; | |||
} | |||
else if (!c1) return 0; | |||
} | |||
} | |||
} | |||
}; | |||
template <class VAL> class WDL_PtrKeyedArray : public WDL_AssocArray<INT_PTR, VAL> | |||
{ | |||
public: | |||
explicit WDL_PtrKeyedArray(void (*valdispose)(VAL)=0) : WDL_AssocArray<INT_PTR, VAL>(cmpptr, 0, 0, valdispose) {} | |||
~WDL_PtrKeyedArray() {} | |||
private: | |||
static int cmpptr(INT_PTR* a, INT_PTR* b) { const INT_PTR d = *a - *b; return d<0?-1:(d!=0); } | |||
}; | |||
#endif | |||
@@ -0,0 +1,248 @@ | |||
#ifndef _WDL_DENORMAL_H_ | |||
#define _WDL_DENORMAL_H_ | |||
#include <string.h> | |||
#include "wdltypes.h" | |||
// note: the _aggressive versions filter out anything less than around 1.0e-16 or so (approximately) to 0.0, including -0.0 (becomes 0.0) | |||
// note: new! the _aggressive versions also filter inf and NaN to 0.0 | |||
#ifdef __cplusplus | |||
#define WDL_DENORMAL_INLINE inline | |||
#elif defined(_MSC_VER) | |||
#define WDL_DENORMAL_INLINE __inline | |||
#else | |||
#ifdef WDL_STATICFUNC_UNUSED | |||
#define WDL_DENORMAL_INLINE WDL_STATICFUNC_UNUSED | |||
#else | |||
#define WDL_DENORMAL_INLINE | |||
#endif | |||
#endif | |||
static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_FLOAT_W(const float *a) { unsigned int v; memcpy(&v,a,sizeof(v)); return v; } | |||
static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_DOUBLE_HW(const double *a) { WDL_UINT64 v; memcpy(&v,(char*)a,sizeof(v)); return (unsigned int) (v>>32); } | |||
#define WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF 0x3cA00000 // 0x3B8000000 maybe instead? that's 10^-5 smaller or so | |||
#define WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF 0x25000000 | |||
// define WDL_DENORMAL_WANTS_SCOPED_FTZ, and then use a WDL_denormal_ftz_scope in addition to denormal_*(), then | |||
// if FTZ is available it will be used instead... | |||
// | |||
#ifdef WDL_DENORMAL_WANTS_SCOPED_FTZ | |||
#if defined(__SSE2__) || _M_IX86_FP >= 2 || defined(_WIN64) | |||
#define WDL_DENORMAL_FTZMODE | |||
#define WDL_DENORMAL_FTZSTATE_TYPE unsigned int | |||
#ifdef _MSC_VER | |||
#include <intrin.h> | |||
#else | |||
#include <xmmintrin.h> | |||
#endif | |||
#define wdl_denorm_mm_getcsr() _mm_getcsr() | |||
#define wdl_denorm_mm_setcsr(x) _mm_setcsr(x) | |||
#if defined(__SSE3__) | |||
#define wdl_denorm_mm_csr_mask ((1<<15)|(1<<11) | (1<<8) | (1<<6)) // FTZ, underflow, denormal mask, DAZ | |||
#else | |||
#define wdl_denorm_mm_csr_mask ((1<<15)|(1<<11)) // FTZ and underflow only (target SSE2) | |||
#endif | |||
#elif defined(__arm__) || defined(__aarch64__) | |||
#define WDL_DENORMAL_FTZMODE | |||
#define WDL_DENORMAL_FTZSTATE_TYPE unsigned long | |||
static unsigned long __attribute__((unused)) wdl_denorm_mm_getcsr() | |||
{ | |||
unsigned long rv; | |||
#ifdef __aarch64__ | |||
asm volatile ( "mrs %0, fpcr" : "=r" (rv)); | |||
#else | |||
asm volatile ( "fmrx %0, fpscr" : "=r" (rv)); | |||
#endif | |||
return rv; | |||
} | |||
static void __attribute__((unused)) wdl_denorm_mm_setcsr(unsigned long v) | |||
{ | |||
#ifdef __aarch64__ | |||
asm volatile ( "msr fpcr, %0" :: "r"(v)); | |||
#else | |||
asm volatile ( "fmxr fpscr, %0" :: "r"(v)); | |||
#endif | |||
} | |||
#define wdl_denorm_mm_csr_mask (1<<24) | |||
#endif | |||
class WDL_denormal_ftz_scope | |||
{ | |||
public: | |||
WDL_denormal_ftz_scope() | |||
{ | |||
#ifdef WDL_DENORMAL_FTZMODE | |||
const WDL_DENORMAL_FTZSTATE_TYPE b = wdl_denorm_mm_csr_mask; | |||
old_state = wdl_denorm_mm_getcsr(); | |||
if ((need_restore = (old_state & b) != b)) | |||
wdl_denorm_mm_setcsr(old_state|b); | |||
#endif | |||
} | |||
~WDL_denormal_ftz_scope() | |||
{ | |||
#ifdef WDL_DENORMAL_FTZMODE | |||
if (need_restore) wdl_denorm_mm_setcsr(old_state); | |||
#endif | |||
} | |||
#ifdef WDL_DENORMAL_FTZMODE | |||
WDL_DENORMAL_FTZSTATE_TYPE old_state; | |||
bool need_restore; | |||
#endif | |||
}; | |||
#endif | |||
#if !defined(WDL_DENORMAL_FTZMODE) && !defined(WDL_DENORMAL_DO_NOT_FILTER) | |||
static double WDL_DENORMAL_INLINE denormal_filter_double(double a) | |||
{ | |||
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0; | |||
} | |||
static double WDL_DENORMAL_INLINE denormal_filter_double2(double a) | |||
{ | |||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) > 0x100000 ? a : 0.0; | |||
} | |||
static double WDL_DENORMAL_INLINE denormal_filter_double_aggressive(double a) | |||
{ | |||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0; | |||
} | |||
static float WDL_DENORMAL_INLINE denormal_filter_float(float a) | |||
{ | |||
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f; | |||
} | |||
static float WDL_DENORMAL_INLINE denormal_filter_float2(float a) | |||
{ | |||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) > 0x800000 ? a : 0.0f; | |||
} | |||
static float WDL_DENORMAL_INLINE denormal_filter_float_aggressive(float a) | |||
{ | |||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix_double(double *a) | |||
{ | |||
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix_double_aggressive(double *a) | |||
{ | |||
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix_float(float *a) | |||
{ | |||
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix_float_aggressive(float *a) | |||
{ | |||
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f; | |||
} | |||
#ifdef __cplusplus // automatic typed versions (though one should probably use the explicit versions... | |||
static double WDL_DENORMAL_INLINE denormal_filter(double a) | |||
{ | |||
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0; | |||
} | |||
static double WDL_DENORMAL_INLINE denormal_filter_aggressive(double a) | |||
{ | |||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0; | |||
} | |||
static float WDL_DENORMAL_INLINE denormal_filter(float a) | |||
{ | |||
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f; | |||
} | |||
static float WDL_DENORMAL_INLINE denormal_filter_aggressive(float a) | |||
{ | |||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix(double *a) | |||
{ | |||
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(double *a) | |||
{ | |||
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix(float *a) | |||
{ | |||
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f; | |||
} | |||
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(float *a) | |||
{ | |||
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f; | |||
} | |||
#endif // cplusplus versions | |||
#else // end of !WDL_DENORMAL_DO_NOT_FILTER (and other platform-specific checks) | |||
#define denormal_filter(x) (x) | |||
#define denormal_filter2(x) (x) | |||
#define denormal_filter_double(x) (x) | |||
#define denormal_filter_double2(x) (x) | |||
#define denormal_filter_double_aggressive(x) (x) | |||
#define denormal_filter_float(x) (x) | |||
#define denormal_filter_float2(x) (x) | |||
#define denormal_filter_float_aggressive(x) (x) | |||
#define denormal_filter_aggressive(x) (x) | |||
#define denormal_fix(x) do { } while(0) | |||
#define denormal_fix_aggressive(x) do { } while(0) | |||
#define denormal_fix_double(x) do { } while(0) | |||
#define denormal_fix_double_aggressive(x) do { } while(0) | |||
#define denormal_fix_float(x) do { } while(0) | |||
#define denormal_fix_float_aggressive(x) do { } while(0) | |||
#endif | |||
//////////////////// | |||
// this isnt a denormal function but it is similar, so we'll put it here as a bonus | |||
static void WDL_DENORMAL_INLINE GetDoubleMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0 | |||
{ | |||
WDL_UINT64 i, o; | |||
memcpy(&i,in,sizeof(i)); | |||
memcpy(&o,out,sizeof(o)); | |||
i &= WDL_UINT64_CONST(0x7fffffffffffffff); | |||
if (i > o) memcpy(out,&i,sizeof(i)); | |||
} | |||
static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(float *out, const float *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0 | |||
{ | |||
unsigned int i, o; | |||
memcpy(&i, in, sizeof(i)); | |||
memcpy(&o, out, sizeof(o)); | |||
i &= 0x7fffffff; | |||
if (i > o) memcpy(out, &i, sizeof(i)); | |||
} | |||
#ifdef __cplusplus | |||
static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0 | |||
{ | |||
GetDoubleMaxAbsValue(out,in); | |||
} | |||
#endif | |||
#endif |
@@ -0,0 +1,329 @@ | |||
/* | |||
WDL - dirscan.h | |||
Copyright (C) 2005 and later Cockos Incorporated | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
/* | |||
This file provides the interface and implementation for WDL_DirScan, a simple | |||
(and somewhat portable) directory reading class. On non-Win32 systems it wraps | |||
opendir()/readdir()/etc. On Win32, it uses FindFirst*, and supports wildcards as | |||
well. | |||
*/ | |||
#ifndef _WDL_DIRSCAN_H_ | |||
#define _WDL_DIRSCAN_H_ | |||
#include "wdlstring.h" | |||
#ifndef _WIN32 | |||
#include <sys/types.h> | |||
#include <sys/stat.h> | |||
#include <dirent.h> | |||
extern struct stat wdl_stat_chk; | |||
// if this fails on linux, use CFLAGS += -D_FILE_OFFSET_BITS=64 | |||
typedef char wdl_dirscan_assert_failed_stat_not_64[sizeof(wdl_stat_chk.st_size)!=8 ? -1 : 1]; | |||
#endif | |||
class WDL_DirScan | |||
{ | |||
public: | |||
WDL_DirScan() : | |||
#ifdef _WIN32 | |||
m_h(INVALID_HANDLE_VALUE) | |||
#ifndef WDL_NO_SUPPORT_UTF8 | |||
, m_wcmode(false) | |||
#endif | |||
#else | |||
m_h(NULL), m_ent(NULL) | |||
#endif | |||
{ | |||
} | |||
~WDL_DirScan() | |||
{ | |||
Close(); | |||
} | |||
int First(const char *dirname | |||
#ifdef _WIN32 | |||
, int isExactSpec=0 | |||
#endif | |||
) // returns 0 if success | |||
{ | |||
WDL_FastString scanstr(dirname); | |||
const int l = scanstr.GetLength(); | |||
if (l < 1) return -1; | |||
#ifdef _WIN32 | |||
if (!isExactSpec) | |||
{ | |||
if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1); | |||
m_leading_path = scanstr; | |||
scanstr.Append("\\*"); | |||
} | |||
else | |||
{ | |||
m_leading_path = scanstr; | |||
// remove trailing wildcards and directory separator from m_leading_path | |||
const char *sp = m_leading_path.Get(); | |||
int idx = m_leading_path.GetLength() - 1; | |||
while (idx > 0 && sp[idx] != '/' && sp[idx] != '\\') idx--; | |||
if (idx > 0) m_leading_path.SetLen(idx); | |||
} | |||
#else | |||
if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1); | |||
m_leading_path = scanstr; | |||
if (!scanstr.GetLength()) scanstr.Set("/"); // fix for scanning / | |||
#endif | |||
Close(); | |||
#ifdef _WIN32 | |||
#ifndef WDL_NO_SUPPORT_UTF8 | |||
m_h=INVALID_HANDLE_VALUE; | |||
#ifdef WDL_SUPPORT_WIN9X | |||
m_wcmode = GetVersion()< 0x80000000; | |||
#else | |||
m_wcmode = true; | |||
#endif | |||
if (m_wcmode) | |||
{ | |||
int reqbuf = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,NULL,0); | |||
if (reqbuf > 1000) | |||
{ | |||
WDL_TypedBuf<WCHAR> tmp; | |||
tmp.Resize(reqbuf+20); | |||
if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,tmp.Get(),tmp.GetSize()-10)) | |||
{ | |||
correctlongpath(tmp.Get()); | |||
m_h=FindFirstFileW(tmp.Get(),&m_fd); | |||
} | |||
} | |||
else | |||
{ | |||
WCHAR wfilename[1024]; | |||
if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,wfilename,1024-10)) | |||
{ | |||
correctlongpath(wfilename); | |||
m_h=FindFirstFileW(wfilename,&m_fd); | |||
} | |||
} | |||
} | |||
if (m_h==INVALID_HANDLE_VALUE) m_wcmode=false; | |||
if (m_h==INVALID_HANDLE_VALUE) | |||
#endif | |||
m_h=FindFirstFile(scanstr.Get(),(WIN32_FIND_DATA*)&m_fd); | |||
return (m_h == INVALID_HANDLE_VALUE); | |||
#else | |||
m_ent=0; | |||
m_h=opendir(scanstr.Get()); | |||
return !m_h || Next(); | |||
#endif | |||
} | |||
int Next() // returns 0 on success | |||
{ | |||
#ifdef _WIN32 | |||
if (m_h == INVALID_HANDLE_VALUE) return -1; | |||
#ifndef WDL_NO_SUPPORT_UTF8 | |||
if (m_wcmode) return !FindNextFileW(m_h,&m_fd); | |||
#endif | |||
return !FindNextFile(m_h,(WIN32_FIND_DATA*)&m_fd); | |||
#else | |||
if (!m_h) return -1; | |||
return !(m_ent=readdir(m_h)); | |||
#endif | |||
} | |||
void Close() | |||
{ | |||
#ifdef _WIN32 | |||
if (m_h != INVALID_HANDLE_VALUE) FindClose(m_h); | |||
m_h=INVALID_HANDLE_VALUE; | |||
#else | |||
if (m_h) closedir(m_h); | |||
m_h=0; m_ent=0; | |||
#endif | |||
} | |||
#ifdef _WIN32 | |||
const char *GetCurrentFN() | |||
{ | |||
#ifndef WDL_NO_SUPPORT_UTF8 | |||
if (m_wcmode) | |||
{ | |||
if (!WideCharToMultiByte(CP_UTF8,0,m_fd.cFileName,-1,m_tmpbuf,sizeof(m_tmpbuf),NULL,NULL)) | |||
m_tmpbuf[0]=0; | |||
return m_tmpbuf; | |||
} | |||
#endif | |||
return ((WIN32_FIND_DATA *)&m_fd)->cFileName; | |||
} | |||
#else | |||
const char *GetCurrentFN() const { return m_ent?m_ent->d_name : ""; } | |||
#endif | |||
template<class T> void GetCurrentFullFN(T *str) | |||
{ | |||
str->Set(m_leading_path.Get()); | |||
#ifdef _WIN32 | |||
str->Append("\\"); | |||
#else | |||
str->Append("/"); | |||
#endif | |||
str->Append(GetCurrentFN()); | |||
} | |||
int GetCurrentIsDirectory() const // returns 1 if dir, 2 if symlink to dir, 4 if possibly-recursive symlink to dir | |||
{ | |||
#ifdef _WIN32 | |||
return !!(m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); | |||
#else | |||
char tmp[2048]; | |||
if (m_ent) switch (m_ent->d_type) | |||
{ | |||
case DT_DIR: return 1; | |||
case DT_LNK: | |||
{ | |||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name); | |||
char *rp = realpath(tmp,NULL); | |||
if (!rp) return 0; | |||
struct stat sb; | |||
int ret = (!stat(rp,&sb) && (sb.st_mode & S_IFMT) == S_IFDIR) ? 2 : 0; | |||
if (ret) | |||
{ | |||
// treat symlinks of /path/to/foo -> /path from being resolved (avoiding obvious feedback loops) | |||
const int rpl = (int) strlen(rp); | |||
if ( | |||
#ifdef __APPLE__ | |||
!strnicmp(rp,m_leading_path.Get(),rpl) | |||
#else | |||
!strncmp(rp,m_leading_path.Get(),rpl) | |||
#endif | |||
&& (m_leading_path.Get()[rpl] == '/' || m_leading_path.Get()[rpl] == 0) | |||
) ret = 4; | |||
} | |||
free(rp); | |||
return ret; | |||
} | |||
case DT_UNKNOWN: | |||
{ | |||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name); | |||
DIR *d = opendir(tmp); | |||
if (d) { closedir(d); return 1; } | |||
return 0; | |||
} | |||
} | |||
return 0; | |||
#endif | |||
} | |||
// these are somewhat windows specific calls, eh | |||
#ifdef _WIN32 | |||
DWORD GetCurrentFileSize(DWORD *HighWord=NULL) const { if (HighWord) *HighWord = m_fd.nFileSizeHigh; return m_fd.nFileSizeLow; } | |||
void GetCurrentLastWriteTime(FILETIME *ft) const { *ft = m_fd.ftLastWriteTime; } | |||
void GetCurrentLastAccessTime(FILETIME *ft) const { *ft = m_fd.ftLastAccessTime; } | |||
void GetCurrentCreationTime(FILETIME *ft) const { *ft = m_fd.ftCreationTime; } | |||
DWORD GetFileAttributes() const { return m_fd.dwFileAttributes; } | |||
#elif defined(_WDL_SWELL_H_) | |||
void GetCurrentCreationTime(FILETIME *ft) | |||
{ | |||
char tmp[2048]; | |||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN()); | |||
struct stat st={0,}; | |||
stat(tmp,&st); | |||
unsigned long long a=(unsigned long long)st.st_ctime; // seconds since january 1st, 1970 | |||
a+=11644473600ull; // 1601->1970 | |||
a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds) | |||
ft->dwLowDateTime=a & 0xffffffff; | |||
ft->dwHighDateTime=a>>32; | |||
} | |||
void GetCurrentLastWriteTime(FILETIME *ft) | |||
{ | |||
char tmp[2048]; | |||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN()); | |||
struct stat st={0,}; | |||
stat(tmp,&st); | |||
unsigned long long a=(unsigned long long)st.st_mtime; // seconds since january 1st, 1970 | |||
a+=11644473600ull; // 1601->1970 | |||
a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds) | |||
ft->dwLowDateTime=a & 0xffffffff; | |||
ft->dwHighDateTime=a>>32; | |||
} | |||
DWORD GetCurrentFileSize(DWORD *HighWord=NULL) | |||
{ | |||
char tmp[2048]; | |||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN()); | |||
struct stat st={0,}; | |||
stat(tmp,&st); | |||
if (HighWord) *HighWord = (DWORD)(st.st_size>>32); | |||
return (DWORD)(st.st_size&0xffffffff); | |||
} | |||
#endif | |||
private: | |||
#ifdef _WIN32 | |||
#ifndef WDL_NO_SUPPORT_UTF8 | |||
bool m_wcmode; | |||
WIN32_FIND_DATAW m_fd; | |||
char m_tmpbuf[MAX_PATH*5]; // even if each byte gets encoded as 4 utf-8 bytes this should be plenty ;) | |||
#else | |||
WIN32_FIND_DATA m_fd; | |||
#endif | |||
HANDLE m_h; | |||
#else | |||
DIR *m_h; | |||
struct dirent *m_ent; | |||
#endif | |||
WDL_FastString m_leading_path; | |||
#ifdef _WIN32 | |||
static void correctlongpath(WCHAR *buf) // this also exists as wdl_utf8_correctlongpath | |||
{ | |||
const WCHAR *insert; | |||
WCHAR *wr; | |||
int skip = 0; | |||
if (!buf || !buf[0] || wcslen(buf) < 256) return; | |||
if (buf[1] == ':') insert=L"\\\\?\\"; | |||
else if (buf[0] == '\\' && buf[1] == '\\') { insert = L"\\\\?\\UNC\\"; skip=2; } | |||
else return; | |||
wr = buf + wcslen(insert); | |||
memmove(wr, buf + skip, (wcslen(buf+skip)+1)*2); | |||
memmove(buf,insert,wcslen(insert)*2); | |||
while (*wr) | |||
{ | |||
if (*wr == '/') *wr = '\\'; | |||
wr++; | |||
} | |||
} | |||
#endif | |||
} WDL_FIXALIGN; | |||
#endif |
@@ -0,0 +1,113 @@ | |||
%option reentrant | |||
%option prefix="nseel" | |||
%option bison-bridge | |||
%option bison-locations | |||
%option noyywrap | |||
%option never-interactive | |||
%option batch | |||
%option nounput | |||
%{ | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#define YY_USER_ACTION yylloc->first_line = yylineno; | |||
#define YY_FATAL_ERROR(msg) { ((struct yyguts_t*)yyscanner)->yyextra_r->errVar=1; } | |||
#define YY_INPUT(buf,result,max_size) { (result) = nseel_gets(yyextra,(buf),max_size); } | |||
#define YY_EXTRA_TYPE compileContext * | |||
#undef YY_BUF_SIZE | |||
#define YY_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN*2) | |||
#undef YY_READ_BUF_SIZE | |||
#define YY_READ_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN) | |||
#include "y.tab.h" | |||
#ifdef _WIN32 | |||
#define YY_NO_UNISTD_H | |||
#endif | |||
#include "ns-eel-int.h" | |||
int nseel_gets(compileContext *ctx, char *buf, size_t sz); | |||
#define PARSENUM *yylval = nseel_translate(yyextra,yytext, 0); return VALUE; | |||
#define EEL_ACTION(x) return x; | |||
#ifdef stdin | |||
#undef stdin | |||
#endif | |||
#define stdin (0) | |||
#ifdef stdout | |||
#undef stdout | |||
#endif | |||
#define stdout (0) | |||
static int g_fake_errno; | |||
#ifdef errno | |||
#undef errno | |||
#endif | |||
#define errno g_fake_errno | |||
static void comment(yyscan_t yyscanner); | |||
%} | |||
%% | |||
[0-9]+\.?[0-9]* PARSENUM; | |||
\.[0-9]+ PARSENUM; | |||
0[xX][0-9a-fA-F]* PARSENUM; | |||
\$[xX][0-9a-fA-F]* PARSENUM; | |||
\$\~[0-9]* PARSENUM; | |||
\$[Ee] PARSENUM; | |||
\$[Pp][Ii] PARSENUM; | |||
\$[Pp][Hh][Ii] PARSENUM; | |||
\$\'.\' PARSENUM; | |||
\#[a-zA-Z0-9\._]* *yylval = nseel_translate(yyextra,yytext, 0); return STRING_IDENTIFIER; | |||
\<\< return TOKEN_SHL; | |||
\>\> return TOKEN_SHR; | |||
\<= return TOKEN_LTE; | |||
\>= return TOKEN_GTE; | |||
== return TOKEN_EQ; | |||
=== return TOKEN_EQ_EXACT; | |||
\!= return TOKEN_NE; | |||
\!== return TOKEN_NE_EXACT; | |||
\&\& return TOKEN_LOGICAL_AND; | |||
\|\| return TOKEN_LOGICAL_OR; | |||
\+= return TOKEN_ADD_OP; | |||
-= return TOKEN_SUB_OP; | |||
%= return TOKEN_MOD_OP; | |||
\|= return TOKEN_OR_OP; | |||
\&= return TOKEN_AND_OP; | |||
\~= return TOKEN_XOR_OP; | |||
\/= return TOKEN_DIV_OP; | |||
\*= return TOKEN_MUL_OP; | |||
\^= return TOKEN_POW_OP; | |||
[a-zA-Z_][a-zA-Z0-9\._]* &yylval = nseel_createCompiledValuePtr((compileContext *)yyextra, NULL, yytext); return IDENTIFIER; | |||
[ \t\r\n]+ /* whitespace */ | |||
\/\/.*$ /* comment */ | |||
"/*" { comment(yyscanner); } | |||
. return (int)yytext[0]; | |||
%% | |||
static void comment(yyscan_t yyscanner) | |||
{ | |||
int c,lc=0; | |||
while (0 != (c = input(yyscanner))) | |||
{ | |||
if (c == '/' && lc == '*') return; | |||
lc = c; | |||
} | |||
// end of file, ignore for now | |||
} |
@@ -0,0 +1,376 @@ | |||
%pure-parser | |||
%name-prefix="nseel" | |||
%parse-param { compileContext* context } | |||
%lex-param { void* scanner } | |||
/* this will prevent y.tab.c from ever calling yydestruct(), since we do not use it and it is a waste */ | |||
%destructor { | |||
#define yydestruct(a,b,c,d,e) | |||
} VALUE | |||
%{ | |||
#ifdef _WIN32 | |||
#include <windows.h> | |||
#endif | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <math.h> | |||
#include "y.tab.h" | |||
#include "ns-eel-int.h" | |||
#define scanner context->scanner | |||
#define YY_(x) ("") | |||
%} | |||
%token VALUE IDENTIFIER TOKEN_SHL TOKEN_SHR | |||
%token TOKEN_LTE TOKEN_GTE TOKEN_EQ TOKEN_EQ_EXACT TOKEN_NE TOKEN_NE_EXACT TOKEN_LOGICAL_AND TOKEN_LOGICAL_OR | |||
%token TOKEN_ADD_OP TOKEN_SUB_OP TOKEN_MOD_OP TOKEN_OR_OP TOKEN_AND_OP TOKEN_XOR_OP TOKEN_DIV_OP TOKEN_MUL_OP TOKEN_POW_OP | |||
%token STRING_LITERAL STRING_IDENTIFIER | |||
%expect 75 | |||
%start program | |||
%% | |||
more_params: | |||
expression | |||
| expression ',' more_params | |||
{ | |||
$$ = nseel_createMoreParametersOpcode(context,$1,$3); | |||
} | |||
; | |||
string: | |||
STRING_LITERAL | |||
| STRING_LITERAL string | |||
{ | |||
((struct eelStringSegmentRec *)$1)->_next = (struct eelStringSegmentRec *)$2; | |||
$$ = $1; | |||
} | |||
; | |||
assignable_value: | |||
IDENTIFIER | |||
{ | |||
if (!($$ = nseel_resolve_named_symbol(context, $1, -1, NULL))) /* convert from purely named to namespace-relative, etc */ | |||
{ | |||
yyerror(&yyloc, context, ""); | |||
YYERROR; | |||
} | |||
} | |||
/* we used to have VALUE in here rather than rvalue, to allow 1=1 1+=2 etc, but silly to, | |||
though this breaks Vmorph, which does 1=1 for a nop, and Jonas DrumReaplacer, which does x = 0 = y = 0 */ | |||
| '(' expression ')' | |||
{ | |||
$$ = $2; | |||
} | |||
| IDENTIFIER '(' expression ')' '(' expression ')' | |||
{ | |||
int err; | |||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, $6, &err))) | |||
{ | |||
if (err == -1) yyerror(&yylsp[-2], context, ""); | |||
else if (err == 0) yyerror(&yylsp[-6], context, ""); | |||
else yyerror(&yylsp[-3], context, ""); // parameter count wrong | |||
YYERROR; | |||
} | |||
} | |||
| IDENTIFIER '(' expression ')' | |||
{ | |||
int err; | |||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, 0, &err))) | |||
{ | |||
if (err == 0) yyerror(&yylsp[-3], context, ""); | |||
else yyerror(&yylsp[0], context, ""); // parameter count wrong | |||
YYERROR; | |||
} | |||
} | |||
| IDENTIFIER '(' ')' | |||
{ | |||
int err; | |||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, nseel_createCompiledValue(context,0.0), 0, 0, 0,&err))) | |||
{ | |||
if (err == 0) yyerror(&yylsp[-2], context, ""); // function not found | |||
else yyerror(&yylsp[0], context, ""); // parameter count wrong | |||
YYERROR; | |||
} | |||
} | |||
| IDENTIFIER '(' expression ',' expression ')' | |||
{ | |||
int err; | |||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, 0, 0,&err))) | |||
{ | |||
if (err == 0) yyerror(&yylsp[-5], context, ""); | |||
else if (err == 2) yyerror(&yylsp[0], context, ""); // needs more than 2 parameters | |||
else yyerror(&yylsp[-2], context, ""); // less than 2 | |||
YYERROR; | |||
} | |||
} | |||
| IDENTIFIER '(' expression ',' expression ',' more_params ')' | |||
{ | |||
int err; | |||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, $7, 0, &err))) | |||
{ | |||
if (err == 0) yyerror(&yylsp[-7], context, ""); | |||
else if (err==2) yyerror(&yylsp[0], context, ""); // needs more parameters | |||
else if (err==4) yyerror(&yylsp[-4], context, ""); // needs single parameter | |||
else yyerror(&yylsp[-2], context, ""); // less parm | |||
YYERROR; | |||
} | |||
} | |||
| rvalue '[' ']' | |||
{ | |||
$$ = nseel_createMemoryAccess(context,$1,0); | |||
} | |||
| rvalue '[' expression ']' | |||
{ | |||
$$ = nseel_createMemoryAccess(context,$1,$3); | |||
} | |||
; | |||
rvalue: | |||
VALUE | |||
| STRING_IDENTIFIER | |||
| string | |||
{ | |||
$$ = nseel_eelMakeOpcodeFromStringSegments(context,(struct eelStringSegmentRec *)$1); | |||
} | |||
| assignable_value | |||
; | |||
assignment: | |||
rvalue | |||
| assignable_value '=' if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_ASSIGN,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_ADD_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_ADD_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_SUB_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_SUB_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_MOD_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_MOD_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_OR_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_OR_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_AND_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_AND_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_XOR_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_XOR_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_DIV_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_DIV_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_MUL_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_MUL_OP,2,$1,$3); | |||
} | |||
| assignable_value TOKEN_POW_OP if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_POW_OP,2,$1,$3); | |||
} | |||
| STRING_IDENTIFIER '=' if_else_expr | |||
{ | |||
$$ = nseel_createFunctionByName(context,"strcpy",2,$1,$3,NULL); | |||
} | |||
| STRING_IDENTIFIER TOKEN_ADD_OP if_else_expr | |||
{ | |||
$$ = nseel_createFunctionByName(context,"strcat",2,$1,$3,NULL); | |||
} | |||
; | |||
unary_expr: | |||
assignment | |||
| '+' unary_expr | |||
{ | |||
$$ = $2; | |||
} | |||
| '-' unary_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_UMINUS,1,$2,0); | |||
} | |||
| '!' unary_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_NOT,1,$2,0); | |||
} | |||
; | |||
pow_expr: | |||
unary_expr | |||
| pow_expr '^' unary_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_POW,2,$1,$3); | |||
} | |||
; | |||
mod_expr: | |||
pow_expr | |||
| mod_expr '%' pow_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_MOD,2,$1,$3); | |||
} | |||
| mod_expr TOKEN_SHL pow_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_SHL,2,$1,$3); | |||
} | |||
| mod_expr TOKEN_SHR pow_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_SHR,2,$1,$3); | |||
} | |||
; | |||
div_expr: | |||
mod_expr | |||
| div_expr '/' mod_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_DIVIDE,2,$1,$3); | |||
} | |||
; | |||
mul_expr: | |||
div_expr | |||
| mul_expr '*' div_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_MULTIPLY,2,$1,$3); | |||
} | |||
; | |||
sub_expr: | |||
mul_expr | |||
| sub_expr '-' mul_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_SUB,2,$1,$3); | |||
} | |||
; | |||
add_expr: | |||
sub_expr | |||
| add_expr '+' sub_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_ADD,2,$1,$3); | |||
} | |||
; | |||
andor_expr: | |||
add_expr | |||
| andor_expr '&' add_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_AND,2,$1,$3); | |||
} | |||
| andor_expr '|' add_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_OR,2,$1,$3); | |||
} | |||
| andor_expr '~' add_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_XOR,2,$1,$3); | |||
} | |||
; | |||
cmp_expr: | |||
andor_expr | |||
| cmp_expr '<' andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_LT,2,$1,$3); | |||
} | |||
| cmp_expr '>' andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_GT,2,$1,$3); | |||
} | |||
| cmp_expr TOKEN_LTE andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_LTE,2,$1,$3); | |||
} | |||
| cmp_expr TOKEN_GTE andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_GTE,2,$1,$3); | |||
} | |||
| cmp_expr TOKEN_EQ andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_EQ,2,$1,$3); | |||
} | |||
| cmp_expr TOKEN_EQ_EXACT andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_EQ_EXACT,2,$1,$3); | |||
} | |||
| cmp_expr TOKEN_NE andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_NE,2,$1,$3); | |||
} | |||
| cmp_expr TOKEN_NE_EXACT andor_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_NE_EXACT,2,$1,$3); | |||
} | |||
; | |||
logical_and_or_expr: | |||
cmp_expr | |||
| logical_and_or_expr TOKEN_LOGICAL_AND cmp_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_AND,2,$1,$3); | |||
} | |||
| logical_and_or_expr TOKEN_LOGICAL_OR cmp_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_OR,2,$1,$3); | |||
} | |||
; | |||
if_else_expr: | |||
logical_and_or_expr | |||
| logical_and_or_expr '?' if_else_expr ':' if_else_expr | |||
{ | |||
$$ = nseel_createIfElse(context, $1, $3, $5); | |||
} | |||
| logical_and_or_expr '?' ':' if_else_expr | |||
{ | |||
$$ = nseel_createIfElse(context, $1, 0, $4); | |||
} | |||
| logical_and_or_expr '?' if_else_expr | |||
{ | |||
$$ = nseel_createIfElse(context, $1, $3, 0); | |||
} | |||
; | |||
expression: | |||
if_else_expr | |||
| expression ';' if_else_expr | |||
{ | |||
$$ = nseel_createSimpleCompiledFunction(context,FN_JOIN_STATEMENTS,2,$1,$3); | |||
} | |||
| expression ';' | |||
{ | |||
$$ = $1; | |||
} | |||
; | |||
program: | |||
expression | |||
{ | |||
if (@1.first_line) { } | |||
context->result = $1; | |||
} | |||
; | |||
%% |
@@ -0,0 +1,71 @@ | |||
#ifndef __EEL_ATOMIC_H__ | |||
#define __EEL_ATOMIC_H__ | |||
// requires these to be defined | |||
//#define EEL_ATOMIC_SET_SCOPE(opaque) WDL_Mutex *mutex = (opaque?&((effectProcessor *)opaque)->m_atomic_mutex:&atomic_mutex); | |||
//#define EEL_ATOMIC_ENTER mutex->Enter() | |||
//#define EEL_ATOMIC_LEAVE mutex->Leave() | |||
static EEL_F NSEEL_CGEN_CALL atomic_setifeq(void *opaque, EEL_F *a, EEL_F *cmp, EEL_F *nd) | |||
{ | |||
EEL_F ret; | |||
EEL_ATOMIC_SET_SCOPE(opaque) | |||
EEL_ATOMIC_ENTER; | |||
ret = *a; | |||
if (fabs(ret - *cmp) < NSEEL_CLOSEFACTOR) *a = *nd; | |||
EEL_ATOMIC_LEAVE; | |||
return ret; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL atomic_exch(void *opaque, EEL_F *a, EEL_F *b) | |||
{ | |||
EEL_F tmp; | |||
EEL_ATOMIC_SET_SCOPE(opaque) | |||
EEL_ATOMIC_ENTER; | |||
tmp = *b; | |||
*b = *a; | |||
*a = tmp; | |||
EEL_ATOMIC_LEAVE; | |||
return tmp; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL atomic_add(void *opaque, EEL_F *a, EEL_F *b) | |||
{ | |||
EEL_F tmp; | |||
EEL_ATOMIC_SET_SCOPE(opaque) | |||
EEL_ATOMIC_ENTER; | |||
tmp = (*a += *b); | |||
EEL_ATOMIC_LEAVE; | |||
return tmp; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL atomic_set(void *opaque, EEL_F *a, EEL_F *b) | |||
{ | |||
EEL_F tmp; | |||
EEL_ATOMIC_SET_SCOPE(opaque) | |||
EEL_ATOMIC_ENTER; | |||
tmp = *a = *b; | |||
EEL_ATOMIC_LEAVE; | |||
return tmp; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL atomic_get(void *opaque, EEL_F *a) | |||
{ | |||
EEL_F tmp; | |||
EEL_ATOMIC_SET_SCOPE(opaque) | |||
EEL_ATOMIC_ENTER; | |||
tmp = *a; | |||
EEL_ATOMIC_LEAVE; | |||
return tmp; | |||
} | |||
static void EEL_atomic_register() | |||
{ | |||
NSEEL_addfunc_retval("atomic_setifequal",3, NSEEL_PProc_THIS, &atomic_setifeq); | |||
NSEEL_addfunc_retval("atomic_exch",2, NSEEL_PProc_THIS, &atomic_exch); | |||
NSEEL_addfunc_retval("atomic_add",2, NSEEL_PProc_THIS, &atomic_add); | |||
NSEEL_addfunc_retval("atomic_set",2, NSEEL_PProc_THIS, &atomic_set); | |||
NSEEL_addfunc_retval("atomic_get",1, NSEEL_PProc_THIS, &atomic_get); | |||
} | |||
#endif |
@@ -0,0 +1,81 @@ | |||
#ifndef _EEL_EVAL_H_ | |||
#define _EEL_EVAL_H_ | |||
#ifndef EEL_EVAL_GET_CACHED | |||
#define EEL_EVAL_GET_CACHED(str, ch) (NULL) | |||
#endif | |||
#ifndef EEL_EVAL_SET_CACHED | |||
#define EEL_EVAL_SET_CACHED(sv, ch) { NSEEL_code_free(ch); free(sv); } | |||
#endif | |||
#ifndef EEL_EVAL_SCOPE_ENTER | |||
#define EEL_EVAL_SCOPE_ENTER 1 | |||
#define EEL_EVAL_SCOPE_LEAVE | |||
#endif | |||
static EEL_F NSEEL_CGEN_CALL _eel_eval(void *opaque, EEL_F *s) | |||
{ | |||
NSEEL_VMCTX r = EEL_EVAL_GET_VMCTX(opaque); | |||
NSEEL_CODEHANDLE ch = NULL; | |||
char *sv=NULL; | |||
if (r) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (!str) | |||
{ | |||
EEL_STRING_DEBUGOUT("eval() passed invalid string handle %f",*s); | |||
} | |||
#endif | |||
if (str && *str) | |||
{ | |||
sv=EEL_EVAL_GET_CACHED(str,ch); | |||
if (!sv) sv=strdup(str); | |||
} | |||
} | |||
if (sv) | |||
{ | |||
if (!ch) ch = NSEEL_code_compile(r,sv,0); | |||
if (ch) | |||
{ | |||
if (EEL_EVAL_SCOPE_ENTER) | |||
{ | |||
NSEEL_code_execute(ch); | |||
EEL_EVAL_SCOPE_LEAVE | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("eval() reentrancy limit reached"); | |||
#endif | |||
} | |||
EEL_EVAL_SET_CACHED(sv,ch); | |||
return 1.0; | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
const char *err=NSEEL_code_getcodeerror(r); | |||
if (err) EEL_STRING_DEBUGOUT("eval() error: %s",err); | |||
#endif | |||
} | |||
free(sv); | |||
} | |||
return 0.0; | |||
} | |||
void EEL_eval_register() | |||
{ | |||
NSEEL_addfunc_retval("eval",1,NSEEL_PProc_THIS,&_eel_eval); | |||
} | |||
#ifdef EEL_WANT_DOCUMENTATION | |||
static const char *eel_eval_function_reference = | |||
"eval\t\"code\"\tExecutes code passed in. Code can use functions, but functions created in code can't be used elsewhere.\0" | |||
; | |||
#endif | |||
#endif |
@@ -0,0 +1,396 @@ | |||
#ifndef __EEL_FFT_H_ | |||
#define __EEL_FFT_H_ | |||
#include "../fft.h" | |||
#if WDL_FFT_REALSIZE != EEL_F_SIZE | |||
#error WDL_FFT_REALSIZE -- EEL_F_SIZE size mismatch | |||
#endif | |||
#ifndef EEL_FFT_MINBITLEN | |||
#define EEL_FFT_MINBITLEN 4 | |||
#endif | |||
#ifndef EEL_FFT_MAXBITLEN | |||
#define EEL_FFT_MAXBITLEN 15 | |||
#endif | |||
#ifndef EEL_FFT_MINBITLEN_REORDER | |||
#define EEL_FFT_MINBITLEN_REORDER (EEL_FFT_MINBITLEN-1) | |||
#endif | |||
//#define EEL_SUPER_FAST_FFT_REORDERING // quite a bit faster (50-100%) than "normal", but uses a 256kb lookup | |||
//#define EEL_SLOW_FFT_REORDERING // 20%-80% slower than normal, alloca() use, no reason to ever use this | |||
#ifdef EEL_SUPER_FAST_FFT_REORDERING | |||
static int *fft_reorder_table_for_bitsize(int bitsz) | |||
{ | |||
static int s_tab[ (2 << EEL_FFT_MAXBITLEN) + 24*(EEL_FFT_MAXBITLEN-EEL_FFT_MINBITLEN_REORDER+1) ]; // big 256kb table, ugh | |||
if (bitsz<=EEL_FFT_MINBITLEN_REORDER) return s_tab; | |||
return s_tab + (1<<bitsz) + (bitsz-EEL_FFT_MINBITLEN_REORDER) * 24; | |||
} | |||
static void fft_make_reorder_table(int bitsz, int *tab) | |||
{ | |||
const int fft_sz=1<<bitsz; | |||
char flag[1<<EEL_FFT_MAXBITLEN]; | |||
int x; | |||
int *tabstart = tab; | |||
memset(flag,0,fft_sz); | |||
for (x=0;x<fft_sz;x++) | |||
{ | |||
int fx; | |||
if (!flag[x] && (fx=WDL_fft_permute(fft_sz,x))!=x) | |||
{ | |||
flag[x]=1; | |||
*tab++ = x; | |||
do | |||
{ | |||
flag[fx]=1; | |||
*tab++ = fx; | |||
fx = WDL_fft_permute(fft_sz, fx); | |||
} | |||
while (fx != x); | |||
*tab++ = 0; // delimit a run | |||
} | |||
else flag[x]=1; | |||
} | |||
*tab++ = 0; // doublenull terminated | |||
} | |||
static void fft_reorder_buffer(int bitsz, WDL_FFT_COMPLEX *data, int fwd) | |||
{ | |||
const int *tab=fft_reorder_table_for_bitsize(bitsz); | |||
if (!fwd) | |||
{ | |||
while (*tab) | |||
{ | |||
const int sidx=*tab++; | |||
WDL_FFT_COMPLEX a=data[sidx]; | |||
for (;;) | |||
{ | |||
WDL_FFT_COMPLEX ta; | |||
const int idx=*tab++; | |||
if (!idx) break; | |||
ta=data[idx]; | |||
data[idx]=a; | |||
a=ta; | |||
} | |||
data[sidx] = a; | |||
} | |||
} | |||
else | |||
{ | |||
while (*tab) | |||
{ | |||
const int sidx=*tab++; | |||
int lidx = sidx; | |||
const WDL_FFT_COMPLEX sta=data[lidx]; | |||
for (;;) | |||
{ | |||
const int idx=*tab++; | |||
if (!idx) break; | |||
data[lidx]=data[idx]; | |||
lidx=idx; | |||
} | |||
data[lidx] = sta; | |||
} | |||
} | |||
return 1; | |||
} | |||
#else | |||
#ifndef EEL_SLOW_FFT_REORDERING | |||
// moderate speed mode, minus the big 256k table | |||
static void fft_reorder_buffer(int bitsz, WDL_FFT_COMPLEX *data, int fwd) | |||
{ | |||
// this is a good compromise, quite a bit faster than out of place reordering, but no separate 256kb lookup required | |||
/* | |||
these generated via: | |||
static void fft_make_reorder_table(int bitsz) | |||
{ | |||
int fft_sz=1<<bitsz,x; | |||
char flag[65536]={0,}; | |||
printf("static const int tab%d[]={ ",fft_sz); | |||
for (x=0;x<fft_sz;x++) | |||
{ | |||
int fx; | |||
if (!flag[x] && (fx=WDL_fft_permute(fft_sz,x))!=x) | |||
{ | |||
printf("%d, ",x); | |||
do { flag[fx]=1; fx = WDL_fft_permute(fft_sz, fx); } while (fx != x); | |||
} | |||
flag[x]=1; | |||
} | |||
printf(" 0 };\n"); | |||
} | |||
*/ | |||
static const int tab4_8_32[]={ 1, 0 }; | |||
static const int tab16[]={ 1, 3, 0 }; | |||
static const int tab64[]={ 1, 3, 9, 0 }; | |||
static const int tab128[]={ 1, 3, 4, 9, 14, 0 }; | |||
static const int tab256[]={ 1, 3, 6, 12, 13, 14, 19, 0 }; | |||
static const int tab512[]={ 1, 4, 7, 9, 18, 50, 115, 0 }; | |||
static const int tab1024[]={ 1, 3, 4, 25, 26, 77, 79, 0 }; | |||
static const int tab2048[]={ 1, 58, 59, 106, 135, 206, 210, 212, 0 }; | |||
static const int tab4096[]={ 1, 3, 12, 25, 54, 221, 313, 431, 453, 0 }; | |||
static const int tab8192[]={ 1, 12, 18, 26, 30, 100, 101, 106, 113, 144, 150, 237, 244, 247, 386, 468, 513, 1210, 4839, 0 }; | |||
static const int tab16384[]={ 1, 3, 6, 24, 1219, 0 }; | |||
static const int tab32768[]={ 1, 3, 4, 7, 13, 18, 31, 64, 113, 145, 203, 246, 594, 956, 1871, 2439, 4959, 19175, 0 }; | |||
const int *tab; | |||
switch (bitsz) | |||
{ | |||
case 1: return; // no reorder necessary | |||
case 2: | |||
case 3: | |||
case 5: tab = tab4_8_32; break; | |||
case 4: tab=tab16; break; | |||
case 6: tab=tab64; break; | |||
case 7: tab=tab128; break; | |||
case 8: tab=tab256; break; | |||
case 9: tab=tab512; break; | |||
case 10: tab=tab1024; break; | |||
case 11: tab=tab2048; break; | |||
case 12: tab=tab4096; break; | |||
case 13: tab=tab8192; break; | |||
case 14: tab=tab16384; break; | |||
case 15: tab=tab32768; break; | |||
default: return; // no reorder possible | |||
} | |||
const int fft_sz=1<<bitsz; | |||
const int *tb2 = WDL_fft_permute_tab(fft_sz); | |||
if (!tb2) return; // ugh | |||
if (!fwd) | |||
{ | |||
while (*tab) | |||
{ | |||
const int sidx=*tab++; | |||
WDL_FFT_COMPLEX a=data[sidx]; | |||
int idx=sidx; | |||
for (;;) | |||
{ | |||
WDL_FFT_COMPLEX ta; | |||
idx=tb2[idx]; | |||
if (idx==sidx) break; | |||
ta=data[idx]; | |||
data[idx]=a; | |||
a=ta; | |||
} | |||
data[sidx] = a; | |||
} | |||
} | |||
else | |||
{ | |||
while (*tab) | |||
{ | |||
const int sidx=*tab++; | |||
int lidx = sidx; | |||
const WDL_FFT_COMPLEX sta=data[lidx]; | |||
for (;;) | |||
{ | |||
const int idx=tb2[lidx]; | |||
if (idx==sidx) break; | |||
data[lidx]=data[idx]; | |||
lidx=idx; | |||
} | |||
data[lidx] = sta; | |||
} | |||
} | |||
} | |||
#endif // not fast ,not slow, just right | |||
#endif | |||
//#define TIMING | |||
//#include "../timing.h" | |||
// 0=fw, 1=iv, 2=fwreal, 3=ireal, 4=permutec, 6=permuter | |||
// low bit: is inverse | |||
// second bit: was isreal, but no longer used | |||
// third bit: is permute | |||
static void FFT(int sizebits, EEL_F *data, int dir) | |||
{ | |||
if (dir >= 4 && dir < 8) | |||
{ | |||
if (dir == 4 || dir == 5) | |||
{ | |||
//timingEnter(0); | |||
#if defined(EEL_SUPER_FAST_FFT_REORDERING) || !defined(EEL_SLOW_FFT_REORDERING) | |||
fft_reorder_buffer(sizebits,(WDL_FFT_COMPLEX*)data,dir==4); | |||
#else | |||
// old blech | |||
const int flen=1<<sizebits; | |||
int x; | |||
EEL_F *tmp=(EEL_F*)alloca(sizeof(EEL_F)*flen*2); | |||
const int flen2=flen+flen; | |||
// reorder entries, now | |||
memcpy(tmp,data,sizeof(EEL_F)*flen*2); | |||
if (dir == 4) | |||
{ | |||
for (x = 0; x < flen2; x += 2) | |||
{ | |||
int y=WDL_fft_permute(flen,x/2)*2; | |||
data[x]=tmp[y]; | |||
data[x+1]=tmp[y+1]; | |||
} | |||
} | |||
else | |||
{ | |||
for (x = 0; x < flen2; x += 2) | |||
{ | |||
int y=WDL_fft_permute(flen,x/2)*2; | |||
data[y]=tmp[x]; | |||
data[y+1]=tmp[x+1]; | |||
} | |||
} | |||
#endif | |||
//timingLeave(0); | |||
} | |||
} | |||
else if (dir >= 0 && dir < 2) | |||
{ | |||
WDL_fft((WDL_FFT_COMPLEX*)data,1<<sizebits,dir&1); | |||
} | |||
else if (dir >= 2 && dir < 4) | |||
{ | |||
WDL_real_fft((WDL_FFT_REAL*)data,1<<sizebits,dir&1); | |||
} | |||
} | |||
static EEL_F * fft_func(int dir, EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
const int offs = (int)(*start + 0.0001); | |||
const int itemSizeShift=(dir&2)?0:1; | |||
int l=(int)(*length + 0.0001); | |||
int bitl=0; | |||
int ilen; | |||
EEL_F *ptr; | |||
while (l>1 && bitl < EEL_FFT_MAXBITLEN) | |||
{ | |||
bitl++; | |||
l>>=1; | |||
} | |||
if (bitl < ((dir&4) ? EEL_FFT_MINBITLEN_REORDER : EEL_FFT_MINBITLEN)) // smallest FFT is 16 item, smallest reorder is 8 item | |||
{ | |||
return start; | |||
} | |||
ilen=1<<bitl; | |||
// check to make sure we don't cross a boundary | |||
if (offs/NSEEL_RAM_ITEMSPERBLOCK != (offs + (ilen<<itemSizeShift) - 1)/NSEEL_RAM_ITEMSPERBLOCK) | |||
{ | |||
return start; | |||
} | |||
ptr=__NSEEL_RAMAlloc(blocks,offs); | |||
if (!ptr || ptr==&nseel_ramalloc_onfail) | |||
{ | |||
return start; | |||
} | |||
FFT(bitl,ptr,dir); | |||
return start; | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL eel_fft(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return fft_func(0,blocks,start,length); | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL eel_ifft(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return fft_func(1,blocks,start,length); | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL eel_fft_real(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return fft_func(2,blocks,start,length); | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL eel_ifft_real(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return fft_func(3,blocks,start,length); | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL eel_fft_permute(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return fft_func(4,blocks,start,length); | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL eel_ifft_permute(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return fft_func(5,blocks,start,length); | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL eel_convolve_c(EEL_F **blocks,EEL_F *dest, EEL_F *src, EEL_F *lenptr) | |||
{ | |||
const int dest_offs = (int)(*dest + 0.0001); | |||
const int src_offs = (int)(*src + 0.0001); | |||
const int len = ((int)(*lenptr + 0.0001)) * 2; | |||
EEL_F *srcptr,*destptr; | |||
if (len < 1 || len > NSEEL_RAM_ITEMSPERBLOCK || dest_offs < 0 || src_offs < 0 || | |||
dest_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK || src_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return dest; | |||
if ((dest_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest; | |||
if ((src_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest; | |||
srcptr = __NSEEL_RAMAlloc(blocks,src_offs); | |||
if (!srcptr || srcptr==&nseel_ramalloc_onfail) return dest; | |||
destptr = __NSEEL_RAMAlloc(blocks,dest_offs); | |||
if (!destptr || destptr==&nseel_ramalloc_onfail) return dest; | |||
WDL_fft_complexmul((WDL_FFT_COMPLEX*)destptr,(WDL_FFT_COMPLEX*)srcptr,(len/2)&~1); | |||
return dest; | |||
} | |||
void EEL_fft_register() | |||
{ | |||
WDL_fft_init(); | |||
#if defined(EEL_SUPER_FAST_FFT_REORDERING) | |||
if (!fft_reorder_table_for_bitsize(EEL_FFT_MINBITLEN_REORDER)[0]) | |||
{ | |||
int x; | |||
for (x=EEL_FFT_MINBITLEN_REORDER;x<=EEL_FFT_MAXBITLEN;x++) fft_make_reorder_table(x,fft_reorder_table_for_bitsize(x)); | |||
} | |||
#endif | |||
NSEEL_addfunc_retptr("convolve_c",3,NSEEL_PProc_RAM,&eel_convolve_c); | |||
NSEEL_addfunc_retptr("fft",2,NSEEL_PProc_RAM,&eel_fft); | |||
NSEEL_addfunc_retptr("ifft",2,NSEEL_PProc_RAM,&eel_ifft); | |||
NSEEL_addfunc_retptr("fft_real",2,NSEEL_PProc_RAM,&eel_fft_real); | |||
NSEEL_addfunc_retptr("ifft_real",2,NSEEL_PProc_RAM,&eel_ifft_real); | |||
NSEEL_addfunc_retptr("fft_permute",2,NSEEL_PProc_RAM,&eel_fft_permute); | |||
NSEEL_addfunc_retptr("fft_ipermute",2,NSEEL_PProc_RAM,&eel_ifft_permute); | |||
} | |||
#ifdef EEL_WANT_DOCUMENTATION | |||
static const char *eel_fft_function_reference = | |||
"convolve_c\tdest,src,size\tMultiplies each of size complex pairs in dest by the complex pairs in src. Often used for convolution.\0" | |||
"fft\tbuffer,size\tPerforms a FFT on the data in the local memory buffer at the offset specified by the first parameter. The size of the FFT is specified " | |||
"by the second parameter, which must be 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, or 32768. The outputs are permuted, so if " | |||
"you plan to use them in-order, call fft_permute(buffer, size) before and fft_ipermute(buffer,size) after your in-order use. Your inputs or " | |||
"outputs will need to be scaled down by 1/size, if used.\n" | |||
"Note that fft()/ifft() require real / imaginary input pairs, so a 256 point FFT actually works with 512 items.\n" | |||
"Note that fft()/ifft() must NOT cross a 65,536 item boundary, so be sure to specify the offset accordingly.\0" | |||
"ifft\tbuffer,size\tPerform an inverse FFT. For more information see fft().\0" | |||
"fft_real\tbuffer,size\tPerforms an FFT, but takes size input samples and produces size/2 complex output pairs. Usually used along with fft_permute(size/2). Inputs/outputs will need to be scaled by 0.5/size.\0" | |||
"ifft_real\tbuffer,size\tPerforms an inverse FFT, but takes size/2 complex input pairs and produces size real output values. Usually used along with fft_ipermute(size/2).\0" | |||
"fft_permute\tbuffer,size\tPermute the output of fft() to have bands in-order. See fft() for more information.\0" | |||
"fft_ipermute\tbuffer,size\tPermute the input for ifft(), taking bands from in-order to the order ifft() requires. See fft() for more information.\0" | |||
; | |||
#endif | |||
#endif |
@@ -0,0 +1,262 @@ | |||
#ifndef __EEL_FILES_H__ | |||
#define __EEL_FILES_H__ | |||
// should include eel_strings.h before this, probably | |||
//#define EEL_FILE_OPEN(fn,mode) ((instance)opaque)->OpenFile(fn,mode) | |||
//#define EEL_FILE_GETFP(fp) ((instance)opaque)->GetFileFP(fp) | |||
//#define EEL_FILE_CLOSE(fpindex) ((instance)opaque)->CloseFile(fpindex) | |||
static EEL_F NSEEL_CGEN_CALL _eel_fopen(void *opaque, EEL_F *fn_index, EEL_F *mode_index) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
const char *fn = EEL_STRING_GET_FOR_INDEX(*fn_index,NULL); | |||
const char *mode = EEL_STRING_GET_FOR_INDEX(*mode_index,NULL); | |||
if (!fn || !mode) return 0; | |||
return (EEL_F) EEL_FILE_OPEN(fn,mode); | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fclose(void *opaque, EEL_F *fpp) | |||
{ | |||
EEL_F ret=EEL_FILE_CLOSE((int)*fpp); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (ret < 0) EEL_STRING_DEBUGOUT("fclose(): file handle %f not valid",*fpp); | |||
#endif | |||
return ret; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fgetc(void *opaque, EEL_F *fpp) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (fp) return (EEL_F)fgetc(fp); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fgetc(): file handle %f not valid",*fpp); | |||
#endif | |||
return -1.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_ftell(void *opaque, EEL_F *fpp) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (fp) return (EEL_F)ftell(fp); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("ftell(): file handle %f not valid",*fpp); | |||
#endif | |||
return -1.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fflush(void *opaque, EEL_F *fpp) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (fp) { fflush(fp); return 0.0; } | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fflush(): file handle %f not valid",*fpp); | |||
#endif | |||
return -1.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_feof(void *opaque, EEL_F *fpp) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (fp) return feof(fp) ? 1.0 : 0.0; | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("feof(): file handle %f not valid",*fpp); | |||
#endif | |||
return -1.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fseek(void *opaque, EEL_F *fpp, EEL_F *offset, EEL_F *wh) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (fp) return fseek(fp, (int) *offset, *wh<0 ? SEEK_SET : *wh > 0 ? SEEK_END : SEEK_CUR); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fseek(): file handle %f not valid",*fpp); | |||
#endif | |||
return -1.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fgets(void *opaque, EEL_F *fpp, EEL_F *strOut) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
EEL_STRING_STORAGECLASS *wr=NULL; | |||
EEL_STRING_GET_FOR_WRITE(*strOut, &wr); | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (!fp) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fgets(): file handle %f not valid",*fpp); | |||
#endif | |||
if (wr) wr->Set(""); | |||
return 0.0; | |||
} | |||
char buf[16384]; | |||
buf[0]=0; | |||
fgets(buf,sizeof(buf),fp); | |||
if (wr) | |||
{ | |||
wr->Set(buf); | |||
return (EEL_F)wr->GetLength(); | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fgets: bad destination specifier passed %f, throwing away %d bytes",*strOut, (int)strlen(buf)); | |||
#endif | |||
return (int)strlen(buf); | |||
} | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fread(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen) | |||
{ | |||
int use_len = (int) *flen; | |||
if (use_len < 1) return 0.0; | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
EEL_STRING_STORAGECLASS *wr=NULL; | |||
EEL_STRING_GET_FOR_WRITE(*strOut, &wr); | |||
if (!wr) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fread: bad destination specifier passed %f, not reading %d bytes",*strOut, use_len); | |||
#endif | |||
return -1; | |||
} | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (!fp) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fread(): file handle %f not valid",*fpp); | |||
#endif | |||
if (wr) wr->Set(""); | |||
return 0.0; | |||
} | |||
wr->SetLen(use_len); | |||
if (wr->GetLength() != use_len) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fread: error allocating storage for read of %d bytes", use_len); | |||
#endif | |||
return -1.0; | |||
} | |||
use_len = (int)fread((char *)wr->Get(),1,use_len,fp); | |||
wr->SetLen(use_len > 0 ? use_len : 0, true); | |||
return (EEL_F) use_len; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fwrite(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
int use_len = (int) *flen; | |||
EEL_STRING_STORAGECLASS *wr=NULL; | |||
const char *str=EEL_STRING_GET_FOR_INDEX(*strOut, &wr); | |||
if (!wr && !str) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fwrite: bad source specifier passed %f, not writing %d bytes",*strOut, use_len); | |||
#endif | |||
return -1.0; | |||
} | |||
if (!wr) | |||
{ | |||
const int ssl = (int)strlen(str); | |||
if (use_len < 1 || use_len > ssl) use_len = ssl; | |||
} | |||
else | |||
{ | |||
if (use_len < 1 || use_len > wr->GetLength()) use_len = wr->GetLength(); | |||
} | |||
if (use_len < 1) return 0.0; | |||
FILE *fp = EEL_FILE_GETFP((int)*fpp); | |||
if (!fp) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fwrite(): file handle %f not valid",*fpp); | |||
#endif | |||
return 0.0; | |||
} | |||
return (EEL_F) fwrite(str,1,use_len,fp); | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_fprintf(void *opaque, INT_PTR nparam, EEL_F **parm) | |||
{ | |||
if (opaque && nparam > 1) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
FILE *fp = EEL_FILE_GETFP((int)*(parm[0])); | |||
if (!fp) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fprintf(): file handle %f not valid",parm[0][0]); | |||
#endif | |||
return 0.0; | |||
} | |||
EEL_STRING_STORAGECLASS *wr_src=NULL; | |||
const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parm[1]),&wr_src); | |||
if (fmt) | |||
{ | |||
char buf[16384]; | |||
const int len = eel_format_strings(opaque,fmt,wr_src?(fmt+wr_src->GetLength()) : NULL, buf,(int)sizeof(buf),(int)nparam-2,parm+2); | |||
if (len >= 0) | |||
{ | |||
return (EEL_F) fwrite(buf,1,len,fp); | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fprintf: bad format string %s",fmt); | |||
#endif | |||
} | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("fprintf: bad format specifier passed %f",*(parm[1])); | |||
#endif | |||
} | |||
} | |||
return 0.0; | |||
} | |||
void EEL_file_register() | |||
{ | |||
NSEEL_addfunc_retval("fopen",2,NSEEL_PProc_THIS,&_eel_fopen); | |||
NSEEL_addfunc_retval("fread",3,NSEEL_PProc_THIS,&_eel_fread); | |||
NSEEL_addfunc_retval("fgets",2,NSEEL_PProc_THIS,&_eel_fgets); | |||
NSEEL_addfunc_retval("fgetc",1,NSEEL_PProc_THIS,&_eel_fgetc); | |||
NSEEL_addfunc_retval("fwrite",3,NSEEL_PProc_THIS,&_eel_fwrite); | |||
NSEEL_addfunc_varparm("fprintf",2,NSEEL_PProc_THIS,&_eel_fprintf); | |||
NSEEL_addfunc_retval("fseek",3,NSEEL_PProc_THIS,&_eel_fseek); | |||
NSEEL_addfunc_retval("ftell",1,NSEEL_PProc_THIS,&_eel_ftell); | |||
NSEEL_addfunc_retval("feof",1,NSEEL_PProc_THIS,&_eel_feof); | |||
NSEEL_addfunc_retval("fflush",1,NSEEL_PProc_THIS,&_eel_fflush); | |||
NSEEL_addfunc_retval("fclose",1,NSEEL_PProc_THIS,&_eel_fclose); | |||
} | |||
#ifdef EEL_WANT_DOCUMENTATION | |||
static const char *eel_file_function_reference = | |||
"fopen\t\"fn\",\"mode\"\tOpens a file \"fn\" with mode \"mode\". For read, use \"r\" or \"rb\", write \"w\" or \"wb\". Returns a positive integer on success.\0" | |||
"fclose\tfp\tCloses a file previously opened with fopen().\0" | |||
"fread\tfp,#str,length\tReads from file fp into #str, up to length bytes. Returns actual length read, or negative if error.\0" | |||
"fgets\tfp,#str\tReads a line from file fp into #str. Returns length of #str read.\0" | |||
"fgetc\tfp\tReads a character from file fp, returns -1 if EOF.\0" | |||
"fwrite\tfp,#str,len\tWrites up to len characters of #str to file fp. If len is less than 1, the full contents of #str will be written. Returns the number of bytes written to file.\0" | |||
"fprintf\tfp,\"format\"[,...]\tFormats a string and writes it to file fp. For more information on format specifiers, see sprintf(). Returns bytes written to file.\0" | |||
"fseek\tfp,offset,whence\tSeeks file fp, offset bytes from whence reference. Whence negative specifies start of file, positive whence specifies end of file, and zero whence specifies current file position.\0" | |||
"ftell\tfp\tRetunrs the current file position.\0" | |||
"feof\tfp\tReturns nonzero if the file fp is at the end of file.\0" | |||
"fflush\tfp\tIf file fp is open for writing, flushes out any buffered data to disk.\0" | |||
; | |||
#endif | |||
#endif |
@@ -0,0 +1,104 @@ | |||
/******************************************************************************************* | |||
* imported EEL | |||
******************************************************************************************/ | |||
void (*NSEEL_addfunc_ret_type)(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination); // ret_type=-1 for bool, 1 for value, 0 for ptr | |||
void (*NSEEL_addfunc_varparm_ex)(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination); | |||
NSEEL_VMCTX (*NSEEL_VM_alloc)(); // return a handle | |||
void (*NSEEL_VM_SetGRAM)(NSEEL_VMCTX, void **); | |||
void (*NSEEL_VM_free)(NSEEL_VMCTX ctx); // free when done with a VM and ALL of its code have been freed, as well | |||
void (*NSEEL_VM_SetFunctionTable)(NSEEL_VMCTX, eel_function_table *tab); // use NULL to use default (global) table | |||
EEL_F *(*NSEEL_VM_regvar)(NSEEL_VMCTX ctx, const char *name); // register a variable (before compilation) | |||
void (*NSEEL_VM_SetCustomFuncThis)(NSEEL_VMCTX ctx, void *thisptr); | |||
NSEEL_CODEHANDLE (*NSEEL_code_compile_ex)(NSEEL_VMCTX ctx, const char *code, int lineoffs, int flags); | |||
void (*NSEEL_VM_set_var_resolver)(NSEEL_VMCTX ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx); | |||
char *(*NSEEL_code_getcodeerror)(NSEEL_VMCTX ctx); | |||
void (*NSEEL_code_execute)(NSEEL_CODEHANDLE code); | |||
void (*NSEEL_code_free)(NSEEL_CODEHANDLE code); | |||
EEL_F *(*nseel_int_register_var)(compileContext *ctx, const char *name, int isReg, const char **namePtrOut); | |||
void (*NSEEL_VM_enumallvars)(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx); | |||
EEL_F *(*NSEEL_VM_getramptr)(NSEEL_VMCTX ctx, unsigned int offs, int *validAmt); | |||
void ** (*eel_gmem_attach)(const char *nm, bool is_alloc); | |||
void (*eel_fft_register)(eel_function_table*); | |||
struct eelStringSegmentRec { | |||
struct eelStringSegmentRec *_next; | |||
const char *str_start; // escaped characters, including opening/trailing characters | |||
int str_len; | |||
}; | |||
void (*NSEEL_VM_SetStringFunc)(NSEEL_VMCTX ctx, | |||
EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list), | |||
EEL_F (*onNamedString)(void *caller_this, const char *name)); | |||
// call with NULL to calculate size, or non-null to generate to buffer (returning size used -- will not null terminate, caller responsibility) | |||
int (*nseel_stringsegments_tobuf)(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list); | |||
void *(*NSEEL_PProc_RAM)(void *data, int data_size, struct _compileContext *ctx); | |||
void *(*NSEEL_PProc_THIS)(void *data, int data_size, struct _compileContext *ctx); | |||
void (*eel_enterfp)(int s[2]); | |||
void (*eel_leavefp)(int s[2]); | |||
eel_function_table g_eel_function_table; | |||
#define NSEEL_ADDFUNC_DESTINATION (&g_eel_function_table) | |||
// | |||
// adds a function that returns a value (EEL_F) | |||
#define NSEEL_addfunc_retval(name,np,pproc,fptr) \ | |||
NSEEL_addfunc_ret_type(name,np,1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) | |||
// adds a function that returns a pointer (EEL_F*) | |||
#define NSEEL_addfunc_retptr(name,np,pproc,fptr) \ | |||
NSEEL_addfunc_ret_type(name,np,0,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) | |||
// adds a void or bool function | |||
#define NSEEL_addfunc_retbool(name,np,pproc,fptr) \ | |||
NSEEL_addfunc_ret_type(name,np,-1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) | |||
// adds a function that takes min_np or more parameters (func sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) | |||
#define NSEEL_addfunc_varparm(name, min_np, pproc, fptr) \ | |||
NSEEL_addfunc_varparm_ex(name,min_np,0,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) | |||
// adds a function that takes np parameters via func: sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) | |||
#define NSEEL_addfunc_exparms(name, np, pproc, fptr) \ | |||
NSEEL_addfunc_varparm_ex(name,np,1,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) | |||
class eel_string_context_state; | |||
#define __NS_EELINT_H__ | |||
#define EEL_IMPORT_ALL(IMPORT_FUNC) \ | |||
IMPORT_FUNC(NSEEL_addfunc_ret_type) \ | |||
IMPORT_FUNC(NSEEL_addfunc_varparm_ex) \ | |||
IMPORT_FUNC(NSEEL_VM_free) \ | |||
IMPORT_FUNC(NSEEL_VM_SetFunctionTable) \ | |||
IMPORT_FUNC(NSEEL_VM_regvar) \ | |||
IMPORT_FUNC(NSEEL_VM_SetCustomFuncThis) \ | |||
IMPORT_FUNC(NSEEL_code_compile_ex) \ | |||
IMPORT_FUNC(NSEEL_code_getcodeerror) \ | |||
IMPORT_FUNC(NSEEL_code_execute) \ | |||
IMPORT_FUNC(NSEEL_code_free) \ | |||
IMPORT_FUNC(NSEEL_PProc_THIS) \ | |||
IMPORT_FUNC(NSEEL_PProc_RAM) \ | |||
IMPORT_FUNC(NSEEL_VM_SetStringFunc) \ | |||
IMPORT_FUNC(NSEEL_VM_enumallvars) \ | |||
IMPORT_FUNC(NSEEL_VM_getramptr) \ | |||
IMPORT_FUNC(NSEEL_VM_SetGRAM) \ | |||
IMPORT_FUNC(eel_gmem_attach) \ | |||
IMPORT_FUNC(eel_fft_register) \ | |||
IMPORT_FUNC(nseel_stringsegments_tobuf) \ | |||
IMPORT_FUNC(nseel_int_register_var) \ | |||
IMPORT_FUNC(eel_leavefp) \ | |||
IMPORT_FUNC(eel_enterfp) \ | |||
IMPORT_FUNC(NSEEL_VM_set_var_resolver) \ | |||
IMPORT_FUNC(NSEEL_VM_alloc) /* keep NSEEL_VM_alloc last */ | |||
/******************************************************************************************* | |||
* END of imported EEL | |||
******************************************************************************************/ |
@@ -0,0 +1,782 @@ | |||
#ifndef _EEL_MDCT_H_ | |||
#define _EEL_MDCT_H_ | |||
#include "ns-eel-int.h" | |||
#ifdef _WIN32 | |||
#include <malloc.h> | |||
#endif | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#include <math.h> | |||
#define EEL_DCT_MINBITLEN 5 | |||
#define EEL_DCT_MAXBITLEN 12 | |||
#define PI 3.1415926535897932384626433832795 | |||
typedef struct { | |||
int n; | |||
int log2n; | |||
EEL_F *trig; | |||
int *bitrev; | |||
EEL_F scale; | |||
EEL_F *window; | |||
} mdct_lookup; | |||
static void mdct(EEL_F *in, EEL_F *out, int len) | |||
{ | |||
int k; | |||
EEL_F pioverlen = PI * 0.5 / (EEL_F)len; | |||
for (k = 0; k < len / 2; k ++) | |||
{ | |||
int i; | |||
EEL_F d = 0.0; | |||
for (i = 0; i < len; i ++) | |||
{ | |||
d += in[i] * cos(pioverlen * (2.0 * i + 1.0 + len * 0.5) * (2.0 * k + 1.0)); | |||
} | |||
out[k] = (EEL_F)d; | |||
} | |||
} | |||
static void imdct(EEL_F *in, EEL_F *out, int len) | |||
{ | |||
int k; | |||
EEL_F fourovern = 4.0 / (EEL_F)len; | |||
EEL_F pioverlen = PI * 0.5 / (EEL_F)len; | |||
for (k = 0; k < len; k ++) | |||
{ | |||
int i; | |||
EEL_F d = 0.0; | |||
for (i = 0; i < len / 2; i ++) | |||
{ | |||
d += in[i] * cos(pioverlen * (2.0 * k + 1.0 + len * 0.5) * (2 * i + 1.0)); | |||
} | |||
out[k] = (EEL_F)(d * fourovern); | |||
} | |||
} | |||
// MDCT/iMDCT borrowed from Vorbis, thanks xiph! | |||
#define cPI3_8 .38268343236508977175 | |||
#define cPI2_8 .70710678118654752441 | |||
#define cPI1_8 .92387953251128675613 | |||
#define FLOAT_CONV(x) ((EEL_F) ( x )) | |||
#define MULT_NORM(x) (x) | |||
#define HALVE(x) ((x)*.5f) | |||
/* 8 point butterfly (in place, 4 register) */ | |||
static void mdct_butterfly_8(EEL_F *x) { | |||
EEL_F r0 = x[6] + x[2]; | |||
EEL_F r1 = x[6] - x[2]; | |||
EEL_F r2 = x[4] + x[0]; | |||
EEL_F r3 = x[4] - x[0]; | |||
x[6] = r0 + r2; | |||
x[4] = r0 - r2; | |||
r0 = x[5] - x[1]; | |||
r2 = x[7] - x[3]; | |||
x[0] = r1 + r0; | |||
x[2] = r1 - r0; | |||
r0 = x[5] + x[1]; | |||
r1 = x[7] + x[3]; | |||
x[3] = r2 + r3; | |||
x[1] = r2 - r3; | |||
x[7] = r1 + r0; | |||
x[5] = r1 - r0; | |||
} | |||
/* 16 point butterfly (in place, 4 register) */ | |||
static void mdct_butterfly_16(EEL_F *x) { | |||
EEL_F r0 = x[1] - x[9]; | |||
EEL_F r1 = x[0] - x[8]; | |||
x[8] += x[0]; | |||
x[9] += x[1]; | |||
x[0] = MULT_NORM((r0 + r1) * cPI2_8); | |||
x[1] = MULT_NORM((r0 - r1) * cPI2_8); | |||
r0 = x[3] - x[11]; | |||
r1 = x[10] - x[2]; | |||
x[10] += x[2]; | |||
x[11] += x[3]; | |||
x[2] = r0; | |||
x[3] = r1; | |||
r0 = x[12] - x[4]; | |||
r1 = x[13] - x[5]; | |||
x[12] += x[4]; | |||
x[13] += x[5]; | |||
x[4] = MULT_NORM((r0 - r1) * cPI2_8); | |||
x[5] = MULT_NORM((r0 + r1) * cPI2_8); | |||
r0 = x[14] - x[6]; | |||
r1 = x[15] - x[7]; | |||
x[14] += x[6]; | |||
x[15] += x[7]; | |||
x[6] = r0; | |||
x[7] = r1; | |||
mdct_butterfly_8(x); | |||
mdct_butterfly_8(x + 8); | |||
} | |||
/* 32 point butterfly (in place, 4 register) */ | |||
static void mdct_butterfly_32(EEL_F *x) { | |||
EEL_F r0 = x[30] - x[14]; | |||
EEL_F r1 = x[31] - x[15]; | |||
x[30] += x[14]; | |||
x[31] += x[15]; | |||
x[14] = r0; | |||
x[15] = r1; | |||
r0 = x[28] - x[12]; | |||
r1 = x[29] - x[13]; | |||
x[28] += x[12]; | |||
x[29] += x[13]; | |||
x[12] = MULT_NORM( r0 * cPI1_8 - r1 * cPI3_8 ); | |||
x[13] = MULT_NORM( r0 * cPI3_8 + r1 * cPI1_8 ); | |||
r0 = x[26] - x[10]; | |||
r1 = x[27] - x[11]; | |||
x[26] += x[10]; | |||
x[27] += x[11]; | |||
x[10] = MULT_NORM(( r0 - r1 ) * cPI2_8); | |||
x[11] = MULT_NORM(( r0 + r1 ) * cPI2_8); | |||
r0 = x[24] - x[8]; | |||
r1 = x[25] - x[9]; | |||
x[24] += x[8]; | |||
x[25] += x[9]; | |||
x[8] = MULT_NORM( r0 * cPI3_8 - r1 * cPI1_8 ); | |||
x[9] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); | |||
r0 = x[22] - x[6]; | |||
r1 = x[7] - x[23]; | |||
x[22] += x[6]; | |||
x[23] += x[7]; | |||
x[6] = r1; | |||
x[7] = r0; | |||
r0 = x[4] - x[20]; | |||
r1 = x[5] - x[21]; | |||
x[20] += x[4]; | |||
x[21] += x[5]; | |||
x[4] = MULT_NORM( r1 * cPI1_8 + r0 * cPI3_8 ); | |||
x[5] = MULT_NORM( r1 * cPI3_8 - r0 * cPI1_8 ); | |||
r0 = x[2] - x[18]; | |||
r1 = x[3] - x[19]; | |||
x[18] += x[2]; | |||
x[19] += x[3]; | |||
x[2] = MULT_NORM(( r1 + r0 ) * cPI2_8); | |||
x[3] = MULT_NORM(( r1 - r0 ) * cPI2_8); | |||
r0 = x[0] - x[16]; | |||
r1 = x[1] - x[17]; | |||
x[16] += x[0]; | |||
x[17] += x[1]; | |||
x[0] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 ); | |||
x[1] = MULT_NORM( r1 * cPI1_8 - r0 * cPI3_8 ); | |||
mdct_butterfly_16(x); | |||
mdct_butterfly_16(x + 16); | |||
} | |||
/* N point first stage butterfly (in place, 2 register) */ | |||
static void mdct_butterfly_first(EEL_F *T, | |||
EEL_F *x, | |||
int points) { | |||
EEL_F *x1 = x + points - 8; | |||
EEL_F *x2 = x + (points >> 1) - 8; | |||
EEL_F r0; | |||
EEL_F r1; | |||
do { | |||
r0 = x1[6] - x2[6]; | |||
r1 = x1[7] - x2[7]; | |||
x1[6] += x2[6]; | |||
x1[7] += x2[7]; | |||
x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
r0 = x1[4] - x2[4]; | |||
r1 = x1[5] - x2[5]; | |||
x1[4] += x2[4]; | |||
x1[5] += x2[5]; | |||
x2[4] = MULT_NORM(r1 * T[5] + r0 * T[4]); | |||
x2[5] = MULT_NORM(r1 * T[4] - r0 * T[5]); | |||
r0 = x1[2] - x2[2]; | |||
r1 = x1[3] - x2[3]; | |||
x1[2] += x2[2]; | |||
x1[3] += x2[3]; | |||
x2[2] = MULT_NORM(r1 * T[9] + r0 * T[8]); | |||
x2[3] = MULT_NORM(r1 * T[8] - r0 * T[9]); | |||
r0 = x1[0] - x2[0]; | |||
r1 = x1[1] - x2[1]; | |||
x1[0] += x2[0]; | |||
x1[1] += x2[1]; | |||
x2[0] = MULT_NORM(r1 * T[13] + r0 * T[12]); | |||
x2[1] = MULT_NORM(r1 * T[12] - r0 * T[13]); | |||
x1 -= 8; | |||
x2 -= 8; | |||
T += 16; | |||
} while(x2 >= x); | |||
} | |||
/* N/stage point generic N stage butterfly (in place, 2 register) */ | |||
static void mdct_butterfly_generic(EEL_F *T, | |||
EEL_F *x, | |||
int points, | |||
int trigint) { | |||
EEL_F *x1 = x + points - 8; | |||
EEL_F *x2 = x + (points >> 1) - 8; | |||
EEL_F r0; | |||
EEL_F r1; | |||
do { | |||
r0 = x1[6] - x2[6]; | |||
r1 = x1[7] - x2[7]; | |||
x1[6] += x2[6]; | |||
x1[7] += x2[7]; | |||
x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
T += trigint; | |||
r0 = x1[4] - x2[4]; | |||
r1 = x1[5] - x2[5]; | |||
x1[4] += x2[4]; | |||
x1[5] += x2[5]; | |||
x2[4] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
x2[5] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
T += trigint; | |||
r0 = x1[2] - x2[2]; | |||
r1 = x1[3] - x2[3]; | |||
x1[2] += x2[2]; | |||
x1[3] += x2[3]; | |||
x2[2] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
x2[3] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
T += trigint; | |||
r0 = x1[0] - x2[0]; | |||
r1 = x1[1] - x2[1]; | |||
x1[0] += x2[0]; | |||
x1[1] += x2[1]; | |||
x2[0] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
x2[1] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
T += trigint; | |||
x1 -= 8; | |||
x2 -= 8; | |||
} while(x2 >= x); | |||
} | |||
static void mdct_butterflies(mdct_lookup *init, | |||
EEL_F *x, | |||
int points) { | |||
EEL_F *T = init->trig; | |||
int stages = init->log2n - 5; | |||
int i, j; | |||
if(--stages > 0) { | |||
mdct_butterfly_first(T, x, points); | |||
} | |||
for(i = 1; --stages > 0; i++) { | |||
for(j = 0; j < (1 << i); j++) | |||
mdct_butterfly_generic(T, x + (points >> i)*j, points >> i, 4 << i); | |||
} | |||
for(j = 0; j < points; j += 32) | |||
mdct_butterfly_32(x + j); | |||
} | |||
static void mdct_bitreverse(mdct_lookup *init, | |||
EEL_F *x) { | |||
int n = init->n; | |||
int *bit = init->bitrev; | |||
EEL_F *w0 = x; | |||
EEL_F *w1 = x = w0 + (n >> 1); | |||
EEL_F *T = init->trig + n; | |||
do { | |||
EEL_F *x0 = x + bit[0]; | |||
EEL_F *x1 = x + bit[1]; | |||
EEL_F r0 = x0[1] - x1[1]; | |||
EEL_F r1 = x0[0] + x1[0]; | |||
EEL_F r2 = MULT_NORM(r1 * T[0] + r0 * T[1]); | |||
EEL_F r3 = MULT_NORM(r1 * T[1] - r0 * T[0]); | |||
w1 -= 4; | |||
r0 = HALVE(x0[1] + x1[1]); | |||
r1 = HALVE(x0[0] - x1[0]); | |||
w0[0] = r0 + r2; | |||
w1[2] = r0 - r2; | |||
w0[1] = r1 + r3; | |||
w1[3] = r3 - r1; | |||
x0 = x + bit[2]; | |||
x1 = x + bit[3]; | |||
r0 = x0[1] - x1[1]; | |||
r1 = x0[0] + x1[0]; | |||
r2 = MULT_NORM(r1 * T[2] + r0 * T[3]); | |||
r3 = MULT_NORM(r1 * T[3] - r0 * T[2]); | |||
r0 = HALVE(x0[1] + x1[1]); | |||
r1 = HALVE(x0[0] - x1[0]); | |||
w0[2] = r0 + r2; | |||
w1[0] = r0 - r2; | |||
w0[3] = r1 + r3; | |||
w1[1] = r3 - r1; | |||
T += 4; | |||
bit += 4; | |||
w0 += 4; | |||
} while(w0 < w1); | |||
} | |||
static void megabuf_mdct_apply_window(void *init, EEL_F *inbuf, EEL_F *outbuf) | |||
{ | |||
mdct_lookup *p = (mdct_lookup *)init; | |||
EEL_F *w; | |||
int cnt; | |||
if (!p) return; | |||
w = p->window; | |||
if (!w) return; | |||
cnt = p->n / 2; | |||
while (cnt--) *outbuf++ = *inbuf++ * *w++; | |||
cnt = p->n / 2; | |||
while (cnt--) *outbuf++ = *inbuf++ * *--w; | |||
} | |||
static void *megabuf_mdct_init(int n) { | |||
mdct_lookup *lookup = (mdct_lookup *)calloc(sizeof(mdct_lookup), 1); | |||
int i; | |||
EEL_F c = (PI / (EEL_F) n); | |||
int *bitrev; | |||
EEL_F *T; | |||
int n2, log2n; | |||
if (!lookup) return 0; | |||
lookup->n = n; | |||
lookup->window = (EEL_F *)calloc(sizeof(EEL_F), n / 2); | |||
if (!lookup->window) return lookup; | |||
for (i = 0; i < n / 2; i ++) | |||
{ | |||
lookup->window[i] = sin(c * (i + 0.5)); | |||
} | |||
if (n <= 32) return lookup; | |||
bitrev = (int*)calloc(sizeof(int), (n / 4)); | |||
lookup->bitrev = bitrev; | |||
if (!bitrev) return lookup; | |||
T = (EEL_F*)calloc(sizeof(EEL_F), (n + n / 4)); | |||
lookup->trig = T; | |||
if (!T) return lookup; | |||
n2 = n >> 1; | |||
log2n = lookup->log2n = (int)(log((double)n) / log(2.0) + 0.5); | |||
/* trig lookups... */ | |||
for(i = 0; i < n / 4; i++) { | |||
T[i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i))); | |||
T[i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i))); | |||
T[n2 + i * 2] = FLOAT_CONV(cos((PI / (2 * n)) * (2 * i + 1))); | |||
T[n2 + i * 2 + 1] = FLOAT_CONV(sin((PI / (2 * n)) * (2 * i + 1))); | |||
} | |||
for(i = 0; i < n / 8; i++) { | |||
T[n + i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i + 2)) * .5); | |||
T[n + i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i + 2)) * .5); | |||
} | |||
/* bitreverse lookup... */ | |||
{ | |||
int mask = (1 << (log2n - 1)) - 1, j; | |||
int msb = 1 << (log2n - 2); | |||
for(i = 0; i < n / 8; i++) { | |||
int acc = 0; | |||
for(j = 0; msb >> j; j++) | |||
if((msb >> j)&i)acc |= 1 << j; | |||
bitrev[i * 2] = ((~acc)&mask) - 1; | |||
bitrev[i * 2 + 1] = acc; | |||
} | |||
} | |||
lookup->scale = FLOAT_CONV(4.f / n); | |||
return lookup; | |||
} | |||
static void megabuf_mdct_backward(void *init, EEL_F *in, EEL_F *out) { | |||
mdct_lookup *lookup = (mdct_lookup *)init; | |||
int n, n2, n4; | |||
EEL_F *iX, *oX, *T; | |||
if (!lookup) return; | |||
n = lookup->n; | |||
if (n <= 32 || !lookup->bitrev || !lookup->trig) | |||
{ | |||
imdct(in, out, n); | |||
return; | |||
} | |||
n2 = n >> 1; | |||
n4 = n >> 2; | |||
/* rotate */ | |||
iX = in + n2 - 7; | |||
oX = out + n2 + n4; | |||
T = lookup->trig + n4; | |||
do { | |||
oX -= 4; | |||
oX[0] = MULT_NORM(-iX[2] * T[3] - iX[0] * T[2]); | |||
oX[1] = MULT_NORM (iX[0] * T[3] - iX[2] * T[2]); | |||
oX[2] = MULT_NORM(-iX[6] * T[1] - iX[4] * T[0]); | |||
oX[3] = MULT_NORM (iX[4] * T[1] - iX[6] * T[0]); | |||
iX -= 8; | |||
T += 4; | |||
} while(iX >= in); | |||
iX = in + n2 - 8; | |||
oX = out + n2 + n4; | |||
T = lookup->trig + n4; | |||
do { | |||
T -= 4; | |||
oX[0] = MULT_NORM (iX[4] * T[3] + iX[6] * T[2]); | |||
oX[1] = MULT_NORM (iX[4] * T[2] - iX[6] * T[3]); | |||
oX[2] = MULT_NORM (iX[0] * T[1] + iX[2] * T[0]); | |||
oX[3] = MULT_NORM (iX[0] * T[0] - iX[2] * T[1]); | |||
iX -= 8; | |||
oX += 4; | |||
} while(iX >= in); | |||
mdct_butterflies(lookup, out + n2, n2); | |||
mdct_bitreverse(lookup, out); | |||
/* roatate + window */ | |||
{ | |||
EEL_F *oX1 = out + n2 + n4; | |||
EEL_F *oX2 = out + n2 + n4; | |||
iX = out; | |||
T = lookup->trig + n2; | |||
do { | |||
oX1 -= 4; | |||
oX1[3] = MULT_NORM (iX[0] * T[1] - iX[1] * T[0]); | |||
oX2[0] = -MULT_NORM (iX[0] * T[0] + iX[1] * T[1]); | |||
oX1[2] = MULT_NORM (iX[2] * T[3] - iX[3] * T[2]); | |||
oX2[1] = -MULT_NORM (iX[2] * T[2] + iX[3] * T[3]); | |||
oX1[1] = MULT_NORM (iX[4] * T[5] - iX[5] * T[4]); | |||
oX2[2] = -MULT_NORM (iX[4] * T[4] + iX[5] * T[5]); | |||
oX1[0] = MULT_NORM (iX[6] * T[7] - iX[7] * T[6]); | |||
oX2[3] = -MULT_NORM (iX[6] * T[6] + iX[7] * T[7]); | |||
oX2 += 4; | |||
iX += 8; | |||
T += 8; | |||
} while(iX < oX1); | |||
iX = out + n2 + n4; | |||
oX1 = out + n4; | |||
oX2 = oX1; | |||
do { | |||
oX1 -= 4; | |||
iX -= 4; | |||
oX2[0] = -(oX1[3] = iX[3]); | |||
oX2[1] = -(oX1[2] = iX[2]); | |||
oX2[2] = -(oX1[1] = iX[1]); | |||
oX2[3] = -(oX1[0] = iX[0]); | |||
oX2 += 4; | |||
} while(oX2 < iX); | |||
iX = out + n2 + n4; | |||
oX1 = out + n2 + n4; | |||
oX2 = out + n2; | |||
do { | |||
oX1 -= 4; | |||
oX1[0] = iX[3]; | |||
oX1[1] = iX[2]; | |||
oX1[2] = iX[1]; | |||
oX1[3] = iX[0]; | |||
iX += 4; | |||
} while(oX1 > oX2); | |||
} | |||
} | |||
static void megabuf_mdct_forward(void *init, EEL_F *in, EEL_F *out) { | |||
mdct_lookup *lookup = (mdct_lookup *)init; | |||
int n, n2, n4, n8; | |||
EEL_F *w, *w2; | |||
if (!lookup) return; | |||
n = lookup->n; | |||
if (n <= 32 || !lookup->bitrev || !lookup->trig) | |||
{ | |||
mdct(in, out, n); | |||
return; | |||
} | |||
n2 = n >> 1; | |||
n4 = n >> 2; | |||
n8 = n >> 3; | |||
EEL_F oldw[1<<EEL_DCT_MAXBITLEN]; | |||
w = oldw; | |||
w2 = w + n2; | |||
/* rotate */ | |||
/* window + rotate + step 1 */ | |||
{ | |||
EEL_F r0; | |||
EEL_F r1; | |||
EEL_F *x0 = in + n2 + n4; | |||
EEL_F *x1 = x0 + 1; | |||
EEL_F *T = lookup->trig + n2; | |||
int i = 0; | |||
for(i = 0; i < n8; i += 2) { | |||
x0 -= 4; | |||
T -= 2; | |||
r0 = x0[2] + x1[0]; | |||
r1 = x0[0] + x1[2]; | |||
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
x1 += 4; | |||
} | |||
x1 = in + 1; | |||
for(; i < n2 - n8; i += 2) { | |||
T -= 2; | |||
x0 -= 4; | |||
r0 = x0[2] - x1[0]; | |||
r1 = x0[0] - x1[2]; | |||
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
x1 += 4; | |||
} | |||
x0 = in + n; | |||
for(; i < n2; i += 2) { | |||
T -= 2; | |||
x0 -= 4; | |||
r0 = -x0[2] - x1[0]; | |||
r1 = -x0[0] - x1[2]; | |||
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]); | |||
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]); | |||
x1 += 4; | |||
} | |||
mdct_butterflies(lookup, w + n2, n2); | |||
mdct_bitreverse(lookup, w); | |||
/* roatate + window */ | |||
T = lookup->trig + n2; | |||
x0 = out + n2; | |||
for(i = 0; i < n4; i++) { | |||
x0--; | |||
out[i] = MULT_NORM((w[0] * T[0] + w[1] * T[1]) * lookup->scale); | |||
x0[0] = MULT_NORM((w[0] * T[1] - w[1] * T[0]) * lookup->scale); | |||
w += 2; | |||
T += 2; | |||
} | |||
} | |||
} | |||
#if 0 | |||
static void dct(EEL_F *in, EEL_F *out, int len) | |||
{ | |||
int k; | |||
EEL_F wk = sqrt(2.0 / len); | |||
EEL_F overtwolen = 0.5 / (EEL_F)len; | |||
for (k = 0; k < len; k ++) | |||
{ | |||
int n; | |||
EEL_F d = 0.0; | |||
for (n = 0; n < len; n ++) | |||
{ | |||
int an = n + 1; | |||
d += in[n] * cos(PI * (2.0 * n + 1.0) * (EEL_F)k * overtwolen); | |||
} | |||
if (!k) d /= sqrt(len); | |||
else d *= wk; | |||
out[k] = (EEL_F)d; | |||
} | |||
} | |||
static void idct(EEL_F *in, EEL_F *out, int len) | |||
{ | |||
int n; | |||
EEL_F dd0 = 1.0 / sqrt(len); | |||
EEL_F dd1 = sqrt(2.0 / len); | |||
EEL_F overtwolen = 0.5 / len; | |||
for (n = 0; n < len; n ++) | |||
{ | |||
int k; | |||
EEL_F d = 0.0; | |||
for (k = 0; k < len; k ++) | |||
{ | |||
EEL_F dd; | |||
if (!k) dd = dd0 * in[k]; | |||
else dd = dd1 * in[k]; | |||
d += dd * cos(PI * (2.0 * n + 1.0) * k * overtwolen); | |||
} | |||
out[n] = (EEL_F)d; | |||
} | |||
} | |||
#endif | |||
// 0 is megabuf blocks | |||
// 1 is need_free flag | |||
static EEL_F * NSEEL_CGEN_CALL mdct_func(int dir, EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
int l = (int)(*length + 0.0001); | |||
int offs = (int)(*start + 0.0001); | |||
int bitl = 0; | |||
int ilen; | |||
int bidx; | |||
EEL_F *ptr; | |||
while (l > 1 && bitl < EEL_DCT_MAXBITLEN) | |||
{ | |||
bitl++; | |||
l >>= 1; | |||
} | |||
if (bitl < EEL_DCT_MINBITLEN) | |||
{ | |||
return start; | |||
} | |||
ilen = 1 << bitl; | |||
bidx = bitl - EEL_DCT_MINBITLEN; | |||
// check to make sure we don't cross a boundary | |||
if (offs / NSEEL_RAM_ITEMSPERBLOCK != (offs + ilen * 2 - 1) / NSEEL_RAM_ITEMSPERBLOCK) | |||
{ | |||
return start; | |||
} | |||
ptr = __NSEEL_RAMAlloc(blocks, offs); | |||
if (!ptr || ptr == &nseel_ramalloc_onfail) | |||
{ | |||
return start; | |||
} | |||
if (ilen > 1) | |||
{ | |||
static void *mdct_ctxs[1 + EEL_DCT_MAXBITLEN - EEL_DCT_MINBITLEN]; | |||
if (!mdct_ctxs[bidx]) | |||
{ | |||
NSEEL_HOSTSTUB_EnterMutex(); | |||
if (!mdct_ctxs[bidx]) | |||
mdct_ctxs[bidx] = megabuf_mdct_init(ilen); | |||
NSEEL_HOSTSTUB_LeaveMutex(); | |||
} | |||
if (mdct_ctxs[bidx]) | |||
{ | |||
EEL_F buf[1 << EEL_DCT_MAXBITLEN]; | |||
if (dir < 0) | |||
{ | |||
megabuf_mdct_backward(mdct_ctxs[bidx], ptr, buf); | |||
megabuf_mdct_apply_window(mdct_ctxs[bidx], buf, ptr); | |||
} | |||
else | |||
{ | |||
megabuf_mdct_apply_window(mdct_ctxs[bidx], ptr, buf); | |||
megabuf_mdct_forward(mdct_ctxs[bidx], buf, ptr); | |||
} | |||
} | |||
} | |||
return start; | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL megabuf_mdct(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return mdct_func(0, blocks, start, length); | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL megabuf_imdct(EEL_F **blocks, EEL_F *start, EEL_F *length) | |||
{ | |||
return mdct_func(-1, blocks, start, length); | |||
} | |||
void EEL_mdct_register() | |||
{ | |||
NSEEL_addfunc_retptr("mdct", 2, NSEEL_PProc_RAM, &megabuf_mdct); | |||
NSEEL_addfunc_retptr("imdct", 2, NSEEL_PProc_RAM, &megabuf_imdct); | |||
} | |||
#ifdef EEL_WANT_DOCUMENTATION | |||
static const char *eel_mdct_function_reference = | |||
"mdct\tbuffer,length\tPerforms a windowed modified DCT, taking length inputs and producing length/2 outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0" | |||
"imdct\tbuffer,length\tPerforms a windowed inverse modified DCT, taking length/2 inputs and producing length outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0" | |||
; | |||
#endif | |||
#endif |
@@ -0,0 +1,73 @@ | |||
#ifndef _EEL_MISC_H_ | |||
#define _EEL_MISC_H_ | |||
#ifndef _WIN32 | |||
#include <sys/time.h> | |||
#endif | |||
#include <time.h> | |||
// some generic EEL functions for things like time | |||
#ifndef EEL_MISC_NO_SLEEP | |||
static EEL_F NSEEL_CGEN_CALL _eel_sleep(void *opaque, EEL_F *amt) | |||
{ | |||
if (*amt >= 0.0) | |||
{ | |||
#ifdef _WIN32 | |||
if (*amt > 30000000.0) Sleep(30000000); | |||
else Sleep((DWORD)(*amt+0.5)); | |||
#else | |||
if (*amt > 30000000.0) usleep(((useconds_t)30000000)*1000); | |||
else usleep((useconds_t)(*amt*1000.0+0.5)); | |||
#endif | |||
} | |||
return 0.0; | |||
} | |||
#endif | |||
static EEL_F * NSEEL_CGEN_CALL _eel_time(void *opaque, EEL_F *v) | |||
{ | |||
*v = (EEL_F) time(NULL); | |||
return v; | |||
} | |||
static EEL_F * NSEEL_CGEN_CALL _eel_time_precise(void *opaque, EEL_F *v) | |||
{ | |||
#ifdef _WIN32 | |||
LARGE_INTEGER freq,now; | |||
QueryPerformanceFrequency(&freq); | |||
QueryPerformanceCounter(&now); | |||
*v = (double)now.QuadPart / (double)freq.QuadPart; | |||
// *v = (EEL_F)timeGetTime() * 0.001; | |||
#else | |||
struct timeval tm={0,}; | |||
gettimeofday(&tm,NULL); | |||
*v = tm.tv_sec + tm.tv_usec*0.000001; | |||
#endif | |||
return v; | |||
} | |||
void EEL_misc_register() | |||
{ | |||
#ifndef EEL_MISC_NO_SLEEP | |||
NSEEL_addfunc_retval("sleep",1,NSEEL_PProc_THIS,&_eel_sleep); | |||
#endif | |||
NSEEL_addfunc_retptr("time",1,NSEEL_PProc_THIS,&_eel_time); | |||
NSEEL_addfunc_retptr("time_precise",1,NSEEL_PProc_THIS,&_eel_time_precise); | |||
} | |||
#ifdef EEL_WANT_DOCUMENTATION | |||
static const char *eel_misc_function_reference = | |||
#ifndef EEL_MISC_NO_SLEEP | |||
"sleep\tms\tYields the CPU for the millisecond count specified, calling Sleep() on Windows or usleep() on other platforms.\0" | |||
#endif | |||
"time\t[&val]\tSets the parameter (or a temporary buffer if omitted) to the number of seconds since January 1, 1970, and returns a reference to that value. " | |||
"The granularity of the value returned is 1 second.\0" | |||
"time_precise\t[&val]\tSets the parameter (or a temporary buffer if omitted) to a system-local timestamp in seconds, and returns a reference to that value. " | |||
"The granularity of the value returned is system defined (but generally significantly smaller than one second).\0" | |||
; | |||
#endif | |||
#endif |
@@ -0,0 +1,564 @@ | |||
#ifndef _EEL_NET_H_ | |||
#define _EEL_NET_H_ | |||
// x = tcp_listen(port[,interface, connected_ip_out]) poll this, returns connection id > 0, or <0 on error, or 0 if no new connect -- interface only valid on first call (or after tcp_listen_end(port)) | |||
// tcp_listen_end(port); | |||
// connection = tcp_connect(host, port[, block]) // connection id > 0 on ok | |||
// tcp_set_block(connection, block?) | |||
// tcp_close(connection) | |||
// tcp_send(connection, string[, length]) // can return 0 if block, -1 if error, otherwise returns length sent | |||
// tcp_recv(connection, string[, maxlength]) // 0 on nothing, -1 on error, otherwise returns length recv'd | |||
// need: | |||
// #define EEL_NET_GET_CONTEXT(opaque) (((sInst *)opaque)->m_net_state) | |||
// you must pass a JNL_AsyncDNS object to eel_net_state to support nonblocking connect with DNS resolution, otherwise DNS will block | |||
// #define EEL_NET_NO_SYNC_DNS -- never ever call gethostbyname() synchronously, may disable DNS for blocking connect, or if a JNL_IAsyncDNS is not provided. | |||
#ifndef EEL_NET_MAXSEND | |||
#define EEL_NET_MAXSEND (EEL_STRING_MAXUSERSTRING_LENGTH_HINT+4096) | |||
#endif | |||
#include "../jnetlib/netinc.h" | |||
#define JNL_NO_IMPLEMENTATION | |||
#include "../jnetlib/asyncdns.h" | |||
class eel_net_state | |||
{ | |||
public: | |||
enum { STATE_FREE=0, STATE_RESOLVING, STATE_CONNECTED, STATE_ERR }; | |||
enum { CONNECTION_ID_BASE=0x110000 }; | |||
eel_net_state(int max_con, JNL_IAsyncDNS *dns); | |||
~eel_net_state(); | |||
struct connection_state { | |||
char *hostname; // set during resolve only | |||
SOCKET sock; | |||
int state; // STATE_RESOLVING... | |||
int port; | |||
bool blockmode; | |||
}; | |||
WDL_TypedBuf<connection_state> m_cons; | |||
WDL_IntKeyedArray<SOCKET> m_listens; | |||
JNL_IAsyncDNS *m_dns; | |||
EEL_F onConnect(char *hostNameOwned, int port, int block); | |||
EEL_F onClose(void *opaque, EEL_F handle); | |||
EEL_F set_block(void *opaque, EEL_F handle, bool block); | |||
EEL_F onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut); | |||
int __run_connect(connection_state *cs, unsigned int ip); | |||
int __run(connection_state *cs); | |||
int do_send(void *opaque, EEL_F h, const char *src, int len); | |||
int do_recv(void *opaque, EEL_F h, char *buf, int maxlen); | |||
#ifdef _WIN32 | |||
bool m_had_socketlib_init; | |||
#endif | |||
}; | |||
eel_net_state::eel_net_state(int max_con, JNL_IAsyncDNS *dns) | |||
{ | |||
#ifdef _WIN32 | |||
m_had_socketlib_init=false; | |||
#endif | |||
m_cons.Resize(max_con); | |||
int x; | |||
for (x=0;x<m_cons.GetSize();x++) | |||
{ | |||
m_cons.Get()[x].state = STATE_FREE; | |||
m_cons.Get()[x].sock = INVALID_SOCKET; | |||
m_cons.Get()[x].hostname = NULL; | |||
} | |||
m_dns=dns; | |||
} | |||
eel_net_state::~eel_net_state() | |||
{ | |||
int x; | |||
for (x=0;x<m_cons.GetSize();x++) | |||
{ | |||
SOCKET s=m_cons.Get()[x].sock; | |||
if (s != INVALID_SOCKET) | |||
{ | |||
shutdown(s,SHUT_RDWR); | |||
closesocket(s); | |||
} | |||
free(m_cons.Get()[x].hostname); | |||
} | |||
for (x=0;x<m_listens.GetSize();x++) | |||
{ | |||
SOCKET s=m_listens.Enumerate(x); | |||
shutdown(s, SHUT_RDWR); | |||
closesocket(s); | |||
} | |||
} | |||
EEL_F eel_net_state::onConnect(char *hostNameOwned, int port, int block) | |||
{ | |||
int x; | |||
#ifdef _WIN32 | |||
if (!m_had_socketlib_init) | |||
{ | |||
m_had_socketlib_init=1; | |||
WSADATA wsaData; | |||
WSAStartup(MAKEWORD(1, 1), &wsaData); | |||
} | |||
#endif | |||
for(x=0;x<m_cons.GetSize();x++) | |||
{ | |||
connection_state *s=m_cons.Get()+x; | |||
if (s->state == STATE_FREE) | |||
{ | |||
unsigned int ip=inet_addr(hostNameOwned); | |||
if (m_dns && ip == INADDR_NONE && !block) | |||
{ | |||
const int r=m_dns->resolve(hostNameOwned,&ip); | |||
if (r<0) break; // error! | |||
if (r>0) ip = INADDR_NONE; | |||
} | |||
#ifndef EEL_NET_NO_SYNC_DNS | |||
else if (ip == INADDR_NONE) | |||
{ | |||
struct hostent *he = gethostbyname(hostNameOwned); | |||
if (he) ip = *(int *)he->h_addr; | |||
} | |||
#endif | |||
if (hostNameOwned || ip != INADDR_NONE) | |||
{ | |||
if (ip != INADDR_NONE) | |||
{ | |||
free(hostNameOwned); | |||
hostNameOwned=NULL; | |||
} | |||
s->state = STATE_RESOLVING; | |||
s->hostname = hostNameOwned; | |||
s->blockmode = !!block; | |||
s->port = port; | |||
if (hostNameOwned || __run_connect(s,ip)) return x + CONNECTION_ID_BASE; | |||
s->state=STATE_FREE; | |||
s->hostname=NULL; | |||
} | |||
break; | |||
} | |||
} | |||
free(hostNameOwned); | |||
return -1; | |||
} | |||
EEL_F eel_net_state::onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut) | |||
{ | |||
const int port = (int) handle; | |||
if (port < 1 || port > 65535) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_listen(%d): invalid port specified, will never succeed",port); | |||
#endif | |||
return 0.0; | |||
} | |||
#ifdef _WIN32 | |||
if (!m_had_socketlib_init) | |||
{ | |||
m_had_socketlib_init=1; | |||
WSADATA wsaData; | |||
WSAStartup(MAKEWORD(1, 1), &wsaData); | |||
} | |||
#endif | |||
SOCKET *sockptr = m_listens.GetPtr(port); | |||
if (mode<0) | |||
{ | |||
if (!sockptr) return -1.0; | |||
SOCKET ss=*sockptr; | |||
m_listens.Delete(port); | |||
if (ss != INVALID_SOCKET) | |||
{ | |||
shutdown(ss, SHUT_RDWR); | |||
closesocket(ss); | |||
} | |||
return 0.0; | |||
} | |||
if (!sockptr) | |||
{ | |||
struct sockaddr_in sin; | |||
memset((char *) &sin, 0,sizeof(sin)); | |||
if (ifStr) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
const char *fn = EEL_STRING_GET_FOR_INDEX(*ifStr,NULL); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (!fn) EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for second parameter (interface)",port,*ifStr); | |||
#endif | |||
if (fn && *fn) sin.sin_addr.s_addr=inet_addr(fn); | |||
} | |||
if (!sin.sin_addr.s_addr || sin.sin_addr.s_addr==INADDR_NONE) sin.sin_addr.s_addr = INADDR_ANY; | |||
sin.sin_family = AF_INET; | |||
sin.sin_port = htons( (short) port ); | |||
SOCKET sock = socket(AF_INET,SOCK_STREAM,0); | |||
if (sock != INVALID_SOCKET) | |||
{ | |||
SET_SOCK_DEFAULTS(sock); | |||
SET_SOCK_BLOCK(sock,0); | |||
if (bind(sock,(struct sockaddr *)&sin,sizeof(sin)) || listen(sock,8)==-1) | |||
{ | |||
shutdown(sock, SHUT_RDWR); | |||
closesocket(sock); | |||
sock=INVALID_SOCKET; | |||
} | |||
} | |||
#ifdef EEL_STRING_DEBUGOUT | |||
//if (sock == INVALID_SOCKET) EEL_STRING_DEBUGOUT("tcp_listen(%d): failed listening on port",port); | |||
// we report -1 to the caller, no need to error message | |||
#endif | |||
m_listens.Insert(port,sock); | |||
sockptr = m_listens.GetPtr(port); | |||
} | |||
if (!sockptr || *sockptr == INVALID_SOCKET) return -1; | |||
struct sockaddr_in saddr; | |||
socklen_t length = sizeof(struct sockaddr_in); | |||
SOCKET newsock = accept(*sockptr, (struct sockaddr *) &saddr, &length); | |||
if (newsock == INVALID_SOCKET) | |||
{ | |||
return 0; // nothing to report here | |||
} | |||
SET_SOCK_DEFAULTS(newsock); | |||
int x; | |||
for(x=0;x<m_cons.GetSize();x++) | |||
{ | |||
connection_state *cs=m_cons.Get()+x; | |||
if (cs->state == STATE_FREE) | |||
{ | |||
cs->state=STATE_CONNECTED; | |||
free(cs->hostname); | |||
cs->hostname=NULL; | |||
cs->sock = newsock; | |||
cs->blockmode=true; | |||
cs->port=0; | |||
if (ipOut) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
WDL_FastString *ws=NULL; | |||
EEL_STRING_GET_FOR_WRITE(*ipOut,&ws); | |||
if (ws) | |||
{ | |||
const unsigned int a = ntohl(saddr.sin_addr.s_addr); | |||
ws->SetFormatted(128,"%d.%d.%d.%d",(a>>24)&0xff,(a>>16)&0xff,(a>>8)&0xff,a&0xff); | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for third parameter (IP-out)",port,*ipOut); | |||
#endif | |||
} | |||
} | |||
return x + CONNECTION_ID_BASE; | |||
} | |||
} | |||
shutdown(newsock, SHUT_RDWR); | |||
closesocket(newsock); | |||
return -1; | |||
} | |||
int eel_net_state::__run_connect(connection_state *cs, unsigned int ip) | |||
{ | |||
SOCKET s=socket(AF_INET,SOCK_STREAM,0); | |||
if (s == INVALID_SOCKET) return 0; | |||
SET_SOCK_DEFAULTS(s); | |||
if (!cs->blockmode) SET_SOCK_BLOCK(s,0); | |||
struct sockaddr_in sa={0,}; | |||
sa.sin_family=AF_INET; | |||
sa.sin_addr.s_addr = ip; | |||
sa.sin_port = htons(cs->port); | |||
if (!connect(s,(struct sockaddr *)&sa,16) || (!cs->blockmode && JNL_ERRNO == JNL_EINPROGRESS)) | |||
{ | |||
cs->state = STATE_CONNECTED; | |||
cs->sock = s; | |||
return 1; | |||
} | |||
shutdown(s, SHUT_RDWR); | |||
closesocket(s); | |||
return 0; | |||
} | |||
int eel_net_state::__run(connection_state *cs) | |||
{ | |||
if (cs->sock != INVALID_SOCKET) return 0; | |||
if (!cs->hostname) return -1; | |||
unsigned int ip=INADDR_NONE; | |||
const int r=m_dns ? m_dns->resolve(cs->hostname,&ip) : -1; | |||
if (r>0) return 0; | |||
free(cs->hostname); | |||
cs->hostname=NULL; | |||
if (r<0 || !__run_connect(cs,ip)) | |||
{ | |||
cs->state = STATE_ERR; | |||
return -1; | |||
} | |||
return 0; | |||
} | |||
int eel_net_state::do_recv(void *opaque, EEL_F h, char *buf, int maxlen) | |||
{ | |||
const int idx=(int)h-CONNECTION_ID_BASE; | |||
if (idx>=0 && idx<m_cons.GetSize()) | |||
{ | |||
connection_state *s=m_cons.Get()+idx; | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (s->sock == INVALID_SOCKET && !s->hostname) | |||
EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is not open",h); | |||
#endif | |||
if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0; | |||
if (maxlen == 0) return 0; | |||
const int rv=(int)recv(s->sock,buf,maxlen,0); | |||
if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0; | |||
if (!rv) return -1; // TCP, 0=connection terminated | |||
return rv; | |||
} | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is out of range",h); | |||
#endif | |||
return -1; | |||
} | |||
int eel_net_state::do_send(void *opaque, EEL_F h, const char *src, int len) | |||
{ | |||
const int idx=(int)h-CONNECTION_ID_BASE; | |||
if (idx>=0 && idx<m_cons.GetSize()) | |||
{ | |||
connection_state *s=m_cons.Get()+idx; | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (s->sock == INVALID_SOCKET && !s->hostname) | |||
EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f is not open",h); | |||
#endif | |||
if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0; | |||
const int rv=(int)send(s->sock,src,len,0); | |||
if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0; | |||
return rv; | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f out of range",h); | |||
#endif | |||
} | |||
return -1; | |||
} | |||
EEL_F eel_net_state::set_block(void *opaque, EEL_F handle, bool block) | |||
{ | |||
int idx=(int)handle-CONNECTION_ID_BASE; | |||
if (idx>=0 && idx<m_cons.GetSize()) | |||
{ | |||
connection_state *s=m_cons.Get()+idx; | |||
if (s->blockmode != block) | |||
{ | |||
s->blockmode=block; | |||
if (s->sock != INVALID_SOCKET) | |||
{ | |||
SET_SOCK_BLOCK(s->sock,(block?1:0)); | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (!s->hostname) EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f is not open",handle); | |||
#endif | |||
} | |||
return 1; | |||
} | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f out of range",handle); | |||
#endif | |||
} | |||
return 0; | |||
} | |||
EEL_F eel_net_state::onClose(void *opaque, EEL_F handle) | |||
{ | |||
int idx=(int)handle-CONNECTION_ID_BASE; | |||
if (idx>=0 && idx<m_cons.GetSize()) | |||
{ | |||
connection_state *s=m_cons.Get()+idx; | |||
const bool hadhn = !!s->hostname; | |||
free(s->hostname); | |||
s->hostname = NULL; | |||
s->state = STATE_ERR; | |||
if (s->sock != INVALID_SOCKET) | |||
{ | |||
shutdown(s->sock,SHUT_RDWR); | |||
closesocket(s->sock); | |||
s->sock = INVALID_SOCKET; | |||
return 1.0; | |||
} | |||
else if (!hadhn) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is not open",handle); | |||
#endif | |||
} | |||
} | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is out of range",handle); | |||
#endif | |||
} | |||
return 0.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_connect(void *opaque, INT_PTR np, EEL_F **parms) | |||
{ | |||
eel_net_state *ctx; | |||
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) | |||
{ | |||
char *dest=NULL; | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
const char *fn = EEL_STRING_GET_FOR_INDEX(parms[0][0],NULL); | |||
if (fn) dest=strdup(fn); | |||
else | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("tcp_connect(): host string identifier %f invalid",parms[0][0]); | |||
#endif | |||
} | |||
} | |||
if (dest) return ctx->onConnect(dest, (int) (parms[1][0]+0.5), np < 3 || parms[2][0] >= 0.5); | |||
} | |||
return -1.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_set_block(void *opaque, EEL_F *handle, EEL_F *bl) | |||
{ | |||
eel_net_state *ctx; | |||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->set_block(opaque,*handle, *bl >= 0.5); | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_close(void *opaque, EEL_F *handle) | |||
{ | |||
eel_net_state *ctx; | |||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onClose(opaque,*handle); | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_recv(void *opaque, INT_PTR np, EEL_F **parms) | |||
{ | |||
eel_net_state *ctx; | |||
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) | |||
{ | |||
char buf[EEL_STRING_MAXUSERSTRING_LENGTH_HINT]; | |||
int ml = np > 2 ? (int)parms[2][0] : 4096; | |||
if (ml < 0 || ml > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) ml = EEL_STRING_MAXUSERSTRING_LENGTH_HINT; | |||
ml=ctx->do_recv(opaque,parms[0][0],buf,ml); | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
WDL_FastString *ws=NULL; | |||
EEL_STRING_GET_FOR_WRITE(parms[1][0],&ws); | |||
if (ws) | |||
{ | |||
if (ml<=0) ws->Set(""); | |||
else ws->SetRaw(buf,ml); | |||
} | |||
} | |||
return ml; | |||
} | |||
return -1; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_send(void *opaque, INT_PTR np, EEL_F **parms) | |||
{ | |||
eel_net_state *ctx; | |||
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) | |||
{ | |||
char buf[EEL_NET_MAXSEND]; | |||
int l; | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
WDL_FastString *ws=NULL; | |||
const char *fn = EEL_STRING_GET_FOR_INDEX(parms[1][0],&ws); | |||
l = ws ? ws->GetLength() : (int) strlen(fn); | |||
if (np > 2) | |||
{ | |||
int al=(int)parms[2][0]; | |||
if (al<0) al=0; | |||
if (al<l) l=al; | |||
} | |||
if (l > 0) memcpy(buf,fn,l); | |||
} | |||
if (l>0) return ctx->do_send(opaque,parms[0][0],buf,l); | |||
return 0; | |||
} | |||
return -1; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen(void *opaque, INT_PTR np, EEL_F **parms) | |||
{ | |||
eel_net_state *ctx; | |||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,parms[0][0],1,np>1?parms[1]:NULL,np>2?parms[2]:NULL); | |||
return 0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen_end(void *opaque, EEL_F *handle) | |||
{ | |||
eel_net_state *ctx; | |||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,*handle,-1,NULL,NULL); | |||
return 0; | |||
} | |||
void EEL_tcp_register() | |||
{ | |||
NSEEL_addfunc_varparm("tcp_listen",1,NSEEL_PProc_THIS,&_eel_tcp_listen); | |||
NSEEL_addfunc_retval("tcp_listen_end",1,NSEEL_PProc_THIS,&_eel_tcp_listen_end); | |||
NSEEL_addfunc_varparm("tcp_connect",2,NSEEL_PProc_THIS,&_eel_tcp_connect); | |||
NSEEL_addfunc_varparm("tcp_send",2,NSEEL_PProc_THIS,&_eel_tcp_send); | |||
NSEEL_addfunc_varparm("tcp_recv",2,NSEEL_PProc_THIS,&_eel_tcp_recv); | |||
NSEEL_addfunc_retval("tcp_set_block",2,NSEEL_PProc_THIS,&_eel_tcp_set_block); | |||
NSEEL_addfunc_retval("tcp_close",1,NSEEL_PProc_THIS,&_eel_tcp_close); | |||
} | |||
#ifdef EEL_WANT_DOCUMENTATION | |||
const char *eel_net_function_reference = | |||
"tcp_listen\tport[,\"interface\",#ip_out]\tListens on port specified. Returns less than 0 if could not listen, 0 if no new connection available, or greater than 0 (as a TCP connection ID) if a new connection was made. If a connection made and #ip_out specified, it will be set to the remote IP. interface can be empty for all interfaces, otherwise an interface IP as a string.\0" | |||
"tcp_listen_end\tport\tEnds listening on port specified.\0" | |||
"tcp_connect\t\"address\",port[,block]\tCreate a new TCP connection to address:port. If block is specified and 0, connection will be made nonblocking. Returns TCP connection ID greater than 0 on success.\0" | |||
"tcp_send\tconnection,\"str\"[,len]\tSends a string to connection. Returns -1 on error, 0 if connection is non-blocking and would block, otherwise returns length sent. If len is specified and not less than 1, only the first len bytes of the string parameter will be sent.\0" | |||
"tcp_recv\tconnection,#str[,maxlen]\tReceives data from a connection to #str. If maxlen is specified, no more than maxlen bytes will be received. If non-blocking, 0 will be returned if would block. Returns less than 0 if error.\0" | |||
"tcp_set_block\tconnection,block\tSets whether a connection blocks.\0" | |||
"tcp_close\tconnection\tCloses a TCP connection created by tcp_listen() or tcp_connect().\0" | |||
; | |||
#endif | |||
#endif |
@@ -0,0 +1,776 @@ | |||
#ifndef _WIN32 | |||
#include <unistd.h> | |||
#ifndef EELSCRIPT_NO_LICE | |||
#include "../swell/swell.h" | |||
#endif | |||
#endif | |||
#include "../wdltypes.h" | |||
#include "../ptrlist.h" | |||
#include "../wdlstring.h" | |||
#include "../assocarray.h" | |||
#include "../queue.h" | |||
#include "../win32_utf8.h" | |||
#include "ns-eel.h" | |||
#ifndef EELSCRIPT_MAX_FILE_HANDLES | |||
#define EELSCRIPT_MAX_FILE_HANDLES 512 | |||
#endif | |||
#ifndef EELSCRIPT_FILE_HANDLE_INDEX_BASE | |||
#define EELSCRIPT_FILE_HANDLE_INDEX_BASE 1000000 | |||
#endif | |||
#ifndef EEL_STRING_MAXUSERSTRING_LENGTH_HINT | |||
#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT (1<<16) // 64KB per string max | |||
#endif | |||
#ifndef EEL_STRING_MAX_USER_STRINGS | |||
#define EEL_STRING_MAX_USER_STRINGS 32768 | |||
#endif | |||
#ifndef EEL_STRING_LITERAL_BASE | |||
#define EEL_STRING_LITERAL_BASE 2000000 | |||
#endif | |||
#ifndef EELSCRIPT_LICE_MAX_IMAGES | |||
#define EELSCRIPT_LICE_MAX_IMAGES 1024 | |||
#endif | |||
#ifndef EELSCRIPT_LICE_MAX_FONTS | |||
#define EELSCRIPT_LICE_MAX_FONTS 128 | |||
#endif | |||
#ifndef EELSCRIPT_NET_MAXCON | |||
#define EELSCRIPT_NET_MAXCON 4096 | |||
#endif | |||
#ifndef EELSCRIPT_LICE_CLASSNAME | |||
#define EELSCRIPT_LICE_CLASSNAME "eelscript_gfx" | |||
#endif | |||
// #define EELSCRIPT_NO_NET | |||
// #define EELSCRIPT_NO_LICE | |||
// #define EELSCRIPT_NO_FILE | |||
// #define EELSCRIPT_NO_FFT | |||
// #define EELSCRIPT_NO_MDCT | |||
// #define EELSCRIPT_NO_EVAL | |||
class eel_string_context_state; | |||
#ifndef EELSCRIPT_NO_NET | |||
class eel_net_state; | |||
#endif | |||
#ifndef EELSCRIPT_NO_LICE | |||
class eel_lice_state; | |||
#endif | |||
class eelScriptInst { | |||
public: | |||
static int init(); | |||
eelScriptInst(); | |||
virtual ~eelScriptInst(); | |||
NSEEL_CODEHANDLE compile_code(const char *code, const char **err); | |||
int runcode(const char *code, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec); | |||
int loadfile(const char *fn, const char *callerfn, bool allowstdin); | |||
NSEEL_VMCTX m_vm; | |||
WDL_PtrList<void> m_code_freelist; | |||
#ifndef EELSCRIPT_NO_FILE | |||
FILE *m_handles[EELSCRIPT_MAX_FILE_HANDLES]; | |||
virtual EEL_F OpenFile(const char *fn, const char *mode) | |||
{ | |||
if (!*fn || !*mode) return 0.0; | |||
#ifndef EELSCRIPT_NO_STDIO | |||
if (!strcmp(fn,"stdin")) return 1; | |||
if (!strcmp(fn,"stdout")) return 2; | |||
if (!strcmp(fn,"stderr")) return 3; | |||
#endif | |||
WDL_FastString fnstr(fn); | |||
if (!translateFilename(&fnstr,mode)) return 0.0; | |||
int x; | |||
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES && m_handles[x];x++); | |||
if (x>= EELSCRIPT_MAX_FILE_HANDLES) return 0.0; | |||
FILE *fp = fopenUTF8(fnstr.Get(),mode); | |||
if (!fp) return 0.0; | |||
m_handles[x]=fp; | |||
return x + EELSCRIPT_FILE_HANDLE_INDEX_BASE; | |||
} | |||
virtual EEL_F CloseFile(int fp_idx) | |||
{ | |||
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE; | |||
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES && m_handles[fp_idx]) | |||
{ | |||
fclose(m_handles[fp_idx]); | |||
m_handles[fp_idx]=0; | |||
return 0.0; | |||
} | |||
return -1.0; | |||
} | |||
virtual FILE *GetFileFP(int fp_idx) | |||
{ | |||
#ifndef EELSCRIPT_NO_STDIO | |||
if (fp_idx==1) return stdin; | |||
if (fp_idx==2) return stdout; | |||
if (fp_idx==3) return stderr; | |||
#endif | |||
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE; | |||
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES) return m_handles[fp_idx]; | |||
return NULL; | |||
} | |||
#endif | |||
virtual bool translateFilename(WDL_FastString *fs, const char *mode) { return true; } | |||
virtual bool GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite); | |||
eel_string_context_state *m_string_context; | |||
#ifndef EELSCRIPT_NO_NET | |||
eel_net_state *m_net_state; | |||
#endif | |||
#ifndef EELSCRIPT_NO_LICE | |||
eel_lice_state *m_gfx_state; | |||
#endif | |||
#ifndef EELSCRIPT_NO_EVAL | |||
struct evalCacheEnt { | |||
char *str; | |||
NSEEL_CODEHANDLE ch; | |||
}; | |||
int m_eval_depth; | |||
WDL_TypedBuf<evalCacheEnt> m_eval_cache; | |||
virtual char *evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch); | |||
virtual void evalCacheDispose(char *key, NSEEL_CODEHANDLE ch); | |||
WDL_Queue m_defer_eval, m_atexit_eval; | |||
void runCodeQ(WDL_Queue *q, const char *fname); | |||
void runAtExitCode() | |||
{ | |||
runCodeQ(&m_atexit_eval,"atexit"); | |||
m_atexit_eval.Clear(); // make sure nothing gets added in atexit(), in case the user called runAtExitCode before destroying | |||
} | |||
#endif | |||
virtual bool run_deferred(); // requires eval support to be useful | |||
virtual bool has_deferred(); | |||
WDL_StringKeyedArray<bool> m_loaded_fnlist; // imported file list (to avoid repeats) | |||
}; | |||
//#define EEL_STRINGS_MUTABLE_LITERALS | |||
//#define EEL_STRING_WANT_MUTEX | |||
#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((eelScriptInst *)opaque)->m_string_context) | |||
#ifndef EEL_STRING_STDOUT_WRITE | |||
#ifndef EELSCRIPT_NO_STDIO | |||
#define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); } | |||
#endif | |||
#endif | |||
#include "eel_strings.h" | |||
#include "eel_misc.h" | |||
#ifndef EELSCRIPT_NO_FILE | |||
#define EEL_FILE_OPEN(fn,mode) ((eelScriptInst*)opaque)->OpenFile(fn,mode) | |||
#define EEL_FILE_GETFP(fp) ((eelScriptInst*)opaque)->GetFileFP(fp) | |||
#define EEL_FILE_CLOSE(fpindex) ((eelScriptInst*)opaque)->CloseFile(fpindex) | |||
#include "eel_files.h" | |||
#endif | |||
#ifndef EELSCRIPT_NO_FFT | |||
#include "eel_fft.h" | |||
#endif | |||
#ifndef EELSCRIPT_NO_MDCT | |||
#include "eel_mdct.h" | |||
#endif | |||
#ifndef EELSCRIPT_NO_NET | |||
#define EEL_NET_GET_CONTEXT(opaque) (((eelScriptInst *)opaque)->m_net_state) | |||
#include "eel_net.h" | |||
#endif | |||
#ifndef EELSCRIPT_NO_LICE | |||
#ifndef EEL_LICE_WANT_STANDALONE | |||
#define EEL_LICE_WANT_STANDALONE | |||
#endif | |||
#ifndef EELSCRIPT_LICE_NOUPDATE | |||
#define EEL_LICE_WANT_STANDALONE_UPDATE // gfx_update() which runs message pump and updates screen etc | |||
#endif | |||
#define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) (((eelScriptInst*)opaque)->GetFilenameForParameter(idx,fs,p)) | |||
#define EEL_LICE_GET_CONTEXT(opaque) ((opaque) ? (((eelScriptInst *)opaque)->m_gfx_state) : NULL) | |||
#include "eel_lice.h" | |||
#endif | |||
#ifndef EELSCRIPT_NO_EVAL | |||
#define EEL_EVAL_GET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheGet(str,&(ch)) | |||
#define EEL_EVAL_SET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheDispose(str,ch) | |||
#define EEL_EVAL_GET_VMCTX(opaque) (((eelScriptInst *)opaque)->m_vm) | |||
#define EEL_EVAL_SCOPE_ENTER (((eelScriptInst *)opaque)->m_eval_depth < 3 ? \ | |||
++((eelScriptInst *)opaque)->m_eval_depth : 0) | |||
#define EEL_EVAL_SCOPE_LEAVE ((eelScriptInst *)opaque)->m_eval_depth--; | |||
#include "eel_eval.h" | |||
static EEL_F NSEEL_CGEN_CALL _eel_defer(void *opaque, EEL_F *s) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL); | |||
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow defer(0) etc | |||
{ | |||
eelScriptInst *inst = (eelScriptInst *)opaque; | |||
if (inst->m_defer_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT) | |||
{ | |||
inst->m_defer_eval.Add(str,strlen(str)+1); | |||
return 1.0; | |||
} | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("defer(): too much defer() code already added, ignoring"); | |||
#endif | |||
} | |||
#ifdef EEL_STRING_DEBUGOUT | |||
else if (!str) | |||
{ | |||
EEL_STRING_DEBUGOUT("defer(): invalid string identifier specified %f",*s); | |||
} | |||
else if (*s < EEL_STRING_MAX_USER_STRINGS) | |||
{ | |||
EEL_STRING_DEBUGOUT("defer(): user string identifier %f specified but not allowed",*s); | |||
} | |||
#endif | |||
return 0.0; | |||
} | |||
static EEL_F NSEEL_CGEN_CALL _eel_atexit(void *opaque, EEL_F *s) | |||
{ | |||
EEL_STRING_MUTEXLOCK_SCOPE | |||
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL); | |||
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow atexit(0) etc | |||
{ | |||
eelScriptInst *inst = (eelScriptInst *)opaque; | |||
if (inst->m_atexit_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT) | |||
{ | |||
inst->m_atexit_eval.Add(str,strlen(str)+1); | |||
return 1.0; | |||
} | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("atexit(): too much atexit() code already added, ignoring"); | |||
#endif | |||
} | |||
#ifdef EEL_STRING_DEBUGOUT | |||
else if (!str) | |||
{ | |||
EEL_STRING_DEBUGOUT("atexit(): invalid string identifier specified %f",*s); | |||
} | |||
else if (*s < EEL_STRING_MAX_USER_STRINGS) | |||
{ | |||
EEL_STRING_DEBUGOUT("atexit(): user string identifier %f specified but not allowed",*s); | |||
} | |||
#endif | |||
return 0.0; | |||
} | |||
#endif | |||
#define opaque ((void *)this) | |||
eelScriptInst::eelScriptInst() : m_loaded_fnlist(false) | |||
{ | |||
#ifndef EELSCRIPT_NO_FILE | |||
memset(m_handles,0,sizeof(m_handles)); | |||
#endif | |||
m_vm = NSEEL_VM_alloc(); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (!m_vm) EEL_STRING_DEBUGOUT("NSEEL_VM_alloc(): failed"); | |||
#endif | |||
NSEEL_VM_SetCustomFuncThis(m_vm,this); | |||
#ifdef NSEEL_ADDFUNC_DESTINATION | |||
NSEEL_VM_SetFunctionTable(m_vm,NSEEL_ADDFUNC_DESTINATION); | |||
#endif | |||
m_string_context = new eel_string_context_state; | |||
eel_string_initvm(m_vm); | |||
#ifndef EELSCRIPT_NO_NET | |||
m_net_state = new eel_net_state(EELSCRIPT_NET_MAXCON,NULL); | |||
#endif | |||
#ifndef EELSCRIPT_NO_LICE | |||
m_gfx_state = new eel_lice_state(m_vm,this,EELSCRIPT_LICE_MAX_IMAGES,EELSCRIPT_LICE_MAX_FONTS); | |||
m_gfx_state->resetVarsToStock(); | |||
#endif | |||
#ifndef EELSCRIPT_NO_EVAL | |||
m_eval_depth=0; | |||
#endif | |||
} | |||
eelScriptInst::~eelScriptInst() | |||
{ | |||
#ifndef EELSCRIPT_NO_EVAL | |||
if (m_atexit_eval.GetSize()>0) runAtExitCode(); | |||
#endif | |||
int x; | |||
m_code_freelist.Empty((void (*)(void *))NSEEL_code_free); | |||
#ifndef EELSCRIPT_NO_EVAL | |||
for (x=0;x<m_eval_cache.GetSize();x++) | |||
{ | |||
free(m_eval_cache.Get()[x].str); | |||
NSEEL_code_free(m_eval_cache.Get()[x].ch); | |||
} | |||
#endif | |||
if (m_vm) NSEEL_VM_free(m_vm); | |||
#ifndef EELSCRIPT_NO_FILE | |||
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES;x++) | |||
{ | |||
if (m_handles[x]) fclose(m_handles[x]); | |||
m_handles[x]=0; | |||
} | |||
#endif | |||
delete m_string_context; | |||
#ifndef EELSCRIPT_NO_NET | |||
delete m_net_state; | |||
#endif | |||
#ifndef EELSCRIPT_NO_LICE | |||
delete m_gfx_state; | |||
#endif | |||
} | |||
bool eelScriptInst::GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite) | |||
{ | |||
const char *fmt = EEL_STRING_GET_FOR_INDEX(idx,NULL); | |||
if (!fmt) return false; | |||
fs->Set(fmt); | |||
return translateFilename(fs,iswrite?"w":"r"); | |||
} | |||
NSEEL_CODEHANDLE eelScriptInst::compile_code(const char *code, const char **err) | |||
{ | |||
if (!m_vm) | |||
{ | |||
*err = "EEL VM not initialized"; | |||
return NULL; | |||
} | |||
NSEEL_CODEHANDLE ch = NSEEL_code_compile_ex(m_vm, code, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS); | |||
if (ch) | |||
{ | |||
m_string_context->update_named_vars(m_vm); | |||
m_code_freelist.Add((void*)ch); | |||
return ch; | |||
} | |||
*err = NSEEL_code_getcodeerror(m_vm); | |||
return NULL; | |||
} | |||
int eelScriptInst::runcode(const char *codeptr, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec) | |||
{ | |||
if (m_vm) | |||
{ | |||
NSEEL_CODEHANDLE code = NSEEL_code_compile_ex(m_vm,codeptr,0,canfree ? 0 : NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS); | |||
if (code) m_string_context->update_named_vars(m_vm); | |||
char *err; | |||
if (!code && (err=NSEEL_code_getcodeerror(m_vm))) | |||
{ | |||
if (!ignoreEndOfInputChk && (NSEEL_code_geterror_flag(m_vm)&1)) return 1; | |||
if (showerr) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (showerr==2) | |||
{ | |||
EEL_STRING_DEBUGOUT("Warning: %s:%s",WDL_get_filepart(showerrfn),err); | |||
} | |||
else | |||
{ | |||
EEL_STRING_DEBUGOUT("%s:%s",WDL_get_filepart(showerrfn),err); | |||
} | |||
#endif | |||
} | |||
return -1; | |||
} | |||
else | |||
{ | |||
if (code) | |||
{ | |||
#ifdef EELSCRIPT_DO_DISASSEMBLE | |||
codeHandleType *p = (codeHandleType*)code; | |||
char buf[512]; | |||
buf[0]=0; | |||
#ifdef _WIN32 | |||
GetTempPath(sizeof(buf)-64,buf); | |||
lstrcatn(buf,"jsfx-out",sizeof(buf)); | |||
#else | |||
lstrcpyn_safe(buf,"/tmp/jsfx-out",sizeof(buf)); | |||
#endif | |||
FILE *fp = fopenUTF8(buf,"wb"); | |||
if (fp) | |||
{ | |||
fwrite(p->code,1,p->code_size,fp); | |||
fclose(fp); | |||
char buf2[2048]; | |||
#ifdef _WIN32 | |||
snprintf(buf2,sizeof(buf2),"disasm \"%s\"",buf); | |||
#else | |||
#ifdef __aarch64__ | |||
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -maarch64 \"%s\"",buf); | |||
#elif defined(__arm__) | |||
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -m arm \"%s\"",buf); | |||
#elif defined(__LP64__) | |||
#ifdef __APPLE__ | |||
snprintf(buf2,sizeof(buf2),"distorm3 --b64 \"%s\"",buf); | |||
#else | |||
snprintf(buf2,sizeof(buf2),"objdump -D -b binary -m i386:x86-64 \"%s\"",buf); | |||
#endif | |||
#else | |||
snprintf(buf2,sizeof(buf2),"distorm3 --b32 \"%s\"",buf); | |||
#endif | |||
#endif | |||
system(buf2); | |||
} | |||
#endif | |||
if (doExec) NSEEL_code_execute(code); | |||
if (canfree) NSEEL_code_free(code); | |||
else m_code_freelist.Add((void*)code); | |||
} | |||
return 0; | |||
} | |||
} | |||
return -1; | |||
} | |||
FILE *eelscript_resolvePath(WDL_FastString &usefn, const char *fn, const char *callerfn) | |||
{ | |||
// resolve path relative to current | |||
int x; | |||
bool had_abs=false; | |||
for (x=0;x<2; x ++) | |||
{ | |||
#ifdef _WIN32 | |||
if (!x && ((fn[0] == '\\' && fn[1] == '\\') || (fn[0] && fn[1] == ':'))) | |||
#else | |||
if (!x && fn[0] == '/') | |||
#endif | |||
{ | |||
usefn.Set(fn); | |||
had_abs=true; | |||
} | |||
else | |||
{ | |||
const char *fnu = fn; | |||
if (x) | |||
{ | |||
while (*fnu) fnu++; | |||
while (fnu >= fn && *fnu != '\\' && *fnu != '/') fnu--; | |||
if (fnu < fn) break; | |||
fnu++; | |||
} | |||
usefn.Set(callerfn); | |||
int l=usefn.GetLength(); | |||
while (l > 0 && usefn.Get()[l-1] != '\\' && usefn.Get()[l-1] != '/') l--; | |||
if (l > 0) | |||
{ | |||
usefn.SetLen(l); | |||
usefn.Append(fnu); | |||
} | |||
else | |||
{ | |||
usefn.Set(fnu); | |||
} | |||
int last_slash_pos=-1; | |||
for (l = 0; l < usefn.GetLength(); l ++) | |||
{ | |||
if (usefn.Get()[l] == '/' || usefn.Get()[l] == '\\') | |||
{ | |||
if (usefn.Get()[l+1] == '.' && usefn.Get()[l+2] == '.' && | |||
(usefn.Get()[l+3] == '/' || usefn.Get()[l+3] == '\\')) | |||
{ | |||
if (last_slash_pos >= 0) | |||
usefn.DeleteSub(last_slash_pos, l+3-last_slash_pos); | |||
else | |||
usefn.DeleteSub(0,l+3+1); | |||
} | |||
else | |||
{ | |||
last_slash_pos=l; | |||
} | |||
} | |||
// take currentfn, remove filename part, add fnu | |||
} | |||
} | |||
FILE *fp = fopenUTF8(usefn.Get(),"r"); | |||
if (fp) return fp; | |||
} | |||
if (had_abs) usefn.Set(fn); | |||
return NULL; | |||
} | |||
int eelScriptInst::loadfile(const char *fn, const char *callerfn, bool allowstdin) | |||
{ | |||
WDL_FastString usefn; | |||
FILE *fp = NULL; | |||
if (!strcmp(fn,"-")) | |||
{ | |||
if (callerfn) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
EEL_STRING_DEBUGOUT("@import: can't import \"-\" (stdin)"); | |||
#endif | |||
return -1; | |||
} | |||
if (allowstdin) | |||
{ | |||
fp = stdin; | |||
fn = "(stdin)"; | |||
} | |||
} | |||
else if (!callerfn) | |||
{ | |||
fp = fopenUTF8(fn,"r"); | |||
if (fp) m_loaded_fnlist.Insert(fn,true); | |||
} | |||
else | |||
{ | |||
fp = eelscript_resolvePath(usefn,fn,callerfn); | |||
if (fp) | |||
{ | |||
if (m_loaded_fnlist.Get(usefn.Get())) | |||
{ | |||
fclose(fp); | |||
return 0; | |||
} | |||
m_loaded_fnlist.Insert(usefn.Get(),true); | |||
fn = usefn.Get(); | |||
} | |||
} | |||
if (!fp) | |||
{ | |||
#ifdef EEL_STRING_DEBUGOUT | |||
if (callerfn) | |||
EEL_STRING_DEBUGOUT("Warning: @import could not open '%s'",fn); | |||
else | |||
EEL_STRING_DEBUGOUT("Error opening %s",fn); | |||
#endif | |||
return -1; | |||
} | |||
WDL_FastString code; | |||
char line[4096]; | |||
for (;;) | |||
{ | |||
line[0]=0; | |||
fgets(line,sizeof(line),fp); | |||
if (!line[0]) break; | |||
if (!strnicmp(line,"@import",7) && isspace((unsigned char)line[7])) | |||
{ | |||
char *p=line+7; | |||
while (isspace((unsigned char)*p)) p++; | |||
char *ep=p; | |||
while (*ep) ep++; | |||
while (ep>p && isspace((unsigned char)ep[-1])) ep--; | |||
*ep=0; | |||
if (*p) loadfile(p,fn,false); | |||
} | |||
else | |||
{ | |||
code.Append(line); | |||
} | |||
} | |||
if (fp != stdin) fclose(fp); | |||
return runcode(code.Get(),callerfn ? 2 : 1, fn,false,true,!callerfn); | |||
} | |||
char *eelScriptInst::evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch) | |||
{ | |||
// should mutex protect if multiple threads access this eelScriptInst context | |||
int x=m_eval_cache.GetSize(); | |||
while (--x >= 0) | |||
{ | |||
char *ret; | |||
if (!strcmp(ret=m_eval_cache.Get()[x].str, str)) | |||
{ | |||
*ch = m_eval_cache.Get()[x].ch; | |||
m_eval_cache.Delete(x); | |||
return ret; | |||
} | |||
} | |||
return NULL; | |||
} | |||
void eelScriptInst::evalCacheDispose(char *key, NSEEL_CODEHANDLE ch) | |||
{ | |||
// should mutex protect if multiple threads access this eelScriptInst context | |||
evalCacheEnt ecc; | |||
ecc.str= key; | |||
ecc.ch = ch; | |||
if (m_eval_cache.GetSize() > 1024) | |||
{ | |||
NSEEL_code_free(m_eval_cache.Get()->ch); | |||
free(m_eval_cache.Get()->str); | |||
m_eval_cache.Delete(0); | |||
} | |||
m_eval_cache.Add(ecc); | |||
} | |||
int eelScriptInst::init() | |||
{ | |||
EEL_string_register(); | |||
#ifndef EELSCRIPT_NO_FILE | |||
EEL_file_register(); | |||
#endif | |||
#ifndef EELSCRIPT_NO_FFT | |||
EEL_fft_register(); | |||
#endif | |||
#ifndef EELSCRIPT_NO_MDCT | |||
EEL_mdct_register(); | |||
#endif | |||
EEL_misc_register(); | |||
#ifndef EELSCRIPT_NO_EVAL | |||
EEL_eval_register(); | |||
NSEEL_addfunc_retval("defer",1,NSEEL_PProc_THIS,&_eel_defer); | |||
NSEEL_addfunc_retval("runloop", 1, NSEEL_PProc_THIS, &_eel_defer); | |||
NSEEL_addfunc_retval("atexit",1,NSEEL_PProc_THIS,&_eel_atexit); | |||
#endif | |||
#ifndef EELSCRIPT_NO_NET | |||
EEL_tcp_register(); | |||
#endif | |||
#ifndef EELSCRIPT_NO_LICE | |||
eel_lice_register(); | |||
#ifdef _WIN32 | |||
eel_lice_register_standalone(GetModuleHandle(NULL),EELSCRIPT_LICE_CLASSNAME,NULL,NULL); | |||
#else | |||
eel_lice_register_standalone(NULL,EELSCRIPT_LICE_CLASSNAME,NULL,NULL); | |||
#endif | |||
#endif | |||
return 0; | |||
} | |||
bool eelScriptInst::has_deferred() | |||
{ | |||
#ifndef EELSCRIPT_NO_EVAL | |||
return m_defer_eval.Available() && m_vm; | |||
#else | |||
return false; | |||
#endif | |||
} | |||
#ifndef EELSCRIPT_NO_EVAL | |||
void eelScriptInst::runCodeQ(WDL_Queue *q, const char *callername) | |||
{ | |||
const int endptr = q->Available(); | |||
int offs = 0; | |||
while (offs < endptr) | |||
{ | |||
if (q->Available() < endptr) break; // should never happen, but safety first! | |||
const char *ptr = (const char *)q->Get() + offs; | |||
offs += strlen(ptr)+1; | |||
NSEEL_CODEHANDLE ch=NULL; | |||
char *sv=evalCacheGet(ptr,&ch); | |||
if (!sv) sv=strdup(ptr); | |||
if (!ch) ch=NSEEL_code_compile(m_vm,sv,0); | |||
if (!ch) | |||
{ | |||
free(sv); | |||
#ifdef EEL_STRING_DEBUGOUT | |||
const char *err = NSEEL_code_getcodeerror(m_vm); | |||
if (err) EEL_STRING_DEBUGOUT("%s: error in code: %s",callername,err); | |||
#endif | |||
} | |||
else | |||
{ | |||
NSEEL_code_execute(ch); | |||
evalCacheDispose(sv,ch); | |||
} | |||
} | |||
q->Advance(endptr); | |||
} | |||
#endif | |||
bool eelScriptInst::run_deferred() | |||
{ | |||
#ifndef EELSCRIPT_NO_EVAL | |||
if (!m_defer_eval.Available()||!m_vm) return false; | |||
runCodeQ(&m_defer_eval,"defer"); | |||
m_defer_eval.Compact(); | |||
return m_defer_eval.Available()>0; | |||
#else | |||
return false; | |||
#endif | |||
} | |||
#ifdef EEL_WANT_DOCUMENTATION | |||
#include "ns-eel-func-ref.h" | |||
void EELScript_GenerateFunctionList(WDL_PtrList<const char> *fs) | |||
{ | |||
const char *p = nseel_builtin_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
p = eel_strings_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
p = eel_misc_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
#ifndef EELSCRIPT_NO_EVAL | |||
fs->Add("atexit\t\"code\"\t" | |||
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC | |||
"Adds code to be executed when the script finishes." | |||
#endif | |||
); | |||
fs->Add("defer\t\"code\"\t" | |||
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC | |||
"Adds code which will be executed some small amount of time after the current code finishes. Identical to runloop()" | |||
#endif | |||
); | |||
fs->Add("runloop\t\"code\"\t" | |||
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC | |||
"Adds code which will be executed some small amount of time after the current code finishes. Identical to defer()" | |||
#endif | |||
); | |||
p = eel_eval_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
#endif | |||
#ifndef EELSCRIPT_NO_NET | |||
p = eel_net_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
#endif | |||
#ifndef EELSCRIPT_NO_FFT | |||
p = eel_fft_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
#endif | |||
#ifndef EELSCRIPT_NO_FILE | |||
p = eel_file_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
#endif | |||
#ifndef EELSCRIPT_NO_MDCT | |||
p = eel_mdct_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
#endif | |||
#ifndef EELSCRIPT_NO_LICE | |||
p = eel_lice_function_reference; | |||
while (*p) { fs->Add(p); p += strlen(p) + 1; } | |||
#endif | |||
} | |||
#endif | |||
#undef opaque |
@@ -0,0 +1,399 @@ | |||
#ifndef _NSEEL_GLUE_AARCH64_H_ | |||
#define _NSEEL_GLUE_AARCH64_H_ | |||
// x0=return value, first parm, x1-x2 parms (x3-x7 more params) | |||
// x8 return struct? | |||
// x9-x15 temporary | |||
// x16-x17 = PLT, linker | |||
// x18 reserved (TLS) | |||
// x19-x28 callee-saved | |||
// x19 = worktable | |||
// x20 = ramtable | |||
// x21 = consttab | |||
// x22 = worktable ptr | |||
// x23-x28 spare | |||
// x29 frame pointer | |||
// x30 link register | |||
// x31 SP/zero | |||
// x0=p1 | |||
// x1=p2 | |||
// x2=p3 | |||
// d0 is return value for fp? | |||
// d/v/f0-7 = arguments/results | |||
// 8-15 callee saved | |||
// 16-31 temporary | |||
// v8-v15 spill registers | |||
#define GLUE_MAX_SPILL_REGS 8 | |||
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4) | |||
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4) | |||
static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) | |||
{ | |||
*(unsigned int *)b = 0x1e604101 + (ws<<5); // fmov d1, d8+ws | |||
} | |||
static void GLUE_SAVE_TO_SPILL(void *b, int ws) | |||
{ | |||
*(unsigned int *)b = 0x1e604008 + ws; // fmov d8+ws, d0 | |||
} | |||
#define GLUE_HAS_FPREG2 1 | |||
static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { 0x1e604001 }; // fmov d1, d0 | |||
static unsigned int GLUE_POP_STACK_TO_FPREG2[] = { | |||
0xfc4107e1 // ldr d1, [sp], #16 | |||
}; | |||
#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support | |||
#define GLUE_MAX_JMPSIZE ((1<<20) - 1024) // maximum relative jump size | |||
// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction. | |||
// 0 = current instruction | |||
static void GLUE_JMP_SET_OFFSET(void *endOfInstruction, int offset) | |||
{ | |||
unsigned int *a = (unsigned int*) endOfInstruction - 1; | |||
offset += 4; | |||
offset >>= 2; // as dwords | |||
if ((a[0] & 0xFC000000) == 0x14000000) | |||
{ | |||
// NC b = 0x14 + 26 bit offset | |||
a[0] = 0x14000000 | (offset & 0x3FFFFFF); | |||
} | |||
else if ((a[0] & 0xFF000000) == 0x54000000) | |||
{ | |||
// condb = 0x54 + 20 bit offset + 5 bit condition: 0=eq, 1=ne, b=lt, c=gt, d=le, a=ge | |||
a[0] = 0x54000000 | (a[0] & 0xF) | ((offset & 0x7FFFF) << 5); | |||
} | |||
} | |||
static const unsigned int GLUE_JMP_NC[] = { 0x14000000 }; | |||
static const unsigned int GLUE_JMP_IF_P1_Z[]= | |||
{ | |||
0x7100001f, // cmp w0, #0 | |||
0x54000000, // b.eq | |||
}; | |||
static const unsigned int GLUE_JMP_IF_P1_NZ[]= | |||
{ | |||
0x7100001f, // cmp w0, #0 | |||
0x54000001, // b.ne | |||
}; | |||
#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 16 // wr=-2, sets d1 | |||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 12 | |||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) | |||
{ | |||
static const unsigned int tab[3] = { | |||
0xd2800000, // mov x0, #0000 (val<<5) | reg | |||
0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg | |||
0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg | |||
}; | |||
// 0xABAAA, B is register, A are bits of word | |||
unsigned int *p=(unsigned int *)b; | |||
int wvo = wv; | |||
if (wv<0) wv=0; | |||
p[0] = tab[0] | wv | ((v&0xFFFF)<<5); | |||
p[1] = tab[1] | wv | (((v>>16)&0xFFFF)<<5); | |||
p[2] = tab[2] | wv | (((v>>32)&0xFFFF)<<5); | |||
if (wvo == -2) p[3] = 0xfd400001; // ldr d1, [x0] | |||
} | |||
const static unsigned int GLUE_FUNC_ENTER[2] = { 0xa9bf7bfd, 0x910003fd }; // stp x29, x30, [sp, #-16]! ; mov x29, sp | |||
#define GLUE_FUNC_ENTER_SIZE 4 | |||
const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop | |||
#define GLUE_FUNC_LEAVE_SIZE 0 | |||
const static unsigned int GLUE_RET[]={ 0xa8c17bfd, 0xd65f03c0 }; // ldp x29,x30, [sp], #16 ; ret | |||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr) | |||
{ | |||
const static unsigned int GLUE_SET_WTP_FROM_R19 = 0xaa1303f6; // mov r22, r19 | |||
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R19,sizeof(GLUE_SET_WTP_FROM_R19)); | |||
return 4; | |||
} | |||
const static unsigned int GLUE_PUSH_P1[1]={ 0xf81f0fe0 }; // str x0, [sp, #-16]! | |||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(offs) ((offs)>=32768 ? 8 : 4) | |||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) | |||
{ | |||
if (offs >= 32768) | |||
{ | |||
// add x1, sp, (offs/4096) lsl 12 | |||
*(unsigned int *)b = 0x914003e1 + ((offs>>12)<<10); | |||
// str x0, [x1, #offs & 4095] | |||
offs &= 4095; | |||
offs <<= 10-3; | |||
offs &= 0x7FFC00; | |||
((unsigned int *)b)[1] = 0xf9000020 + offs; | |||
} | |||
else | |||
{ | |||
// str x0, [sp, #offs] | |||
offs <<= 10-3; | |||
offs &= 0x7FFC00; | |||
*(unsigned int *)b = 0xf90003e0 + offs; | |||
} | |||
} | |||
#define GLUE_MOVE_PX_STACKPTR_SIZE 4 | |||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) | |||
{ | |||
// mov xX, sp | |||
*(unsigned int *)b = 0x910003e0 + wv; | |||
} | |||
#define GLUE_MOVE_STACK_SIZE 4 | |||
static void GLUE_MOVE_STACK(void *b, int amt) | |||
{ | |||
if (amt>=0) | |||
{ | |||
if (amt >= 4096) | |||
*(unsigned int*)b = 0x914003ff | (((amt+4095)>>12)<<10); | |||
else | |||
*(unsigned int*)b = 0x910003ff | (amt << 10); | |||
} | |||
else | |||
{ | |||
amt = -amt; | |||
if (amt >= 4096) | |||
*(unsigned int*)b = 0xd14003ff | (((amt+4095)>>12)<<10); | |||
else | |||
*(unsigned int*)b = 0xd10003ff | (amt << 10); | |||
} | |||
} | |||
#define GLUE_POP_PX_SIZE 4 | |||
static void GLUE_POP_PX(void *b, int wv) | |||
{ | |||
((unsigned int *)b)[0] = 0xf84107e0 | wv; // ldr x, [sp], 16 | |||
} | |||
#define GLUE_SET_PX_FROM_P1_SIZE 4 | |||
static void GLUE_SET_PX_FROM_P1(void *b, int wv) | |||
{ | |||
*(unsigned int *)b = 0xaa0003e0 | wv; | |||
} | |||
static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] = | |||
{ | |||
0xfd400007, // ldr d7, [x0] | |||
0xfc1f0fe7, // str d7, [sp, #-16]! | |||
}; | |||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
*bufptr++ = 0xfc4107e7; // ldr d7, [sp], #16 | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xfd000007; // str d7, [x0] | |||
} | |||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; | |||
} | |||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
*bufptr++ = 0xfd400007; // ldr d7, [x0] | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xfd000007; // str d7, [x0] | |||
} | |||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; | |||
} | |||
#ifndef _MSC_VER | |||
#define GLUE_CALL_CODE(bp, cp, rt) do { \ | |||
unsigned long f; \ | |||
if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \ | |||
!((f=glue_getscr())&(1<<24))) { \ | |||
glue_setscr(f|(1<<24)); \ | |||
eel_callcode64(bp, cp, rt); \ | |||
glue_setscr(f); \ | |||
} else eel_callcode64(bp, cp, rt);\ | |||
} while(0) | |||
static void eel_callcode64(INT_PTR bp, INT_PTR cp, INT_PTR rt) | |||
{ | |||
//fwrite((void *)cp,4,20,stdout); | |||
//return; | |||
static const double consttab[] = { | |||
NSEEL_CLOSEFACTOR, | |||
0.0, | |||
1.0, | |||
-1.0, | |||
-0.5, // for invsqrt | |||
1.5, | |||
}; | |||
__asm__( | |||
"mov x1, %2\n" | |||
"mov x2, %3\n" | |||
"mov x3, %1\n" | |||
"mov x0, %0\n" | |||
"stp x29, x30, [sp, #-64]!\n" | |||
"stp x18, x20, [sp, 16]\n" | |||
"stp x21, x19, [sp, 32]\n" | |||
"stp x22, x23, [sp, 48]\n" | |||
"mov x29, sp\n" | |||
"mov x19, x3\n" | |||
"mov x20, x1\n" | |||
"mov x21, x2\n" | |||
"blr x0\n" | |||
"ldp x29, x30, [sp], 16\n" | |||
"ldp x18, x20, [sp], 16\n" | |||
"ldp x21, x19, [sp], 16\n" | |||
"ldp x22, x23, [sp], 16\n" | |||
::"r" (cp), "r" (bp), "r" (rt), "r" (consttab) :"x0","x1","x2","x3","x4","x5","x6","x7", | |||
"x8","x9","x10","x11","x12","x13","x14","x15", | |||
"v8","v9","v10","v11","v12","v13","v14","v15"); | |||
}; | |||
#endif | |||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) | |||
{ | |||
unsigned int *p=(unsigned int *)_p; | |||
// 0xd2800000, // mov x0, #0000 (val<<5) | reg | |||
// 0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg | |||
// 0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg | |||
while (((p[0]>>5)&0xffff)!=0xdead || | |||
((p[1]>>5)&0xffff)!=0xbeef || | |||
((p[2]>>5)&0xffff)!=0xbeef) p++; | |||
p[0] = (p[0] & 0xFFE0001F) | ((newv&0xffff)<<5); | |||
p[1] = (p[1] & 0xFFE0001F) | (((newv>>16)&0xffff)<<5); | |||
p[2] = (p[2] & 0xFFE0001F) | (((newv>>32)&0xffff)<<5); | |||
return (unsigned char *)(p+2); | |||
} | |||
#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int) | |||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv) | |||
{ | |||
*(unsigned int *)b = 0xaa1603e0 + wv; // mov x, x22 | |||
} | |||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xfd000000; // str d0, [x0] | |||
} | |||
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int); | |||
} | |||
#define GLUE_POP_FPSTACK_SIZE 0 | |||
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack | |||
static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = { | |||
0xfc1f0fe0, // str d0, [sp, #-16]! | |||
}; | |||
static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = { | |||
0xfc0086c0, // str d0, [x22], #8 | |||
}; | |||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 | |||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) | |||
{ | |||
*(unsigned int *)b = 0xfd400000 + (wv<<5); // ldr d0, [xX] | |||
} | |||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) | |||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) | |||
{ | |||
GLUE_SET_PX_FROM_WTP(buf,wv); | |||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); | |||
}; | |||
static const unsigned int GLUE_SET_P1_Z[] = { 0x52800000 }; // mov w0, #0 | |||
static const unsigned int GLUE_SET_P1_NZ[] = { 0x52800020 }; // mov w0, #1 | |||
static void *GLUE_realAddress(void *fn, int *size) | |||
{ | |||
while ((*(int*)fn & 0xFC000000) == 0x14000000) | |||
{ | |||
int offset = (*(int*)fn & 0x3FFFFFF); | |||
if (offset & 0x2000000) | |||
offset |= 0xFC000000; | |||
fn = (int*)fn + offset; | |||
} | |||
static const unsigned int sig[3] = { 0xaa0003e0, 0xaa0103e1, 0xaa0203e2 }; | |||
unsigned char *p = (unsigned char *)fn; | |||
while (memcmp(p,sig,sizeof(sig))) p+=4; | |||
p+=sizeof(sig); | |||
fn = p; | |||
while (memcmp(p,sig,sizeof(sig))) p+=4; | |||
*size = p - (unsigned char *)fn; | |||
return fn; | |||
} | |||
static unsigned long __attribute__((unused)) glue_getscr() | |||
{ | |||
unsigned long rv; | |||
asm volatile ( "mrs %0, fpcr" : "=r" (rv)); | |||
return rv; | |||
} | |||
static void __attribute__((unused)) glue_setscr(unsigned long v) | |||
{ | |||
asm volatile ( "msr fpcr, %0" :: "r"(v)); | |||
} | |||
void eel_enterfp(int _s[2]) | |||
{ | |||
unsigned long *s = (unsigned long*)_s; | |||
s[0] = glue_getscr(); | |||
glue_setscr(s[0] | (1<<24)); | |||
} | |||
void eel_leavefp(int _s[2]) | |||
{ | |||
unsigned long *s = (unsigned long*)_s; | |||
glue_setscr(s[0]); | |||
} | |||
#define GLUE_HAS_FUSE 1 | |||
static int GLUE_FUSE(compileContext *ctx, unsigned char *code, int left_size, int right_size, int fuse_flags, int spill_reg) | |||
{ | |||
if (left_size>=4 && right_size == 4) | |||
{ | |||
unsigned int instr = ((unsigned int *)code)[-1]; | |||
if (spill_reg >= 0 && (instr & 0xfffffc1f) == 0x1e604001) // fmov d1, dX | |||
{ | |||
const int src_reg = (instr>>5)&0x1f; | |||
if (src_reg == spill_reg + 8) | |||
{ | |||
instr = ((unsigned int *)code)[0]; | |||
if ((instr & 0xffffcfff) == 0x1e600820) | |||
{ | |||
((unsigned int *)code)[-1] = instr + ((src_reg-1) << 5); | |||
return -4; | |||
} | |||
} | |||
} | |||
} | |||
return 0; | |||
} | |||
#endif |
@@ -0,0 +1,335 @@ | |||
#ifndef _NSEEL_GLUE_ARM_H_ | |||
#define _NSEEL_GLUE_ARM_H_ | |||
// r0=return value, first parm, r1-r2 parms | |||
// r3+ should be reserved | |||
// blx addr | |||
// stmfd sp!, {register list, lr} | |||
// ldmfd sp!, {register list, pc} | |||
// let's make r8 = worktable | |||
// let's make r7 = ramtable | |||
// r6 = consttab | |||
// r5 = worktable ptr | |||
// r0=p1 | |||
// r1=p2 | |||
// r2=p3 | |||
// d0 is return value? | |||
#define GLUE_HAS_FPREG2 1 | |||
static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { | |||
0xeeb01b40 // fcpyd d1, d0 | |||
}; | |||
static unsigned int GLUE_POP_STACK_TO_FPREG2[] = { | |||
0xed9d1b00,// vldr d1, [sp] | |||
0xe28dd008,// add sp, sp, #8 | |||
}; | |||
#define GLUE_MAX_SPILL_REGS 8 | |||
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4) | |||
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4) | |||
static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) | |||
{ | |||
*(unsigned int *)b = 0xeeb01b48 + ws; // fcpyd d1, d8+ws | |||
} | |||
static void GLUE_SAVE_TO_SPILL(void *b, int ws) | |||
{ | |||
*(unsigned int *)b = 0xeeb08b40 + (ws<<12); // fcpyd d8+ws, d0 | |||
} | |||
#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support | |||
#define GLUE_MAX_JMPSIZE ((1<<25) - 1024) // maximum relative jump size | |||
// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction. | |||
// TODO: verify, but offset probably from next instruction (PC is ahead) | |||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (((int *)(endOfInstruction))[-1]&0xFF000000)|((((offset)>>2)-1))) | |||
// /=conditional=always = 0xE | |||
// |/= 101(L), so 8+2+0 = 10 = A | |||
static const unsigned int GLUE_JMP_NC[] = { 0xEA000000 }; | |||
static const unsigned int GLUE_JMP_IF_P1_Z[]= | |||
{ | |||
0xe1100000, // tst r0, r0 | |||
0x0A000000, // branch if Z set | |||
}; | |||
static const unsigned int GLUE_JMP_IF_P1_NZ[]= | |||
{ | |||
0xe1100000, // tst r0, r0 | |||
0x1A000000, // branch if Z clear | |||
}; | |||
#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 12 // wr=-2, sets d1 | |||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 8 | |||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) | |||
{ | |||
// requires ARMv6thumb2 or later | |||
const unsigned int reg_add = wdl_max(wv,0) << 12; | |||
static const unsigned int tab[2] = { | |||
0xe3000000, // movw r0, #0000 | |||
0xe3400000, // movt r0, #0000 | |||
}; | |||
// 0xABAAA, B is register, A are bits of word | |||
unsigned int *p=(unsigned int *)b; | |||
p[0] = tab[0] | reg_add | (v&0xfff) | ((v&0xf000)<<4); | |||
p[1] = tab[1] | reg_add | ((v>>16)&0xfff) | ((v&0xf0000000)>>12); | |||
if (wv == -2) p[2] = 0xed901b00; // fldd d1, [r0] | |||
} | |||
const static unsigned int GLUE_FUNC_ENTER[1] = { 0xe92d4010 }; // push {r4, lr} | |||
#define GLUE_FUNC_ENTER_SIZE 4 | |||
const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop | |||
#define GLUE_FUNC_LEAVE_SIZE 0 | |||
const static unsigned int GLUE_RET[]={ 0xe8bd8010 }; // pop {r4, pc} | |||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr) | |||
{ | |||
const static unsigned int GLUE_SET_WTP_FROM_R8 = 0xe1a05008; // mov r5, r8 | |||
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R8,sizeof(GLUE_SET_WTP_FROM_R8)); | |||
return sizeof(GLUE_SET_WTP_FROM_R8); | |||
} | |||
const static unsigned int GLUE_PUSH_P1[1]={ 0xe52d0008 }; // push {r0}, aligned to 8 | |||
static int arm_encode_constforalu(int amt) | |||
{ | |||
int nrot = 16; | |||
while (amt >= 0x100 && nrot > 1) | |||
{ | |||
// ARM encodes integers for ALU operations as rotated right by third nibble*2 | |||
amt = (amt + 3)>>2; | |||
nrot--; | |||
} | |||
return ((nrot&15) << 8) | amt; | |||
} | |||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) ((x)>=4096 ? 8 : 4) | |||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) | |||
{ | |||
if (offs >= 4096) | |||
{ | |||
// add r2, sp, (offs&~4095) | |||
*(unsigned int *)b = 0xe28d2000 | arm_encode_constforalu(offs&~4095); | |||
// str r0, [r2, offs&4095] | |||
((unsigned int *)b)[1] = 0xe5820000 + (offs&4095); | |||
} | |||
else | |||
{ | |||
// str r0, [sp, #offs] | |||
*(unsigned int *)b = 0xe58d0000 + offs; | |||
} | |||
} | |||
#define GLUE_MOVE_PX_STACKPTR_SIZE 4 | |||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) | |||
{ | |||
// mov rX, sp | |||
*(unsigned int *)b = 0xe1a0000d + (wv<<12); | |||
} | |||
#define GLUE_MOVE_STACK_SIZE 4 | |||
static void GLUE_MOVE_STACK(void *b, int amt) | |||
{ | |||
unsigned int instr = 0xe28dd000; | |||
if (amt < 0) | |||
{ | |||
instr = 0xe24dd000; | |||
amt=-amt; | |||
} | |||
*(unsigned int*)b = instr | arm_encode_constforalu(amt); | |||
} | |||
#define GLUE_POP_PX_SIZE 4 | |||
static void GLUE_POP_PX(void *b, int wv) | |||
{ | |||
((unsigned int *)b)[0] = 0xe49d0008 | (wv<<12); // pop {rX}, aligned to 8 | |||
} | |||
#define GLUE_SET_PX_FROM_P1_SIZE 4 | |||
static void GLUE_SET_PX_FROM_P1(void *b, int wv) | |||
{ | |||
*(unsigned int *)b = 0xe1a00000 | (wv<<12); // mov rX, r0 | |||
} | |||
static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] = | |||
{ | |||
0xed907b00, // fldd d7, [r0] | |||
0xe24dd008, // sub sp, sp, #8 | |||
0xed8d7b00, // fstd d7, [sp] | |||
}; | |||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
*bufptr++ = 0xed9d7b00; // fldd d7, [sp] | |||
*bufptr++ = 0xe28dd008; // add sp, sp, #8 | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xed807b00; // fstd d7, [r0] | |||
} | |||
return 3*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; | |||
} | |||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
*bufptr++ = 0xed907b00; // fldd d7, [r0] | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xed807b00; // fstd d7, [r0] | |||
} | |||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE; | |||
} | |||
#ifndef _MSC_VER | |||
#define GLUE_CALL_CODE(bp, cp, rt) do { \ | |||
unsigned int f; \ | |||
if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \ | |||
!((f=glue_getscr())&(1<<24))) { \ | |||
glue_setscr(f|(1<<24)); \ | |||
eel_callcode32(bp, cp, rt); \ | |||
glue_setscr(f); \ | |||
} else eel_callcode32(bp, cp, rt);\ | |||
} while(0) | |||
static const double __consttab[] = { | |||
NSEEL_CLOSEFACTOR, | |||
0.0, | |||
1.0, | |||
-1.0, | |||
-0.5, // for invsqrt | |||
1.5, | |||
}; | |||
static void eel_callcode32(INT_PTR bp, INT_PTR cp, INT_PTR rt) | |||
{ | |||
__asm__ volatile( | |||
"mov r7, %2\n" | |||
"mov r6, %3\n" | |||
"mov r8, %1\n" | |||
"mov r0, %0\n" | |||
"mov r1, sp\n" | |||
"bic sp, sp, #7\n" | |||
"push {r1, lr}\n" | |||
"blx r0\n" | |||
"pop {r1, lr}\n" | |||
"mov sp, r1\n" | |||
::"r" (cp), "r" (bp), "r" (rt), "r" (__consttab) : | |||
"r5", "r6", "r7", "r8", "r10", | |||
"d8","d9","d10","d11","d12","d13","d14","d15"); | |||
}; | |||
#endif | |||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) | |||
{ | |||
unsigned int *p=(unsigned int *)_p; | |||
while ((p[0]&0x000F0FFF) != 0x000d0ead && | |||
(p[1]&0x000F0FFF) != 0x000b0eef) p++; | |||
p[0] = (p[0]&0xFFF0F000) | (newv&0xFFF) | ((newv << 4) & 0xF0000); | |||
p[1] = (p[1]&0xFFF0F000) | ((newv>>16)&0xFFF) | ((newv >> 12)&0xF0000); | |||
return (unsigned char *)(p+1); | |||
} | |||
#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int) | |||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv) | |||
{ | |||
*(unsigned int *)b = 0xe1a00005 + (wv<<12); // mov rX, r5 | |||
} | |||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xed800b00; // fstd d0, [r0] | |||
} | |||
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int); | |||
} | |||
#define GLUE_POP_FPSTACK_SIZE 0 | |||
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack | |||
static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = { | |||
0xe24dd008, // sub sp, sp, #8 | |||
0xed8d0b00, // fstd d0, [sp] | |||
}; | |||
static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = { | |||
0xed850b00, // fstd d0, [r5] | |||
0xe2855008, // add r5, r5, #8 | |||
}; | |||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 | |||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) | |||
{ | |||
*(unsigned int *)b = 0xed900b00 + (wv<<16); // fldd d0, [rX] | |||
} | |||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) | |||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) | |||
{ | |||
GLUE_SET_PX_FROM_WTP(buf,wv); | |||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); | |||
}; | |||
static const unsigned int GLUE_SET_P1_Z[] = { 0xe3a00000 }; // mov r0, #0 | |||
static const unsigned int GLUE_SET_P1_NZ[] = { 0xe3a00001 }; // mov r0, #1 | |||
static void *GLUE_realAddress(void *fn, int *size) | |||
{ | |||
static const unsigned int sig[3] = { 0xe1a00000, 0xe1a01001, 0xe1a02002 }; | |||
unsigned char *p = (unsigned char *)fn; | |||
while (memcmp(p,sig,sizeof(sig))) p+=4; | |||
p+=sizeof(sig); | |||
fn = p; | |||
while (memcmp(p,sig,sizeof(sig))) p+=4; | |||
*size = p - (unsigned char *)fn; | |||
return fn; | |||
} | |||
static unsigned int __attribute__((unused)) glue_getscr() | |||
{ | |||
unsigned int rv; | |||
asm volatile ( "fmrx %0, fpscr" : "=r" (rv)); | |||
return rv; | |||
} | |||
static void __attribute__((unused)) glue_setscr(unsigned int v) | |||
{ | |||
asm volatile ( "fmxr fpscr, %0" :: "r"(v)); | |||
} | |||
void eel_enterfp(int s[2]) | |||
{ | |||
s[0] = glue_getscr(); | |||
glue_setscr(s[0] | (1<<24)); // could also do 3<<22 for RTZ | |||
} | |||
void eel_leavefp(int s[2]) | |||
{ | |||
glue_setscr(s[0]); | |||
} | |||
#endif |
@@ -0,0 +1,969 @@ | |||
#ifndef _EEL_GLUE_PORTABLE_H_ | |||
#define _EEL_GLUE_PORTABLE_H_ | |||
/* slightly faster portable implementation, requires tail call elimination though bleh | |||
*/ | |||
#define DECL_ASMFUNC(x) | |||
#define GLUE_JMP_TYPE int | |||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((GLUE_JMP_TYPE *)(endOfInstruction))[-1] = (offset)) | |||
#define GLUE_MAX_FPSTACK_SIZE 64 | |||
#define BIF_FPSTACKUSE(x) (0) // fp stack is not used within functions | |||
#define BIF_GETFPSTACKUSE(x) (1) | |||
#define BC_DECL(x) static const EEL_BC_TYPE GLUE_##x[] = { EEL_BC_##x }; | |||
#define BC_DECL_JMP(x) static const EEL_BC_TYPE GLUE_##x[1 + sizeof(GLUE_JMP_TYPE) / sizeof(EEL_BC_TYPE)] = { EEL_BC_##x }; | |||
#define GLUE_POP_FPSTACK_SIZE sizeof(EEL_BC_TYPE) | |||
#define GLUE_LOOP_BEGIN_SIZE 0 | |||
#define GLUE_LOOP_BEGIN ((void*)"") | |||
#define GLUE_LOOP_CLAMPCNT_SIZE 0 | |||
#define GLUE_LOOP_CLAMPCNT ((void*)"") | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
#define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) | |||
#else | |||
#define GLUE_WHILE_SETUP_SIZE 0 | |||
#define GLUE_WHILE_SETUP ((void *)"") | |||
#define GLUE_WHILE_END_NOJUMP | |||
#endif | |||
#define EEL_BC_STACKSIZE (65536) | |||
// todo: check for stack overflows! we could determine if this is possible at compile time. | |||
#define EEL_BC_STACK_POP_SIZE 8 | |||
#define EEL_BC_STACK_PUSH(type, val) (*(type *)(stackptr -= EEL_BC_STACK_POP_SIZE)) = (val) | |||
#define EEL_BC_STACK_POP() (stackptr += EEL_BC_STACK_POP_SIZE) | |||
#define EEL_BC_TRUE ((EEL_F*)(INT_PTR)1) | |||
typedef struct { | |||
char *stackptr; | |||
EEL_F *_fpstacktop; | |||
EEL_F *p1, *p2, *p3, *wtp; | |||
EEL_F fpstack[GLUE_MAX_FPSTACK_SIZE]; | |||
char __stack[EEL_BC_STACKSIZE]; | |||
} EEL_BC_STATE; | |||
#define GLUE_MEM_NEEDS_PPROC | |||
static void GLUE_CALL_CODE(INT_PTR bp, INT_PTR cp, INT_PTR rt) | |||
{ | |||
EEL_BC_STATE state; | |||
EEL_BC_TYPE *c = (EEL_BC_TYPE *)cp; | |||
state.stackptr=state.__stack + EEL_BC_STACKSIZE; | |||
state._fpstacktop=state.fpstack-1; | |||
state.p1 = NULL; | |||
state.p2 = NULL; | |||
state.p3 = NULL; | |||
state.wtp = (EEL_F*)bp; | |||
c[0](c+1,&state); | |||
} | |||
#define fp_top (((EEL_BC_STATE*)__state)->_fpstacktop[0]) | |||
#define fp_top2 (((EEL_BC_STATE*)__state)->_fpstacktop[-1]) | |||
#define fp_push(x) *++((EEL_BC_STATE*)__state)->_fpstacktop=(x) | |||
#define fp_pop() (*((EEL_BC_STATE*)__state)->_fpstacktop--) | |||
#define fp_rewind(x) (((EEL_BC_STATE*)__state)->_fpstacktop -= (x)) | |||
#define __stack ((EEL_BC_STATE*)__state)->__stack | |||
#define wtp ((EEL_BC_STATE*)__state)->wtp | |||
#define p1 ((EEL_BC_STATE*)__state)->p1 | |||
#define p2 ((EEL_BC_STATE*)__state)->p2 | |||
#define p3 ((EEL_BC_STATE*)__state)->p3 | |||
#define stackptr ((EEL_BC_STATE*)__state)->stackptr | |||
#define EEL_BC_BEGIN(x) static void x(void *inst, void *__state) { | |||
#define EEL_BC_END_BYTEOFFS(x) inst = (char *)inst + (x); EEL_BC_END | |||
#define EEL_BC_END ((EEL_BC_TYPE *)inst)[0]((char*)inst + sizeof(EEL_BC_TYPE),__state); } | |||
EEL_BC_BEGIN(EEL_BC_FXCH) | |||
EEL_F a = fp_top; | |||
fp_top=fp_top2; | |||
fp_top2=a; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_POP_FPSTACK) fp_rewind(1); EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_NOP) EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_RET) | |||
if (EEL_BC_STACK_POP() > __stack+EEL_BC_STACKSIZE) | |||
{ | |||
return; | |||
} | |||
inst = *(void **)(stackptr - EEL_BC_STACK_POP_SIZE); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_JMP_NC) | |||
EEL_BC_END_BYTEOFFS(sizeof(GLUE_JMP_TYPE) + *(GLUE_JMP_TYPE *)inst); | |||
EEL_BC_BEGIN(EEL_BC_JMP_IF_P1_Z) | |||
EEL_BC_END_BYTEOFFS(sizeof(GLUE_JMP_TYPE) + (p1 ? 0 : *(GLUE_JMP_TYPE *)inst)); | |||
EEL_BC_BEGIN(EEL_BC_JMP_IF_P1_NZ) | |||
EEL_BC_END_BYTEOFFS(sizeof(GLUE_JMP_TYPE) + (p1 ? *(GLUE_JMP_TYPE *)inst : 0)); | |||
EEL_BC_BEGIN(EEL_BC_MOV_FPTOP_DV) | |||
fp_push(**(EEL_F **)inst); | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_MOV_P1_DV) | |||
p1 = *(void **)inst; | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_MOV_P2_DV) | |||
p2 = *(void **)inst; | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_MOV_P3_DV) | |||
p3 = *(void **)inst; | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC__RESET_WTP) | |||
wtp = *(void **)inst; | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_PUSH_P1) | |||
EEL_BC_STACK_PUSH(void *, p1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_PUSH_P1PTR_AS_VALUE) | |||
EEL_BC_STACK_PUSH(EEL_F, *p1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_POP_P1) | |||
p1 = *(EEL_F **) stackptr; | |||
EEL_BC_STACK_POP(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_POP_P2) | |||
p2 = *(EEL_F **) stackptr; | |||
EEL_BC_STACK_POP(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_POP_P3) | |||
p3 = *(EEL_F **) stackptr; | |||
EEL_BC_STACK_POP(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_POP_VALUE_TO_ADDR) | |||
**(EEL_F**)inst = *(EEL_F *)stackptr; | |||
EEL_BC_STACK_POP(); | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_MOVE_STACK) | |||
stackptr += *(int *)inst; | |||
EEL_BC_END_BYTEOFFS(sizeof(int)); | |||
EEL_BC_BEGIN(EEL_BC_STORE_P1_TO_STACK_AT_OFFS) | |||
*(void **) (stackptr + *(int *)inst) = p1; | |||
EEL_BC_END_BYTEOFFS(sizeof(int)); | |||
EEL_BC_BEGIN(EEL_BC_MOVE_STACKPTR_TO_P1) | |||
p1 = (double *)stackptr; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MOVE_STACKPTR_TO_P2) | |||
p2 = (double *)stackptr; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MOVE_STACKPTR_TO_P3) | |||
p3 = (double *)stackptr; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SET_P2_FROM_P1) | |||
p2=p1; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SET_P3_FROM_P1) | |||
p3=p1; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_COPY_VALUE_AT_P1_TO_ADDR) | |||
**(EEL_F **)inst = *p1; | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_SET_P1_FROM_WTP) | |||
p1 = wtp; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SET_P2_FROM_WTP) | |||
p2 = wtp; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SET_P3_FROM_WTP) | |||
p3 = wtp; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_POP_FPSTACK_TO_PTR) | |||
**((EEL_F **)inst) = fp_pop(); | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_POP_FPSTACK_TOSTACK) | |||
EEL_BC_STACK_PUSH(EEL_F, fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_PUSH_VAL_AT_P1_TO_FPSTACK) | |||
fp_push(*p1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_PUSH_VAL_AT_P2_TO_FPSTACK) | |||
fp_push(*p2); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_PUSH_VAL_AT_P3_TO_FPSTACK) | |||
fp_push(*p3); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_POP_FPSTACK_TO_WTP) | |||
*wtp++ = fp_pop(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SET_P1_Z) | |||
p1=NULL; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SET_P1_NZ) | |||
p1 = EEL_BC_TRUE; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_LOOP_LOADCNT) | |||
int ret_offs = sizeof(GLUE_JMP_TYPE); | |||
if ((EEL_BC_STACK_PUSH(int, (int)fp_pop())) < 1) | |||
{ | |||
EEL_BC_STACK_POP(); | |||
ret_offs += *(GLUE_JMP_TYPE *)inst; | |||
} | |||
else | |||
{ | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
if ((*(int *)stackptr) > NSEEL_LOOPFUNC_SUPPORT_MAXLEN) (*(int *)stackptr) = NSEEL_LOOPFUNC_SUPPORT_MAXLEN; | |||
#endif | |||
EEL_BC_STACK_PUSH(void *, wtp); | |||
} | |||
EEL_BC_END_BYTEOFFS(ret_offs); | |||
EEL_BC_BEGIN(EEL_BC_LOOP_END) | |||
int ret_offs = sizeof(GLUE_JMP_TYPE); | |||
wtp = *(void **) (stackptr); | |||
if (--(*(int *)(stackptr+EEL_BC_STACK_POP_SIZE)) <= 0) | |||
{ | |||
stackptr += EEL_BC_STACK_POP_SIZE*2; | |||
} | |||
else | |||
{ | |||
ret_offs += *(GLUE_JMP_TYPE *)inst; // back to the start! | |||
} | |||
EEL_BC_END_BYTEOFFS(ret_offs); | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
EEL_BC_BEGIN(EEL_BC_WHILE_SETUP) | |||
EEL_BC_STACK_PUSH(int,NSEEL_LOOPFUNC_SUPPORT_MAXLEN); | |||
EEL_BC_END | |||
#endif | |||
EEL_BC_BEGIN(EEL_BC_WHILE_BEGIN) | |||
EEL_BC_STACK_PUSH(void *, wtp); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_WHILE_END) | |||
int ret_offs = sizeof(GLUE_JMP_TYPE); | |||
wtp = *(EEL_F **) stackptr; | |||
EEL_BC_STACK_POP(); | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
if (--(*(int *)stackptr) <= 0) | |||
{ | |||
EEL_BC_STACK_POP(); | |||
ret_offs += *(GLUE_JMP_TYPE *)inst; // endpt | |||
} | |||
#endif | |||
EEL_BC_END_BYTEOFFS(ret_offs); | |||
EEL_BC_BEGIN(EEL_BC_WHILE_CHECK_RV) | |||
int ret_offs = sizeof(GLUE_JMP_TYPE); | |||
if (p1) | |||
{ | |||
ret_offs += *(GLUE_JMP_TYPE *)inst; // loop | |||
} | |||
else | |||
{ | |||
// done | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
EEL_BC_STACK_POP(); | |||
#endif | |||
} | |||
EEL_BC_END_BYTEOFFS(ret_offs); | |||
EEL_BC_BEGIN(EEL_BC_BNOT) | |||
p1 = p1 ? NULL : EEL_BC_TRUE; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_BNOTNOT) | |||
p1 = p1 ? EEL_BC_TRUE : NULL; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_EQUAL) | |||
p1 = fabs(fp_top - fp_top2) < NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; | |||
fp_rewind(2); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_EQUAL_EXACT) | |||
p1 = fp_top == fp_top2 ? EEL_BC_TRUE : NULL; | |||
fp_rewind(2); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_NOTEQUAL) | |||
p1 = fabs(fp_top - fp_top2) >= NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; | |||
fp_rewind(2); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_NOTEQUAL_EXACT) | |||
p1 = fp_top != fp_top2 ? EEL_BC_TRUE : NULL; | |||
fp_rewind(2); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ABOVE) | |||
p1 = fp_top < fp_top2 ? EEL_BC_TRUE : NULL; | |||
fp_rewind(2); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_BELOWEQ) | |||
p1 = fp_top >= fp_top2 ? EEL_BC_TRUE : NULL; | |||
fp_rewind(2); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ADD) | |||
fp_top2 += fp_top; | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SUB) | |||
fp_top2 -= fp_top; | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MUL) | |||
fp_top2 *= fp_top; | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_DIV) | |||
fp_top2 /= fp_top; | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_AND) | |||
fp_top2 = (EEL_F) (((int)fp_top) & (int)(fp_top2)); | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_OR) | |||
fp_top2 = (EEL_F) (((int)fp_top) | (int)(fp_top2)); | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_OR0) | |||
fp_top = (EEL_F) ((int)(fp_top)); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_XOR) | |||
fp_top2 = (EEL_F) (((int)fp_top) ^ (int)(fp_top2)); | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ADD_OP) | |||
*(p1 = p2) = denormal_filter_double2(*p2 + fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SUB_OP) | |||
*(p1 = p2) = denormal_filter_double2(*p2 - fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ADD_OP_FAST) | |||
*(p1 = p2) += fp_pop(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SUB_OP_FAST) | |||
*(p1 = p2) -= fp_pop(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MUL_OP) | |||
*(p1 = p2) = denormal_filter_double2(*p2 * fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_DIV_OP) | |||
*(p1 = p2) = denormal_filter_double2(*p2 / fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MUL_OP_FAST) | |||
*(p1 = p2) *= fp_pop(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_DIV_OP_FAST) | |||
*(p1 = p2) /= fp_pop(); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_AND_OP) | |||
p1 = p2; | |||
*p2 = (EEL_F) (((int)*p2) & (int)fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_OR_OP) | |||
p1 = p2; | |||
*p2 = (EEL_F) (((int)*p2) | (int)fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_XOR_OP) | |||
p1 = p2; | |||
*p2 = (EEL_F) (((int)*p2) ^ (int)fp_pop()); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_UMINUS) | |||
fp_top = -fp_top; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ASSIGN) | |||
*p2 = denormal_filter_double2(*p1); | |||
p1 = p2; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ASSIGN_FAST) | |||
*p2 = *p1; | |||
p1 = p2; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ASSIGN_FAST_FROMFP) | |||
*p2 = fp_pop(); | |||
p1 = p2; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ASSIGN_FROMFP) | |||
*p2 = denormal_filter_double2(fp_pop()); | |||
p1 = p2; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MOD) | |||
{ | |||
int a = (int) (fp_pop()); | |||
fp_top = a ? (EEL_F) ((int)fp_top % a) : 0.0; | |||
} | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MOD_OP) | |||
{ | |||
int a = (int) (fp_pop()); | |||
*p2 = a ? (EEL_F) ((int)*p2 % a) : 0.0; | |||
p1=p2; | |||
} | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SHR) | |||
fp_top2 = (EEL_F) (((int)fp_top2) >> (int)fp_top); | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SHL) | |||
fp_top2 = (EEL_F) (((int)fp_top2) << (int)fp_top); | |||
fp_rewind(1); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SQR) | |||
fp_top *= fp_top; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MIN) | |||
if (*p1 > *p2) p1 = p2; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MAX) | |||
if (*p1 < *p2) p1 = p2; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MIN_FP) | |||
{ | |||
EEL_F a=fp_pop(); | |||
if (a<fp_top) fp_top=a; | |||
} | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_MAX_FP) | |||
{ | |||
EEL_F a=fp_pop(); | |||
if (a>fp_top) fp_top=a; | |||
} | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_ABS) | |||
fp_top = fabs(fp_top); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_SIGN) | |||
if (fp_top<0.0) fp_top=-1.0; | |||
else if (fp_top>0.0) fp_top=1.0; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_DBG_GETSTACKPTR) | |||
fp_top = (int)(stackptr - __stack); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_INVSQRT) | |||
{ | |||
float y = (float)fp_top; | |||
int i = 0x5f3759df - ( (* (int *) &y) >> 1 ); | |||
y = *(float *) &i; | |||
fp_top = y * ( 1.5F - ( (fp_top * 0.5) * y * y ) ); | |||
} | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_FCALL) | |||
{ | |||
char *newiptr = *(char **)inst; | |||
EEL_BC_STACK_PUSH(void *, ((char*)inst + sizeof(void *))); | |||
inst = (EEL_BC_TYPE *)newiptr; | |||
} | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_BOOLTOFP) | |||
fp_push(p1 ? 1.0 : 0.0); | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_FPTOBOOL) | |||
p1 = fabs(fp_pop()) >= NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_FPTOBOOL_REV) | |||
p1 = fabs(fp_pop()) < NSEEL_CLOSEFACTOR ? EEL_BC_TRUE : NULL; | |||
EEL_BC_END | |||
EEL_BC_BEGIN(EEL_BC_CFUNC_1PDD) | |||
{ | |||
double (*f)(double) = *(double (**)(double)) inst; | |||
fp_top = f(fp_top); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)); | |||
EEL_BC_BEGIN(EEL_BC_CFUNC_2PDD) | |||
{ | |||
double (*f)(double,double) = *(double (**)(double,double))inst; | |||
fp_top2 = f(fp_top2,fp_top); | |||
fp_rewind(1); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_CFUNC_2PDDS) | |||
{ | |||
double (*f)(double,double) = *(double (**)(double,double))inst; | |||
*p2 = f(*p2,fp_pop()); | |||
p1 = p2; | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)); | |||
EEL_BC_BEGIN(EEL_BC_MEGABUF) | |||
{ | |||
unsigned int idx=(unsigned int) (fp_pop() + NSEEL_CLOSEFACTOR); | |||
EEL_F **f = *(EEL_F ***)inst,*f2; | |||
p1 = (idx < NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK && (f2=f[idx/NSEEL_RAM_ITEMSPERBLOCK])) ? | |||
(f2 + (idx&(NSEEL_RAM_ITEMSPERBLOCK-1))) : | |||
__NSEEL_RAMAlloc((void*)f,idx); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)) | |||
EEL_BC_BEGIN(EEL_BC_GMEGABUF) | |||
{ | |||
p1 = __NSEEL_RAMAllocGMEM(*(EEL_F ****)inst,(int) (fp_pop() + NSEEL_CLOSEFACTOR)); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)*2); | |||
EEL_BC_BEGIN(EEL_BC_GENERIC1PARM) | |||
{ | |||
EEL_F *(*f)(void *,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *)) ((char*)inst+sizeof(void *)); | |||
p1 = f(*(void **)inst,p1); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)*2); | |||
EEL_BC_BEGIN(EEL_BC_GENERIC2PARM) | |||
{ | |||
EEL_F *(*f)(void *,EEL_F*,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); | |||
p1 = f(*(void **)inst,p2, p1); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)*2); | |||
EEL_BC_BEGIN(EEL_BC_GENERIC3PARM) | |||
{ | |||
EEL_F *(*f)(void *,EEL_F*,EEL_F*,EEL_F*) = *(EEL_F *(**)(void *, EEL_F *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); | |||
p1 = f(*(void **)inst,p3, p2, p1); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)*2); | |||
EEL_BC_BEGIN(EEL_BC_GENERIC1PARM_RETD) | |||
{ | |||
EEL_F (*f)(void *,EEL_F*) = *(EEL_F (**)(void *, EEL_F *)) ((char*)inst+sizeof(void *)); | |||
fp_push(f(*(void **)inst,p1)); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)*2); | |||
EEL_BC_BEGIN(EEL_BC_GENERIC2PARM_RETD) | |||
{ | |||
EEL_F (*f)(void *,EEL_F*,EEL_F*) = *(EEL_F (**)(void *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); | |||
fp_push(f(*(void **)inst,p2, p1)); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)*2); | |||
EEL_BC_BEGIN(EEL_BC_GENERIC3PARM_RETD) | |||
{ | |||
EEL_F (*f)(void *,EEL_F*,EEL_F*,EEL_F*) = *(EEL_F (**)(void *, EEL_F *, EEL_F *, EEL_F *)) ((char*)inst+sizeof(void *)); | |||
fp_push(f(*(void **)inst,p3, p2, p1)); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)*2); | |||
EEL_BC_BEGIN(EEL_BC_USERSTACK_PUSH) | |||
{ | |||
char *iptr = (char *)inst; | |||
UINT_PTR *sptr = *(UINT_PTR **)iptr; | |||
(*sptr) += 8; | |||
(*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); | |||
(*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); | |||
*(EEL_F *)*sptr = *p1; | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)*3); | |||
EEL_BC_BEGIN(EEL_BC_USERSTACK_POP) | |||
{ | |||
char *iptr = (char *)inst; | |||
UINT_PTR *sptr = *(UINT_PTR **)iptr; | |||
*p1 = *(EEL_F *)*sptr; | |||
(*sptr) -= 8; | |||
(*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); | |||
(*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)*3); | |||
EEL_BC_BEGIN(EEL_BC_USERSTACK_POPFAST) | |||
{ | |||
char *iptr = (char *)inst; | |||
UINT_PTR *sptr = *(UINT_PTR **)iptr; | |||
p1 = (EEL_F *)*sptr; | |||
(*sptr) -= 8; | |||
(*sptr) &= *(UINT_PTR*)(iptr+sizeof(void *)); | |||
(*sptr) |= *(UINT_PTR*)(iptr+2*sizeof(void *)); | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)*3); | |||
EEL_BC_BEGIN(EEL_BC_USERSTACK_PEEK) | |||
{ | |||
char *iptr = (char*)inst; | |||
UINT_PTR sptr = **(UINT_PTR **)iptr; | |||
sptr -= sizeof(EEL_F) * (int)(fp_pop()); | |||
sptr &= *(UINT_PTR*)(iptr+sizeof(void *)); | |||
sptr |= *(UINT_PTR*)(iptr+2*sizeof(void *)); | |||
p1 = (EEL_F *)sptr; | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)*3); | |||
EEL_BC_BEGIN(EEL_BC_USERSTACK_PEEK_INT) | |||
{ | |||
char *iptr = (char*)inst; | |||
UINT_PTR sptr = **(UINT_PTR **)iptr; | |||
sptr -= *(UINT_PTR*)(iptr+sizeof(void*)); | |||
sptr &= *(UINT_PTR*)(iptr+2*sizeof(void *)); | |||
sptr |= *(UINT_PTR*)(iptr+3*sizeof(void *)); | |||
p1 = (EEL_F *)sptr; | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void*)*4); | |||
EEL_BC_BEGIN(EEL_BC_USERSTACK_PEEK_TOP) | |||
p1 = **(EEL_F ***)inst; | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)); | |||
EEL_BC_BEGIN(EEL_BC_USERSTACK_EXCH) | |||
{ | |||
EEL_F *p=**(EEL_F ***)inst; | |||
EEL_F a=*p; | |||
*p=*p1; | |||
*p1=a; | |||
} | |||
EEL_BC_END_BYTEOFFS(sizeof(void *)); | |||
#undef fp_top | |||
#undef fp_top2 | |||
#undef fp_pop | |||
#undef fp_push | |||
#undef iptr | |||
#undef p1 | |||
#undef p2 | |||
#undef p3 | |||
#undef wtp | |||
#undef __stack | |||
#undef stackptr | |||
#define BC_DECLASM(x,y) static EEL_BC_TYPE nseel_asm_##x[1]={EEL_BC_##y}; | |||
#define EEL_BC_ENDOF(x) (((char*)(x))+sizeof(x)) | |||
#define BC_DECLASM_N(x,y,n) static EEL_BC_TYPE nseel_asm_##x[1 + (n*sizeof(INT_PTR))/sizeof(EEL_BC_TYPE)]={EEL_BC_##y, }; | |||
#define BC_DECLASM_N2(x,y,n) static EEL_BC_TYPE _asm_##x[1 + (n*sizeof(INT_PTR))/sizeof(EEL_BC_TYPE)]={EEL_BC_##y, }; | |||
#define BC_DECLASM_N_EXPORT(x,y,n) EEL_BC_TYPE _asm_##x[1 + (n*sizeof(INT_PTR))/sizeof(EEL_BC_TYPE)]={EEL_BC_##y, }; const void *const _asm_##x##_end = EEL_BC_ENDOF(_asm_##x); | |||
BC_DECLASM(band,NOP) | |||
BC_DECLASM(bor,NOP) | |||
BC_DECLASM(bnot,BNOT) | |||
BC_DECLASM(bnotnot,BNOTNOT) | |||
BC_DECLASM(equal,EQUAL) | |||
BC_DECLASM(equal_exact,EQUAL_EXACT) | |||
BC_DECLASM(notequal_exact,NOTEQUAL_EXACT) | |||
BC_DECLASM(notequal,NOTEQUAL) | |||
BC_DECLASM(above,ABOVE) | |||
BC_DECLASM(beloweq,BELOWEQ) | |||
BC_DECLASM(add,ADD) | |||
BC_DECLASM(sub,SUB) | |||
BC_DECLASM(mul,MUL) | |||
BC_DECLASM(div,DIV) | |||
BC_DECLASM(and,AND) | |||
BC_DECLASM(or,OR) | |||
BC_DECLASM(or0,OR0) | |||
BC_DECLASM(xor,XOR) | |||
BC_DECLASM(add_op,ADD_OP) | |||
BC_DECLASM(sub_op,SUB_OP) | |||
BC_DECLASM(add_op_fast,ADD_OP_FAST) | |||
BC_DECLASM(sub_op_fast,SUB_OP_FAST) | |||
BC_DECLASM(mul_op,MUL_OP) | |||
BC_DECLASM(div_op,DIV_OP) | |||
BC_DECLASM(mul_op_fast,MUL_OP_FAST) | |||
BC_DECLASM(div_op_fast,DIV_OP_FAST) | |||
BC_DECLASM(and_op,AND_OP) | |||
BC_DECLASM(or_op,OR_OP) | |||
BC_DECLASM(xor_op,XOR_OP) | |||
BC_DECLASM(uminus,UMINUS) | |||
BC_DECLASM(assign,ASSIGN) | |||
BC_DECLASM(assign_fast,ASSIGN_FAST) | |||
BC_DECLASM(assign_fast_fromfp,ASSIGN_FAST_FROMFP) | |||
BC_DECLASM(assign_fromfp,ASSIGN_FROMFP) | |||
BC_DECLASM(mod,MOD) | |||
BC_DECLASM(mod_op,MOD_OP) | |||
BC_DECLASM(shr,SHR) | |||
BC_DECLASM(shl,SHL) | |||
BC_DECLASM(sqr,SQR) | |||
BC_DECLASM(min,MIN) | |||
BC_DECLASM(max,MAX) | |||
BC_DECLASM(min_fp,MIN_FP) | |||
BC_DECLASM(max_fp,MAX_FP) | |||
BC_DECLASM(abs,ABS) | |||
BC_DECLASM(sign,SIGN) | |||
BC_DECLASM(invsqrt,INVSQRT) | |||
BC_DECLASM(dbg_getstackptr,DBG_GETSTACKPTR) | |||
BC_DECLASM(booltofp,BOOLTOFP) | |||
BC_DECLASM(fptobool,FPTOBOOL) | |||
BC_DECLASM(fptobool_rev,FPTOBOOL_REV) | |||
BC_DECLASM_N(stack_push,USERSTACK_PUSH,3) | |||
BC_DECLASM_N(stack_pop,USERSTACK_POP,3) | |||
BC_DECLASM_N(stack_pop_fast,USERSTACK_POPFAST,3) | |||
BC_DECLASM_N(stack_peek,USERSTACK_PEEK,3) | |||
BC_DECLASM_N(stack_peek_int,USERSTACK_PEEK_INT,4) | |||
BC_DECLASM_N(stack_peek_top,USERSTACK_PEEK_TOP,1) | |||
BC_DECLASM_N(stack_exch,USERSTACK_EXCH,1) | |||
BC_DECLASM_N(fcall,FCALL,1) | |||
BC_DECLASM_N(1pdd,CFUNC_1PDD,1) | |||
BC_DECLASM_N(2pdd,CFUNC_2PDD,1) | |||
BC_DECLASM_N(2pdds,CFUNC_2PDDS,1) | |||
BC_DECLASM_N2(megabuf,MEGABUF,1) | |||
BC_DECLASM_N2(gmegabuf,GMEGABUF,2) | |||
#define _asm_megabuf_end EEL_BC_ENDOF(_asm_megabuf) | |||
#define _asm_gmegabuf_end EEL_BC_ENDOF(_asm_gmegabuf) | |||
BC_DECLASM_N_EXPORT(generic1parm,GENERIC1PARM,2) | |||
BC_DECLASM_N_EXPORT(generic2parm,GENERIC2PARM,2) | |||
BC_DECLASM_N_EXPORT(generic3parm,GENERIC3PARM,2) | |||
BC_DECLASM_N_EXPORT(generic1parm_retd,GENERIC1PARM_RETD,2) | |||
BC_DECLASM_N_EXPORT(generic2parm_retd,GENERIC2PARM_RETD,2) | |||
BC_DECLASM_N_EXPORT(generic3parm_retd,GENERIC3PARM_RETD,2) | |||
#define _asm_generic1parm_end EEL_BC_ENDOF(_asm_generic1parm) | |||
#define _asm_generic2parm_end EEL_BC_ENDOF(_asm_generic2parm) | |||
#define _asm_generic3parm_end EEL_BC_ENDOF(_asm_generic3parm) | |||
#define _asm_generic1parm_retd_end EEL_BC_ENDOF(_asm_generic1parm_retd) | |||
#define _asm_generic2parm_retd_end EEL_BC_ENDOF(_asm_generic2parm_retd) | |||
#define _asm_generic3parm_retd_end EEL_BC_ENDOF(_asm_generic3parm_retd) | |||
#define nseel_asm_1pdd_end EEL_BC_ENDOF(nseel_asm_1pdd) | |||
#define nseel_asm_2pdd_end EEL_BC_ENDOF(nseel_asm_2pdd) | |||
#define nseel_asm_2pdds_end EEL_BC_ENDOF(nseel_asm_2pdds) | |||
#define nseel_asm_fcall_end EEL_BC_ENDOF(nseel_asm_fcall) | |||
#define nseel_asm_band_end EEL_BC_ENDOF(nseel_asm_band) | |||
#define nseel_asm_bor_end EEL_BC_ENDOF(nseel_asm_bor) | |||
#define nseel_asm_bnot_end EEL_BC_ENDOF(nseel_asm_bnot) | |||
#define nseel_asm_bnotnot_end EEL_BC_ENDOF(nseel_asm_bnotnot) | |||
#define nseel_asm_equal_end EEL_BC_ENDOF(nseel_asm_equal) | |||
#define nseel_asm_equal_exact_end EEL_BC_ENDOF(nseel_asm_equal_exact) | |||
#define nseel_asm_notequal_end EEL_BC_ENDOF(nseel_asm_notequal) | |||
#define nseel_asm_notequal_exact_end EEL_BC_ENDOF(nseel_asm_notequal_exact) | |||
#define nseel_asm_above_end EEL_BC_ENDOF(nseel_asm_above) | |||
#define nseel_asm_beloweq_end EEL_BC_ENDOF(nseel_asm_beloweq) | |||
#define nseel_asm_min_end EEL_BC_ENDOF(nseel_asm_min) | |||
#define nseel_asm_max_end EEL_BC_ENDOF(nseel_asm_max) | |||
#define nseel_asm_abs_end EEL_BC_ENDOF(nseel_asm_abs) | |||
#define nseel_asm_min_fp_end EEL_BC_ENDOF(nseel_asm_min_fp) | |||
#define nseel_asm_max_fp_end EEL_BC_ENDOF(nseel_asm_max_fp) | |||
#define nseel_asm_sign_end EEL_BC_ENDOF(nseel_asm_sign) | |||
#define nseel_asm_invsqrt_end EEL_BC_ENDOF(nseel_asm_invsqrt) | |||
#define nseel_asm_dbg_getstackptr_end EEL_BC_ENDOF(nseel_asm_dbg_getstackptr) | |||
#define nseel_asm_add_end EEL_BC_ENDOF(nseel_asm_add) | |||
#define nseel_asm_sub_end EEL_BC_ENDOF(nseel_asm_sub) | |||
#define nseel_asm_mul_end EEL_BC_ENDOF(nseel_asm_mul) | |||
#define nseel_asm_div_end EEL_BC_ENDOF(nseel_asm_div) | |||
#define nseel_asm_and_end EEL_BC_ENDOF(nseel_asm_and) | |||
#define nseel_asm_or_end EEL_BC_ENDOF(nseel_asm_or) | |||
#define nseel_asm_or0_end EEL_BC_ENDOF(nseel_asm_or0) | |||
#define nseel_asm_xor_end EEL_BC_ENDOF(nseel_asm_xor) | |||
#define nseel_asm_add_op_end EEL_BC_ENDOF(nseel_asm_add_op) | |||
#define nseel_asm_sub_op_end EEL_BC_ENDOF(nseel_asm_sub_op) | |||
#define nseel_asm_add_op_fast_end EEL_BC_ENDOF(nseel_asm_add_op_fast) | |||
#define nseel_asm_sub_op_fast_end EEL_BC_ENDOF(nseel_asm_sub_op_fast) | |||
#define nseel_asm_mul_op_end EEL_BC_ENDOF(nseel_asm_mul_op) | |||
#define nseel_asm_mul_op_fast_end EEL_BC_ENDOF(nseel_asm_mul_op_fast) | |||
#define nseel_asm_div_op_end EEL_BC_ENDOF(nseel_asm_div_op) | |||
#define nseel_asm_div_op_fast_end EEL_BC_ENDOF(nseel_asm_div_op_fast) | |||
#define nseel_asm_and_op_end EEL_BC_ENDOF(nseel_asm_and_op) | |||
#define nseel_asm_or_op_end EEL_BC_ENDOF(nseel_asm_or_op) | |||
#define nseel_asm_xor_op_end EEL_BC_ENDOF(nseel_asm_xor_op) | |||
#define nseel_asm_uminus_end EEL_BC_ENDOF(nseel_asm_uminus) | |||
#define nseel_asm_assign_end EEL_BC_ENDOF(nseel_asm_assign) | |||
#define nseel_asm_assign_fast_end EEL_BC_ENDOF(nseel_asm_assign_fast) | |||
#define nseel_asm_assign_fast_fromfp_end EEL_BC_ENDOF(nseel_asm_assign_fast_fromfp) | |||
#define nseel_asm_assign_fromfp_end EEL_BC_ENDOF(nseel_asm_assign_fromfp) | |||
#define nseel_asm_mod_end EEL_BC_ENDOF(nseel_asm_mod) | |||
#define nseel_asm_mod_op_end EEL_BC_ENDOF(nseel_asm_mod_op) | |||
#define nseel_asm_shr_end EEL_BC_ENDOF(nseel_asm_shr) | |||
#define nseel_asm_shl_end EEL_BC_ENDOF(nseel_asm_shl) | |||
#define nseel_asm_sqr_end EEL_BC_ENDOF(nseel_asm_sqr) | |||
#define nseel_asm_booltofp_end EEL_BC_ENDOF(nseel_asm_booltofp) | |||
#define nseel_asm_fptobool_end EEL_BC_ENDOF(nseel_asm_fptobool) | |||
#define nseel_asm_fptobool_rev_end EEL_BC_ENDOF(nseel_asm_fptobool_rev) | |||
#define nseel_asm_stack_push_end EEL_BC_ENDOF(nseel_asm_stack_push) | |||
#define nseel_asm_stack_pop_end EEL_BC_ENDOF(nseel_asm_stack_pop) | |||
#define nseel_asm_stack_pop_fast_end EEL_BC_ENDOF(nseel_asm_stack_pop_fast) | |||
#define nseel_asm_stack_peek_end EEL_BC_ENDOF(nseel_asm_stack_peek) | |||
#define nseel_asm_stack_peek_int_end EEL_BC_ENDOF(nseel_asm_stack_peek_int) | |||
#define nseel_asm_stack_peek_top_end EEL_BC_ENDOF(nseel_asm_stack_peek_top) | |||
#define nseel_asm_stack_exch_end EEL_BC_ENDOF(nseel_asm_stack_exch) | |||
BC_DECL_JMP(JMP_NC) | |||
BC_DECL_JMP(JMP_IF_P1_Z) | |||
BC_DECL_JMP(JMP_IF_P1_NZ) | |||
BC_DECL(RET) | |||
BC_DECL(FXCH) | |||
BC_DECL(POP_FPSTACK) | |||
BC_DECL(PUSH_P1) | |||
BC_DECL(PUSH_P1PTR_AS_VALUE) | |||
BC_DECL(POP_FPSTACK_TOSTACK) | |||
BC_DECL(POP_FPSTACK_TO_WTP) | |||
BC_DECL(SET_P1_Z) | |||
BC_DECL(SET_P1_NZ) | |||
BC_DECL_JMP(LOOP_LOADCNT) | |||
BC_DECL_JMP(LOOP_END) | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
BC_DECL(WHILE_SETUP) | |||
BC_DECL_JMP(WHILE_END) | |||
#else | |||
BC_DECL(WHILE_END) | |||
#endif | |||
BC_DECL(WHILE_BEGIN); | |||
BC_DECL_JMP(WHILE_CHECK_RV) | |||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE (sizeof(EEL_BC_TYPE) + sizeof(INT_PTR)) | |||
#define GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE GLUE_MOV_PX_DIRECTVALUE_SIZE | |||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) | |||
{ | |||
static const EEL_BC_TYPE tab[] = { | |||
EEL_BC_MOV_FPTOP_DV, | |||
EEL_BC_MOV_P1_DV, | |||
EEL_BC_MOV_P2_DV, | |||
EEL_BC_MOV_P3_DV, | |||
}; | |||
*(EEL_BC_TYPE *)b = tab[wv+1]; | |||
*(INT_PTR *) ((char *)b + sizeof(EEL_BC_TYPE)) = v; | |||
} | |||
#define GLUE_FUNC_ENTER_SIZE 0 | |||
#define GLUE_FUNC_LEAVE_SIZE 0 | |||
static const EEL_BC_TYPE GLUE_FUNC_ENTER[1]={NULL}; | |||
static const EEL_BC_TYPE GLUE_FUNC_LEAVE[1]={NULL}; | |||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr) | |||
{ | |||
BC_DECL(_RESET_WTP) | |||
if (out) memcpy(out,&GLUE__RESET_WTP,sizeof(GLUE__RESET_WTP)); | |||
if (out) *(void **) (out+sizeof(GLUE__RESET_WTP)) = ptr; | |||
return sizeof(GLUE__RESET_WTP) + sizeof(void *); | |||
} | |||
#define GLUE_POP_PX_SIZE sizeof(EEL_BC_TYPE) | |||
static void GLUE_POP_PX(void *b, int wv) | |||
{ | |||
static const EEL_BC_TYPE tab[3] ={ | |||
EEL_BC_POP_P1, | |||
EEL_BC_POP_P2, | |||
EEL_BC_POP_P3, | |||
}; | |||
*(EEL_BC_TYPE *)b = tab[wv]; | |||
} | |||
#define GLUE_SET_PX_FROM_P1_SIZE sizeof(EEL_BC_TYPE) | |||
static void GLUE_SET_PX_FROM_P1(void *b, int wv) | |||
{ | |||
static const EEL_BC_TYPE tab[3]={ | |||
EEL_BC_NOP, | |||
EEL_BC_SET_P2_FROM_P1, | |||
EEL_BC_SET_P3_FROM_P1, | |||
}; | |||
*(EEL_BC_TYPE *)b = tab[wv]; | |||
} | |||
#define GLUE_MOVE_STACK_SIZE (sizeof(EEL_BC_TYPE) + sizeof(int)) | |||
static void GLUE_MOVE_STACK(void *b, int amt) | |||
{ | |||
*(EEL_BC_TYPE *)b = EEL_BC_MOVE_STACK; | |||
*(int *)(((EEL_BC_TYPE *)b)+1) = amt; | |||
} | |||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) (sizeof(EEL_BC_TYPE) + sizeof(int)) | |||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) | |||
{ | |||
*(EEL_BC_TYPE *)b = EEL_BC_STORE_P1_TO_STACK_AT_OFFS; | |||
*(int *)(((EEL_BC_TYPE *)b)+1) = offs; | |||
} | |||
#define GLUE_MOVE_PX_STACKPTR_SIZE sizeof(EEL_BC_TYPE) | |||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) | |||
{ | |||
static const EEL_BC_TYPE tab[3] = { | |||
EEL_BC_MOVE_STACKPTR_TO_P1, | |||
EEL_BC_MOVE_STACKPTR_TO_P2, | |||
EEL_BC_MOVE_STACKPTR_TO_P3 | |||
}; | |||
*(EEL_BC_TYPE *)b = tab[wv]; | |||
} | |||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*(EEL_BC_TYPE *)buf = EEL_BC_POP_VALUE_TO_ADDR; | |||
*(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; | |||
} | |||
return sizeof(EEL_BC_TYPE) + sizeof(void *); | |||
} | |||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*(EEL_BC_TYPE *)buf = EEL_BC_COPY_VALUE_AT_P1_TO_ADDR; | |||
*(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; | |||
} | |||
return sizeof(EEL_BC_TYPE) + sizeof(void *); | |||
} | |||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) | |||
{ | |||
int mv=5; | |||
char *p=(char*)_p; | |||
p+=sizeof(EEL_BC_TYPE); | |||
while (*(INT_PTR*)p && mv-- > 0) p++; | |||
if (!mv) return (unsigned char *)p; | |||
*(INT_PTR *)p = newv; | |||
return (unsigned char *) p + sizeof(INT_PTR) - sizeof(EEL_BC_TYPE); | |||
} | |||
#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(EEL_BC_TYPE) | |||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv) | |||
{ | |||
static const EEL_BC_TYPE tab[3]={ | |||
EEL_BC_SET_P1_FROM_WTP, | |||
EEL_BC_SET_P2_FROM_WTP, | |||
EEL_BC_SET_P3_FROM_WTP, | |||
}; | |||
*(EEL_BC_TYPE *)b = tab[wv]; | |||
} | |||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*(EEL_BC_TYPE *)buf = EEL_BC_POP_FPSTACK_TO_PTR; | |||
*(void **) (buf+sizeof(EEL_BC_TYPE)) = destptr; | |||
} | |||
return sizeof(EEL_BC_TYPE) + sizeof(void *); | |||
} | |||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE sizeof(EEL_BC_TYPE) | |||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) | |||
{ | |||
static const EEL_BC_TYPE tab[3] = { | |||
EEL_BC_PUSH_VAL_AT_P1_TO_FPSTACK, | |||
EEL_BC_PUSH_VAL_AT_P2_TO_FPSTACK, | |||
EEL_BC_PUSH_VAL_AT_P3_TO_FPSTACK, | |||
}; | |||
*(EEL_BC_TYPE *)b = tab[wv]; | |||
} | |||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) | |||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) | |||
{ | |||
GLUE_SET_PX_FROM_WTP(buf,wv); | |||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); | |||
}; | |||
static unsigned char GLUE_POP_STACK_TO_FPSTACK[1] = { 0 }; // todo | |||
#define GLUE_INLINE_LOOPS | |||
// end of bytecode glue, now for stubbage | |||
static void *GLUE_realAddress(void *fn, void *fn_e, int *size) | |||
{ | |||
*size = (char *)fn_e - (char *)fn; | |||
return fn; | |||
} | |||
#endif |
@@ -0,0 +1,285 @@ | |||
#ifndef _NSEEL_GLUE_PPC_H_ | |||
#define _NSEEL_GLUE_PPC_H_ | |||
#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support | |||
#define GLUE_MAX_JMPSIZE 30000 // maximum relative jump size for this arch (if not defined, any jump is possible) | |||
// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction. | |||
// on PPC the offset needs to be from the start of the instruction (hence +4), and also the low two bits are flags so | |||
// we make sure they are clear (they should always be clear, anyway, since we always generate 4 byte instructions) | |||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((short *)(endOfInstruction))[-1] = ((offset) + 4) & 0xFFFC) | |||
static const unsigned char GLUE_JMP_NC[] = { 0x48,0, 0, 0, }; // b <offset> | |||
static const unsigned int GLUE_JMP_IF_P1_Z[]= | |||
{ | |||
0x2f830000, //cmpwi cr7, r3, 0 | |||
0x419e0000, // beq cr7, offset-bytes-from-startofthisinstruction | |||
}; | |||
static const unsigned int GLUE_JMP_IF_P1_NZ[]= | |||
{ | |||
0x2f830000, //cmpwi cr7, r3, 0 | |||
0x409e0000, // bne cr7, offset-bytes-from-startofthisinstruction | |||
}; | |||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 8 | |||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) | |||
{ | |||
static const unsigned short tab[3][2] = { | |||
{0x3C60, 0x6063}, // addis r3, r0, hw -- ori r3,r3, lw | |||
{0x3DC0, 0x61CE}, // addis r14, r0, hw -- ori r14, r14, lw | |||
{0x3DE0, 0x61EF}, // addis r15, r0, hw -- oris r15, r15, lw | |||
}; | |||
unsigned int uv=(unsigned int)v; | |||
unsigned short *p=(unsigned short *)b; | |||
*p++ = tab[wv][0]; // addis rX, r0, hw | |||
*p++ = (uv>>16)&0xffff; | |||
*p++ = tab[wv][1]; // ori rX, rX, lw | |||
*p++ = uv&0xffff; | |||
} | |||
// mflr r5 | |||
// stwu r5, -16(r1) | |||
const static unsigned int GLUE_FUNC_ENTER[2] = { 0x7CA802A6, 0x94A1FFF0 }; | |||
#define GLUE_FUNC_ENTER_SIZE 8 | |||
// lwz r5, 0(r1) | |||
// addi r1, r1, 16 | |||
// mtlr r5 | |||
const static unsigned int GLUE_FUNC_LEAVE[3] = { 0x80A10000, 0x38210010, 0x7CA803A6 }; | |||
#define GLUE_FUNC_LEAVE_SIZE 12 | |||
const static unsigned int GLUE_RET[]={0x4E800020}; // blr | |||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr) | |||
{ | |||
const static unsigned int GLUE_SET_WTP_FROM_R17=0x7E308B78; // mr r16 (dest), r17 (src) | |||
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R17,sizeof(GLUE_SET_WTP_FROM_R17)); | |||
return sizeof(GLUE_SET_WTP_FROM_R17); | |||
} | |||
// stwu r3, -16(r1) | |||
const static unsigned int GLUE_PUSH_P1[1]={ 0x9461FFF0}; | |||
#define GLUE_POP_PX_SIZE 8 | |||
static void GLUE_POP_PX(void *b, int wv) | |||
{ | |||
static const unsigned int tab[3] ={ | |||
0x80610000, // lwz r3, 0(r1) | |||
0x81c10000, // lwz r14, 0(r1) | |||
0x81e10000, // lwz r15, 0(r1) | |||
}; | |||
((unsigned int *)b)[0] = tab[wv]; | |||
((unsigned int *)b)[1] = 0x38210010; // addi r1,r1, 16 | |||
} | |||
#define GLUE_SET_PX_FROM_P1_SIZE 4 | |||
static void GLUE_SET_PX_FROM_P1(void *b, int wv) | |||
{ | |||
static const unsigned int tab[3]={ | |||
0x7c631b78, // never used: mr r3, r3 | |||
0x7c6e1b78, // mr r14, r3 | |||
0x7c6f1b78, // mr r15, r3 | |||
}; | |||
*(unsigned int *)b = tab[wv]; | |||
} | |||
// lfd f2, 0(r3) | |||
// stfdu f2, -16(r1) | |||
static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] = { 0xC8430000, 0xDC41FFF0 }; | |||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) | |||
{ | |||
// lfd f2, 0(r1) | |||
// addi r1,r1,16 | |||
// GLUE_MOV_PX_DIRECTVALUE_GEN / GLUE_MOV_PX_DIRECTVALUE_SIZE (r3) | |||
// stfd f2, 0(r3) | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
*bufptr++ = 0xC8410000; | |||
*bufptr++ = 0x38210010; | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xd8430000; | |||
} | |||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE + 4; | |||
} | |||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
// lfd f2, 0(r3) | |||
// GLUE_MOV_PX_DIRECTVALUE_GEN / GLUE_MOV_PX_DIRECTVALUE_SIZE (r3) | |||
// stfd f2, 0(r3) | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
*bufptr++ = 0xc8430000; | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xd8430000; | |||
} | |||
return 4 + GLUE_MOV_PX_DIRECTVALUE_SIZE + 4; | |||
} | |||
static void GLUE_CALL_CODE(INT_PTR bp, INT_PTR cp, INT_PTR rt) | |||
{ | |||
static const double consttab[] = { | |||
NSEEL_CLOSEFACTOR, | |||
4503601774854144.0 /* 0x43300000, 0x80000000, used for integer conversion*/, | |||
}; | |||
// we could have r18 refer to the current user-stack pointer, someday, perhaps | |||
__asm__( | |||
"subi r1, r1, 128\n" | |||
"stfd f31, 8(r1)\n" | |||
"stfd f30, 16(r1)\n" | |||
"stmw r13, 32(r1)\n" | |||
"mtctr %0\n" | |||
"mr r17, %1\n" | |||
"mr r13, %2\n" | |||
"lfd f31, 0(%3)\n" | |||
"lfd f30, 8(%3)\n" | |||
"subi r17, r17, 8\n" | |||
"mflr r0\n" | |||
"stw r0, 24(r1)\n" | |||
"bctrl\n" | |||
"lwz r0, 24(r1)\n" | |||
"mtlr r0\n" | |||
"lmw r13, 32(r1)\n" | |||
"lfd f31, 8(r1)\n" | |||
"lfd f30, 16(r1)\n" | |||
"addi r1, r1, 128\n" | |||
::"r" (cp), "r" (bp), "r" (rt), "r" (consttab)); | |||
}; | |||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) | |||
{ | |||
// 64 bit ppc would take some work | |||
unsigned int *p=(unsigned int *)_p; | |||
while ((p[0]&0x0000FFFF) != 0x0000dead && | |||
(p[1]&0x0000FFFF) != 0x0000beef) p++; | |||
p[0] = (p[0]&0xFFFF0000) | (((newv)>>16)&0xFFFF); | |||
p[1] = (p[1]&0xFFFF0000) | ((newv)&0xFFFF); | |||
return (unsigned char *)(p+1); | |||
} | |||
#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int) | |||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv) | |||
{ | |||
static const unsigned int tab[3]={ | |||
0x7e038378, // mr r3, r16 | |||
0x7e0e8378, // mr r14, r16 | |||
0x7e0f8378, // mr r15, r16 | |||
}; | |||
*(unsigned int *)b = tab[wv]; | |||
} | |||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
// set r3 to destptr | |||
// stfd f1, 0(r3) | |||
if (buf) | |||
{ | |||
unsigned int *bufptr = (unsigned int *)buf; | |||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0); | |||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4; | |||
*bufptr++ = 0xD8230000; // stfd f1, 0(r3) | |||
} | |||
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int); | |||
} | |||
#define GLUE_POP_FPSTACK_SIZE 0 | |||
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack | |||
static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = { | |||
0xdc21fff0, // stfdu f1, -16(r1) | |||
}; | |||
static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = { | |||
0xdc300008, // stfdu f1, 8(r16) | |||
}; | |||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 | |||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) | |||
{ | |||
static const unsigned int tab[3] = { | |||
0xC8230000, // lfd f1, 0(r3) | |||
0xC82E0000, // lfd f1, 0(r14) | |||
0xC82F0000, // lfd f1, 0(r15) | |||
}; | |||
*(unsigned int *)b = tab[wv]; | |||
} | |||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE) | |||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) | |||
{ | |||
memcpy(buf,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); | |||
GLUE_SET_PX_FROM_WTP(buf + sizeof(GLUE_POP_FPSTACK_TO_WTP),wv); // ppc preincs the WTP, so we do this after | |||
}; | |||
static unsigned int GLUE_POP_STACK_TO_FPSTACK[1] = { 0 }; // todo | |||
static const unsigned int GLUE_SET_P1_Z[] = { 0x38600000 }; // li r3, 0 | |||
static const unsigned int GLUE_SET_P1_NZ[] = { 0x38600001 }; // li r3, 1 | |||
static void *GLUE_realAddress(void *fn, int *size) | |||
{ | |||
// magic numbers: mr r0,r0 ; mr r1,r1 ; mr r2, r2 | |||
static const unsigned char sig[12] = { 0x7c, 0x00, 0x03, 0x78, 0x7c, 0x21, 0x0b, 0x78, 0x7c, 0x42, 0x13, 0x78 }; | |||
unsigned char *p = (unsigned char *)fn; | |||
while (memcmp(p,sig,sizeof(sig))) p+=4; | |||
p+=sizeof(sig); | |||
fn = p; | |||
while (memcmp(p,sig,sizeof(sig))) p+=4; | |||
*size = p - (unsigned char *)fn; | |||
return fn; | |||
} | |||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 4 | |||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) | |||
{ | |||
// limited to 32k offset | |||
*(unsigned int *)b = 0x90610000 + (offs&0xffff); | |||
} | |||
#define GLUE_MOVE_PX_STACKPTR_SIZE 4 | |||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) | |||
{ | |||
static const unsigned int tab[3] = | |||
{ | |||
0x7c230b78, // mr r3, r1 | |||
0x7c2e0b78, // mr r14, r1 | |||
0x7c2f0b78, // mr r15, r1 | |||
}; | |||
* (unsigned int *)b = tab[wv]; | |||
} | |||
#define GLUE_MOVE_STACK_SIZE 4 | |||
static void GLUE_MOVE_STACK(void *b, int amt) | |||
{ | |||
// this should be updated to allow for more than 32k moves, but no real need | |||
((unsigned int *)b)[0] = 0x38210000 + (amt&0xffff); // addi r1,r1, amt | |||
} | |||
// end of ppc | |||
#endif |
@@ -0,0 +1,678 @@ | |||
#ifndef _NSEEL_GLUE_X86_H_ | |||
#define _NSEEL_GLUE_X86_H_ | |||
#define GLUE_MAX_FPSTACK_SIZE 8 | |||
// endOfInstruction is end of jump with relative offset, offset is offset from end of instruction to jump to | |||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (offset)) | |||
static const unsigned char GLUE_JMP_NC[] = { 0xE9, 0,0,0,0, }; // jmp<offset> | |||
static const unsigned char GLUE_JMP_IF_P1_Z[] = {0x85, 0xC0, 0x0F, 0x84, 0,0,0,0 }; // test eax, eax, jz | |||
static const unsigned char GLUE_JMP_IF_P1_NZ[] = {0x85, 0xC0, 0x0F, 0x85, 0,0,0,0 }; // test eax, eax, jnz | |||
#define GLUE_FUNC_ENTER_SIZE 0 | |||
#define GLUE_FUNC_LEAVE_SIZE 0 | |||
const static unsigned int GLUE_FUNC_ENTER[1]; | |||
const static unsigned int GLUE_FUNC_LEAVE[1]; | |||
// x86 | |||
// stack is 16 byte aligned | |||
// when pushing values to stack, alignment pushed first, then value (value is at the lower address) | |||
// when pushing pointers to stack, alignment pushed first, then pointer (pointer is at the lower address) | |||
static const unsigned char GLUE_PUSH_P1PTR_AS_VALUE[] = | |||
{ | |||
0x83, 0xEC, 8, /* sub esp, 8 */ | |||
0xff, 0x70, 0x4, /* push dword [eax+4] */ | |||
0xff, 0x30, /* push dword [eax] */ | |||
}; | |||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0xB8; *(void **) buf = destptr; buf+=4; // mov eax, directvalue | |||
*buf++ = 0x8f; *buf++ = 0x00; // pop dword [eax] | |||
*buf++ = 0x8f; *buf++ = 0x40; *buf++ = 4; // pop dword [eax+4] | |||
*buf++ = 0x59; // pop ecx (alignment) | |||
*buf++ = 0x59; // pop ecx (alignment) | |||
} | |||
return 12; | |||
} | |||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0x8B; *buf++ = 0x38; // mov edi, [eax] | |||
*buf++ = 0x8B; *buf++ = 0x48; *buf++ = 0x04; // mov ecx, [eax+4] | |||
*buf++ = 0xB8; *(void **) buf = destptr; buf+=4; // mov eax, directvalue | |||
*buf++ = 0x89; *buf++ = 0x38; // mov [eax], edi | |||
*buf++ = 0x89; *buf++ = 0x48; *buf++ = 0x04; // mov [eax+4], ecx | |||
} | |||
return 2 + 3 + 5 + 2 + 3; | |||
} | |||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0xB8; *(void **) buf = destptr; buf+=4; // mov eax, directvalue | |||
*buf++ = 0xDD; *buf++ = 0x18; // fstp qword [eax] | |||
} | |||
return 1+4+2; | |||
} | |||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 5 | |||
#define GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE 6 // length when wv == -1 | |||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv) | |||
{ | |||
if (wv==-1) | |||
{ | |||
const static unsigned char t[2] = {0xDD, 0x05}; | |||
memcpy(b,t,2); | |||
b= ((unsigned char *)b)+2; | |||
} | |||
else | |||
{ | |||
const static unsigned char tab[3] = { | |||
0xB8 /* mov eax, dv*/, | |||
0xBF /* mov edi, dv */ , | |||
0xB9 /* mov ecx, dv */ | |||
}; | |||
*((unsigned char *)b) = tab[wv]; // mov eax, dv | |||
b= ((unsigned char *)b)+1; | |||
} | |||
*(INT_PTR *)b = v; | |||
} | |||
const static unsigned char GLUE_PUSH_P1[4]={0x83, 0xEC, 12, 0x50}; // sub esp, 12, push eax | |||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 7 | |||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) | |||
{ | |||
((unsigned char *)b)[0] = 0x89; // mov [esp+offs], eax | |||
((unsigned char *)b)[1] = 0x84; | |||
((unsigned char *)b)[2] = 0x24; | |||
*(int *)((unsigned char *)b+3) = offs; | |||
} | |||
#define GLUE_MOVE_PX_STACKPTR_SIZE 2 | |||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_MOVE_PX_STACKPTR_SIZE]= | |||
{ | |||
{ 0x89, 0xe0 }, // mov eax, esp | |||
{ 0x89, 0xe7 }, // mov edi, esp | |||
{ 0x89, 0xe1 }, // mov ecx, esp | |||
}; | |||
memcpy(b,tab[wv],GLUE_MOVE_PX_STACKPTR_SIZE); | |||
} | |||
#define GLUE_MOVE_STACK_SIZE 6 | |||
static void GLUE_MOVE_STACK(void *b, int amt) | |||
{ | |||
((unsigned char *)b)[0] = 0x81; | |||
if (amt <0) | |||
{ | |||
((unsigned char *)b)[1] = 0xEC; | |||
*(int *)((char*)b+2) = -amt; // sub esp, -amt | |||
} | |||
else | |||
{ | |||
((unsigned char *)b)[1] = 0xc4; | |||
*(int *)((char*)b+2) = amt; // add esp, amt | |||
} | |||
} | |||
#define GLUE_POP_PX_SIZE 4 | |||
static void GLUE_POP_PX(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_POP_PX_SIZE]= | |||
{ | |||
{0x58,/*pop eax*/ 0x83, 0xC4, 12 /* add esp, 12*/}, | |||
{0x5F,/*pop edi*/ 0x83, 0xC4, 12}, | |||
{0x59,/*pop ecx*/ 0x83, 0xC4, 12}, | |||
}; | |||
memcpy(b,tab[wv],GLUE_POP_PX_SIZE); | |||
} | |||
#define GLUE_SET_PX_FROM_P1_SIZE 2 | |||
static void GLUE_SET_PX_FROM_P1(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_SET_PX_FROM_P1_SIZE]={ | |||
{0x90,0x90}, // should never be used! (nopnop) | |||
{0x89,0xC7}, // mov edi, eax | |||
{0x89,0xC1}, // mov ecx, eax | |||
}; | |||
memcpy(b,tab[wv],GLUE_SET_PX_FROM_P1_SIZE); | |||
} | |||
#define GLUE_POP_FPSTACK_SIZE 2 | |||
static const unsigned char GLUE_POP_FPSTACK[2] = { 0xDD, 0xD8 }; // fstp st0 | |||
static const unsigned char GLUE_POP_FPSTACK_TOSTACK[] = { | |||
0x83, 0xEC, 16, // sub esp, 16 | |||
0xDD, 0x1C, 0x24 // fstp qword (%esp) | |||
}; | |||
static const unsigned char GLUE_POP_STACK_TO_FPSTACK[] = { | |||
0xDD, 0x04, 0x24, // fld qword (%esp) | |||
0x83, 0xC4, 16 // add esp, 16 | |||
}; | |||
static const unsigned char GLUE_POP_FPSTACK_TO_WTP[] = { | |||
0xDD, 0x1E, /* fstp qword [esi] */ | |||
0x83, 0xC6, 8, /* add esi, 8 */ | |||
}; | |||
#define GLUE_SET_PX_FROM_WTP_SIZE 2 | |||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_SET_PX_FROM_WTP_SIZE]={ | |||
{0x89,0xF0}, // mov eax, esi | |||
{0x89,0xF7}, // mov edi, esi | |||
{0x89,0xF1}, // mov ecx, esi | |||
}; | |||
memcpy(b,tab[wv],GLUE_SET_PX_FROM_WTP_SIZE); | |||
} | |||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 2 | |||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE]={ | |||
{0xDD,0x00}, // fld qword [eax] | |||
{0xDD,0x07}, // fld qword [edi] | |||
{0xDD,0x01}, // fld qword [ecx] | |||
}; | |||
memcpy(b,tab[wv],GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE); | |||
} | |||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (GLUE_SET_PX_FROM_WTP_SIZE + sizeof(GLUE_POP_FPSTACK_TO_WTP)) | |||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) | |||
{ | |||
GLUE_SET_PX_FROM_WTP(buf,wv); | |||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); | |||
}; | |||
const static unsigned char GLUE_RET=0xC3; | |||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr) | |||
{ | |||
if (out) | |||
{ | |||
*out++ = 0xBE; // mov esi, constant | |||
memcpy(out,&ptr,sizeof(void *)); | |||
out+=sizeof(void *); | |||
} | |||
return 1+sizeof(void *); | |||
} | |||
#ifdef _MSC_VER | |||
#pragma warning(push) | |||
#pragma warning(disable: 4731) | |||
#endif | |||
#define GLUE_TABPTR_IGNORED | |||
#define GLUE_CALL_CODE(bp, cp, rt) do { \ | |||
if (h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) eel_callcode32_fast(cp, rt); \ | |||
else eel_callcode32(cp, rt);\ | |||
} while(0) | |||
static void eel_callcode32(INT_PTR cp, INT_PTR ramptr) | |||
{ | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
short oldsw, newsw; | |||
#endif | |||
#ifdef _MSC_VER | |||
__asm | |||
{ | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
fnstcw [oldsw] | |||
mov ax, [oldsw] | |||
or ax, 0x23F // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions | |||
mov [newsw], ax | |||
fldcw [newsw] | |||
#endif | |||
mov eax, cp | |||
mov ebx, ramptr | |||
pushad | |||
mov ebp, esp | |||
and esp, -16 | |||
// on win32, which _MSC_VER implies, we keep things aligned to 16 bytes, and if we call a win32 function, | |||
// the stack is 16 byte aligned before the call, meaning that if calling a function with no frame pointer, | |||
// the stack would be aligned to a 16 byte boundary +4, which isn't good for performance. Having said that, | |||
// normally we compile with frame pointers (which brings that to 16 byte + 8, which is fine), or ICC, which | |||
// for nontrivial functions will align the stack itself (for very short functions, it appears to weigh the | |||
// cost of aligning the stack vs that of the slower misaligned double accesses). | |||
// it may be worthwhile (at some point) to put some logic in the code that calls out to functions | |||
// (generic1parm etc) to detect which alignment would be most optimal. | |||
sub esp, 12 | |||
call eax | |||
mov esp, ebp | |||
popad | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
fldcw [oldsw] | |||
#endif | |||
}; | |||
#else // gcc x86 | |||
__asm__( | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
"fnstcw %2\n" | |||
"movw %2, %%ax\n" | |||
"orw $0x23F, %%ax\n" // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions | |||
"movw %%ax, %3\n" | |||
"fldcw %3\n" | |||
#endif | |||
"pushl %%ebx\n" | |||
"movl %%ecx, %%ebx\n" | |||
"pushl %%ebp\n" | |||
"movl %%esp, %%ebp\n" | |||
"andl $-16, %%esp\n" // align stack to 16 bytes | |||
"subl $12, %%esp\n" // call will push 4 bytes on stack, align for that | |||
"call *%%edx\n" | |||
"leave\n" | |||
"popl %%ebx\n" | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
"fldcw %2\n" | |||
#endif | |||
:: | |||
"d" (cp), "c" (ramptr) | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
, "m" (oldsw), "m" (newsw) | |||
#endif | |||
: "%eax","%esi","%edi"); | |||
#endif //gcc x86 | |||
} | |||
void eel_enterfp(int s[2]) | |||
{ | |||
#ifdef _MSC_VER | |||
__asm | |||
{ | |||
mov ecx, s | |||
fnstcw [ecx] | |||
mov ax, [ecx] | |||
or ax, 0x23F // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions | |||
mov [ecx+4], ax | |||
fldcw [ecx+4] | |||
}; | |||
#else | |||
__asm__( | |||
"fnstcw (%%ecx)\n" | |||
"movw (%%ecx), %%ax\n" | |||
"orw $0x23F, %%ax\n" // 53 or 64 bit precision (depending on whether 0x100 is set), and masking all exceptions | |||
"movw %%ax, 4(%%ecx)\n" | |||
"fldcw 4(%%ecx)\n" | |||
:: "c" (s) : "%eax"); | |||
#endif | |||
} | |||
void eel_leavefp(int s[2]) | |||
{ | |||
#ifdef _MSC_VER | |||
__asm | |||
{ | |||
mov ecx, s | |||
fldcw [ecx] | |||
}; | |||
#else | |||
__asm__( | |||
"fldcw (%%ecx)\n" | |||
:: "c" (s) : "%eax"); | |||
#endif | |||
} | |||
static void eel_callcode32_fast(INT_PTR cp, INT_PTR ramptr) | |||
{ | |||
#ifdef _MSC_VER | |||
__asm | |||
{ | |||
mov eax, cp | |||
mov ebx, ramptr | |||
pushad | |||
mov ebp, esp | |||
and esp, -16 | |||
// on win32, which _MSC_VER implies, we keep things aligned to 16 bytes, and if we call a win32 function, | |||
// the stack is 16 byte aligned before the call, meaning that if calling a function with no frame pointer, | |||
// the stack would be aligned to a 16 byte boundary +4, which isn't good for performance. Having said that, | |||
// normally we compile with frame pointers (which brings that to 16 byte + 8, which is fine), or ICC, which | |||
// for nontrivial functions will align the stack itself (for very short functions, it appears to weigh the | |||
// cost of aligning the stack vs that of the slower misaligned double accesses). | |||
// it may be worthwhile (at some point) to put some logic in the code that calls out to functions | |||
// (generic1parm etc) to detect which alignment would be most optimal. | |||
sub esp, 12 | |||
call eax | |||
mov esp, ebp | |||
popad | |||
}; | |||
#else // gcc x86 | |||
__asm__( | |||
"pushl %%ebx\n" | |||
"movl %%ecx, %%ebx\n" | |||
"pushl %%ebp\n" | |||
"movl %%esp, %%ebp\n" | |||
"andl $-16, %%esp\n" // align stack to 16 bytes | |||
"subl $12, %%esp\n" // call will push 4 bytes on stack, align for that | |||
"call *%%edx\n" | |||
"leave\n" | |||
"popl %%ebx\n" | |||
:: | |||
"d" (cp), "c" (ramptr) | |||
: "%eax","%esi","%edi"); | |||
#endif //gcc x86 | |||
} | |||
#ifdef _MSC_VER | |||
#pragma warning(pop) | |||
#endif | |||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) | |||
{ | |||
char *p=(char*)_p; | |||
INT_PTR scan = 0xFEFEFEFE; | |||
while (*(INT_PTR *)p != scan) p++; | |||
*(INT_PTR *)p = newv; | |||
return (unsigned char *) (((INT_PTR*)p)+1); | |||
} | |||
#define INT_TO_LECHARS(x) ((x)&0xff),(((x)>>8)&0xff), (((x)>>16)&0xff), (((x)>>24)&0xff) | |||
#define GLUE_INLINE_LOOPS | |||
#define GLUE_LOOP_LOADCNT_SIZE (nseel_has_sse3() ? sizeof(GLUE_LOOP_LOADCNT_SSE3) : sizeof(GLUE_LOOP_LOADCNT_NOSSE3)) | |||
#define GLUE_LOOP_LOADCNT (nseel_has_sse3() ? GLUE_LOOP_LOADCNT_SSE3 : GLUE_LOOP_LOADCNT_NOSSE3) | |||
static const unsigned char GLUE_LOOP_LOADCNT_SSE3[]={ | |||
0xdb, 0x0e, // fisttp dword [esi] | |||
0x8B, 0x0E, // mov ecx, [esi] | |||
0x81, 0xf9, 1,0,0,0, // cmp ecx, 1 | |||
0x0F, 0x8C, 0,0,0,0, // JL <skipptr> | |||
}; | |||
static const unsigned char GLUE_LOOP_LOADCNT_NOSSE3[]={ | |||
0xd9, 0x7e, 0x04, // fnstcw [esi+4] | |||
0x66, 0x8b, 0x46, 0x04, // mov ax, [esi+4] | |||
0x66, 0x0d, 0x00, 0x0c, // or ax, 0xC00 | |||
0x66, 0x89, 0x46, 0x08, // mov [esi+8], ax | |||
0xd9, 0x6e, 0x08, // fldcw [esi+8] | |||
0xDB, 0x1E, // fistp dword [esi] | |||
0xd9, 0x6e, 0x04, // fldcw [esi+4] | |||
0x8B, 0x0E, // mov ecx, [esi] | |||
0x81, 0xf9, 1,0,0,0, // cmp ecx, 1 | |||
0x0F, 0x8C, 0,0,0,0, // JL <skipptr> | |||
}; | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
#define GLUE_LOOP_CLAMPCNT_SIZE sizeof(GLUE_LOOP_CLAMPCNT) | |||
static const unsigned char GLUE_LOOP_CLAMPCNT[]={ | |||
0x81, 0xf9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // cmp ecx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
0x0F, 0x8C, 5,0,0,0, // JL over-the-mov | |||
0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // mov ecx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
}; | |||
#else | |||
#define GLUE_LOOP_CLAMPCNT_SIZE 0 | |||
#define GLUE_LOOP_CLAMPCNT "" | |||
#endif | |||
#define GLUE_LOOP_BEGIN_SIZE sizeof(GLUE_LOOP_BEGIN) | |||
static const unsigned char GLUE_LOOP_BEGIN[]={ | |||
0x56, //push esi | |||
0x51, // push ecx | |||
0x81, 0xEC, 0x08, 0,0,0, // sub esp, 8 | |||
}; | |||
static const unsigned char GLUE_LOOP_END[]={ | |||
0x81, 0xC4, 0x08, 0,0,0, // add esp, 8 | |||
0x59, //pop ecx | |||
0x5E, // pop esi | |||
0x49, // dec ecx | |||
0x0f, 0x85, 0,0,0,0, // jnz ... | |||
}; | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
#define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) | |||
static const unsigned char GLUE_WHILE_SETUP[]={ | |||
0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // mov ecx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
}; | |||
static const unsigned char GLUE_WHILE_BEGIN[]={ | |||
0x56, //push esi | |||
0x51, // push ecx | |||
0x81, 0xEC, 0x08, 0,0,0, // sub esp, 8 | |||
}; | |||
static const unsigned char GLUE_WHILE_END[]={ | |||
0x81, 0xC4, 0x08, 0,0,0, // add esp, 8 | |||
0x59, //pop ecx | |||
0x5E, // pop esi | |||
0x49, // dec ecx | |||
0x0f, 0x84, 0,0,0,0, // jz endpt | |||
}; | |||
#else | |||
#define GLUE_WHILE_SETUP_SIZE 0 | |||
#define GLUE_WHILE_SETUP "" | |||
#define GLUE_WHILE_END_NOJUMP | |||
static const unsigned char GLUE_WHILE_BEGIN[]={ | |||
0x56, //push esi | |||
0x81, 0xEC, 12, 0,0,0, // sub esp, 12 | |||
}; | |||
static const unsigned char GLUE_WHILE_END[]={ | |||
0x81, 0xC4, 12, 0,0,0, // add esp, 12 | |||
0x5E, // pop esi | |||
}; | |||
#endif | |||
static const unsigned char GLUE_WHILE_CHECK_RV[] = { | |||
0x85, 0xC0, // test eax, eax | |||
0x0F, 0x85, 0,0,0,0 // jnz looppt | |||
}; | |||
static const unsigned char GLUE_SET_P1_Z[] = { 0x29, 0xC0 }; // sub eax, eax | |||
static const unsigned char GLUE_SET_P1_NZ[] = { 0xb0, 0x01 }; // mov al, 1 | |||
static const unsigned char GLUE_FXCH[] = {0xd9, 0xc9}; | |||
#define GLUE_HAS_FLDZ | |||
static const unsigned char GLUE_FLDZ[] = {0xd9, 0xee}; | |||
#define GLUE_HAS_FLD1 | |||
static const unsigned char GLUE_FLD1[] = {0xd9, 0xe8}; | |||
static EEL_F negativezeropointfive=-0.5f; | |||
static EEL_F onepointfive=1.5f; | |||
#define GLUE_INVSQRT_NEEDREPL &negativezeropointfive, &onepointfive, | |||
#define GLUE_HAS_NATIVE_TRIGSQRTLOG | |||
void nseel_asm_or(void); | |||
void nseel_asm_or0(void); | |||
void nseel_asm_or_op(void); | |||
void nseel_asm_and(void); | |||
void nseel_asm_and_op(void); | |||
void nseel_asm_xor(void); | |||
void nseel_asm_xor_op(void); | |||
void nseel_asm_shl(void); | |||
void nseel_asm_shr(void); | |||
void nseel_asm_mod(void); | |||
void nseel_asm_mod_op(void); | |||
void nseel_asm_stack_peek(void); | |||
void _asm_gmegabuf(void); | |||
void _asm_megabuf(void); | |||
static struct roundinftab { | |||
void *fn; | |||
char istores; // number of fistp's | |||
char flag; // 0=fistpll, 1=fistpl, 2=fistpl 4(%esp) | |||
int newsz; | |||
void *newfn; | |||
} s_round_fixes[] = { | |||
{ nseel_asm_or, 2, }, | |||
{ nseel_asm_or_op, 2, }, | |||
{ nseel_asm_or0, 1, }, | |||
{ nseel_asm_and, 2, }, | |||
{ nseel_asm_and_op, 2, }, | |||
{ nseel_asm_xor, 2, }, | |||
{ nseel_asm_xor_op, 2, }, | |||
{ nseel_asm_shl, 2, 1, }, | |||
{ nseel_asm_shr, 2, 1, }, | |||
{ nseel_asm_mod, 2, 1, }, | |||
{ nseel_asm_mod_op, 2, 1, }, | |||
{ nseel_asm_stack_peek, 1, 1, }, | |||
{ _asm_megabuf, 1, 1, }, | |||
{ _asm_gmegabuf, 1, 2, }, | |||
}; | |||
static void eel_fixup_sse3(unsigned char *p, unsigned char *endp, int np, int flag) | |||
{ | |||
const int isz = flag == 2 ? 3 : 2; | |||
while (p+isz <= endp && np > 0) | |||
{ | |||
if (flag == 0 && p[0] == 0xdf && (p[1]&0xbe) == 0x3e) | |||
{ | |||
*p++ = 0xdd; | |||
*p -= 0x30; | |||
if (*p & 0x40) p++; | |||
np--; | |||
} | |||
else if (flag == 1 && p[0] == 0xdb && (p[1]&0xbe) == 0x1e) | |||
{ | |||
*++p -= 0x10; | |||
if (*p & 0x40) p++; | |||
np--; | |||
} | |||
else if (flag == 2 && p[0] == 0xdb && (p[1]&0xbf) == 0x1c && p[2] == 0x24) | |||
{ | |||
*++p -= 0x10; | |||
if (*p & 0x40) p++; | |||
p++; | |||
np--; | |||
} | |||
p++; | |||
} | |||
WDL_ASSERT(np == 0); | |||
} | |||
static int nseel_has_sse3() | |||
{ | |||
static char c; | |||
if (!c) | |||
{ | |||
int features = 1; | |||
#ifdef _MSC_VER | |||
__asm { | |||
mov eax, 1 | |||
cpuid | |||
mov [features], ecx | |||
}; | |||
#else | |||
__asm__( | |||
"movl $1, %%eax\n" | |||
"pushl %%ebx\n" | |||
"pushl %%edx\n" | |||
"cpuid\n" | |||
"popl %%edx\n" | |||
"popl %%ebx\n" | |||
: "=c" (features) : : "%eax"); | |||
#endif | |||
c=(features&1) ? 1 : -1; | |||
} | |||
return c>0; | |||
} | |||
static void *GLUE_realAddress(void *fn, int *size) | |||
{ | |||
static const unsigned char sig[12] = { 0x89, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; | |||
unsigned char *p = (unsigned char *)fn; | |||
size_t rmatch; | |||
const size_t nmatch = sizeof(s_round_fixes) / sizeof(s_round_fixes[0]); | |||
for (rmatch = 0; rmatch < nmatch && s_round_fixes[rmatch].fn != fn; rmatch++); | |||
if (rmatch < nmatch && s_round_fixes[rmatch].newfn && s_round_fixes[rmatch].newsz) | |||
{ | |||
*size = s_round_fixes[rmatch].newsz; | |||
return s_round_fixes[rmatch].newfn; | |||
} | |||
#if defined(_DEBUG) && defined(_MSC_VER) | |||
if (*p == 0xE9) // this means jump to the following address (debug stub) | |||
{ | |||
p += 5 + *(int *)(p+1); | |||
} | |||
#endif | |||
while (memcmp(p,sig,sizeof(sig))) p++; | |||
p+=sizeof(sig); | |||
fn = p; | |||
while (memcmp(p,sig,sizeof(sig))) p++; | |||
*size = p - (unsigned char *)fn; | |||
if (rmatch < nmatch) | |||
{ | |||
static const unsigned char prefix[] = { | |||
0xd9, 0x7e, 0x10, // fnstcw [esi+16] | |||
0x66, 0x8b, 0x46, 0x10, // mov ax, [esi+16] | |||
0x66, 0x0d, 0x00, 0x0c, // or ax, 0xC00 | |||
0x66, 0x89, 0x46, 0x14, // mov [esi+20], ax | |||
0xd9, 0x6e, 0x14, // fldcw [esi+20] | |||
}; | |||
static const unsigned char postfix[] = { | |||
0xd9, 0x6e, 0x10, // fldcw [esi+16] | |||
}; | |||
const int has_sse = nseel_has_sse3(); | |||
unsigned char *tmp = (unsigned char *) malloc(*size + (has_sse ? 0 : sizeof(prefix) + sizeof(postfix))); | |||
if (tmp) | |||
{ | |||
if (has_sse) | |||
{ | |||
memcpy(tmp,fn,*size); | |||
eel_fixup_sse3(tmp,tmp + *size,s_round_fixes[rmatch].istores, s_round_fixes[rmatch].flag); | |||
} | |||
else | |||
{ | |||
memcpy(tmp,prefix,sizeof(prefix)); | |||
memcpy(tmp+sizeof(prefix),fn,*size); | |||
memcpy(tmp+sizeof(prefix)+*size,postfix,sizeof(postfix)); | |||
*size += sizeof(prefix) + sizeof(postfix); | |||
} | |||
fn = tmp; | |||
s_round_fixes[rmatch].newsz = *size; | |||
s_round_fixes[rmatch].newfn = fn; | |||
} | |||
} | |||
return fn; | |||
} | |||
#endif |
@@ -0,0 +1,338 @@ | |||
#ifndef _NSEEL_GLUE_X86_64_H_ | |||
#define _NSEEL_GLUE_X86_64_H_ | |||
// non-SSE version | |||
// | |||
#define GLUE_MAX_FPSTACK_SIZE 8 | |||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (int) (offset)) | |||
#define GLUE_PREFER_NONFP_DV_ASSIGNS | |||
static const unsigned char GLUE_JMP_NC[] = { 0xE9, 0,0,0,0, }; // jmp<offset> | |||
static const unsigned char GLUE_JMP_IF_P1_Z[] = {0x85, 0xC0, 0x0F, 0x84, 0,0,0,0 }; // test eax, eax, jz | |||
static const unsigned char GLUE_JMP_IF_P1_NZ[] = {0x85, 0xC0, 0x0F, 0x85, 0,0,0,0 }; // test eax, eax, jnz | |||
#define GLUE_FUNC_ENTER_SIZE 0 | |||
#define GLUE_FUNC_LEAVE_SIZE 0 | |||
const static unsigned int GLUE_FUNC_ENTER[1]; | |||
const static unsigned int GLUE_FUNC_LEAVE[1]; | |||
// on x86-64: | |||
// stack is always 16 byte aligned | |||
// pushing values to the stack (for eel functions) has alignment pushed first, then value (value is at the lower address) | |||
// pushing pointers to the stack has the pointer pushed first, then the alignment (pointer is at the higher address) | |||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 10 | |||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wr) { | |||
const static unsigned short tab[3] = | |||
{ | |||
0xB848 /* mov rax, dv*/, | |||
0xBF48 /* mov rdi, dv */ , | |||
0xB948 /* mov rcx, dv */ | |||
}; | |||
unsigned short *bb = (unsigned short *)b; | |||
*bb++ = tab[wr]; // mov rax, directvalue | |||
*(INT_PTR *)bb = v; | |||
} | |||
const static unsigned char GLUE_PUSH_P1[2]={ 0x50,0x50}; // push rax (pointer); push rax (alignment) | |||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 8 | |||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) | |||
{ | |||
((unsigned char *)b)[0] = 0x48; // mov [rsp+offs], rax | |||
((unsigned char *)b)[1] = 0x89; | |||
((unsigned char *)b)[2] = 0x84; | |||
((unsigned char *)b)[3] = 0x24; | |||
*(int *)((unsigned char *)b+4) = offs; | |||
} | |||
#define GLUE_MOVE_PX_STACKPTR_SIZE 3 | |||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_MOVE_PX_STACKPTR_SIZE]= | |||
{ | |||
{ 0x48, 0x89, 0xe0 }, // mov rax, rsp | |||
{ 0x48, 0x89, 0xe7 }, // mov rdi, rsp | |||
{ 0x48, 0x89, 0xe1 }, // mov rcx, rsp | |||
}; | |||
memcpy(b,tab[wv],GLUE_MOVE_PX_STACKPTR_SIZE); | |||
} | |||
#define GLUE_MOVE_STACK_SIZE 7 | |||
static void GLUE_MOVE_STACK(void *b, int amt) | |||
{ | |||
((unsigned char *)b)[0] = 0x48; | |||
((unsigned char *)b)[1] = 0x81; | |||
if (amt < 0) | |||
{ | |||
((unsigned char *)b)[2] = 0xEC; | |||
*(int *)((char*)b+3) = -amt; // sub rsp, -amt32 | |||
} | |||
else | |||
{ | |||
((unsigned char *)b)[2] = 0xc4; | |||
*(int *)((char*)b+3) = amt; // add rsp, amt32 | |||
} | |||
} | |||
#define GLUE_POP_PX_SIZE 2 | |||
static void GLUE_POP_PX(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_POP_PX_SIZE]= | |||
{ | |||
{0x58,/*pop rax*/ 0x58}, // pop alignment, then pop pointer | |||
{0x5F,/*pop rdi*/ 0x5F}, | |||
{0x59,/*pop rcx*/ 0x59}, | |||
}; | |||
memcpy(b,tab[wv],GLUE_POP_PX_SIZE); | |||
} | |||
static const unsigned char GLUE_PUSH_P1PTR_AS_VALUE[] = | |||
{ | |||
0x50, /*push rax - for alignment */ | |||
0xff, 0x30, /* push qword [rax] */ | |||
}; | |||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) // trashes P2 (rdi) and P3 (rcx) | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue | |||
*buf++ = 0x8f; *buf++ = 0x01; // pop qword [rcx] | |||
*buf++ = 0x5F ; // pop rdi (alignment, safe to trash rdi though) | |||
} | |||
return 1+10+2; | |||
} | |||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) // trashes P2/P3 | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue | |||
*buf++ = 0x48; *buf++ = 0x8B; *buf++ = 0x38; // mov rdi, [rax] | |||
*buf++ = 0x48; *buf++ = 0x89; *buf++ = 0x39; // mov [rcx], rdi | |||
} | |||
return 3 + 10 + 3; | |||
} | |||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0x48; | |||
*buf++ = 0xB8; | |||
*(void **) buf = destptr; buf+=8; // mov rax, directvalue | |||
*buf++ = 0xDD; *buf++ = 0x18; // fstp qword [rax] | |||
} | |||
return 2+8+2; | |||
} | |||
#define GLUE_SET_PX_FROM_P1_SIZE 3 | |||
static void GLUE_SET_PX_FROM_P1(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_SET_PX_FROM_P1_SIZE]={ | |||
{0x90,0x90,0x90}, // should never be used! (nopnop) | |||
{0x48,0x89,0xC7}, // mov rdi, rax | |||
{0x48,0x89,0xC1}, // mov rcx, rax | |||
}; | |||
memcpy(b,tab[wv],GLUE_SET_PX_FROM_P1_SIZE); | |||
} | |||
#define GLUE_POP_FPSTACK_SIZE 2 | |||
static const unsigned char GLUE_POP_FPSTACK[2] = { 0xDD, 0xD8 }; // fstp st0 | |||
static const unsigned char GLUE_POP_FPSTACK_TOSTACK[] = { | |||
0x48, 0x81, 0xEC, 16, 0,0,0, // sub rsp, 16 | |||
0xDD, 0x1C, 0x24 // fstp qword (%rsp) | |||
}; | |||
static const unsigned char GLUE_POP_FPSTACK_TO_WTP[] = { | |||
0xDD, 0x1E, /* fstp qword [rsi] */ | |||
0x48, 0x81, 0xC6, 8, 0,0,0,/* add rsi, 8 */ | |||
}; | |||
#define GLUE_SET_PX_FROM_WTP_SIZE 3 | |||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_SET_PX_FROM_WTP_SIZE]={ | |||
{0x48, 0x89,0xF0}, // mov rax, rsi | |||
{0x48, 0x89,0xF7}, // mov rdi, rsi | |||
{0x48, 0x89,0xF1}, // mov rcx, rsi | |||
}; | |||
memcpy(b,tab[wv],GLUE_SET_PX_FROM_WTP_SIZE); | |||
} | |||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 2 | |||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE]={ | |||
{0xDD,0x00}, // fld qword [rax] | |||
{0xDD,0x07}, // fld qword [rdi] | |||
{0xDD,0x01}, // fld qword [rcx] | |||
}; | |||
memcpy(b,tab[wv],GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE); | |||
} | |||
static unsigned char GLUE_POP_STACK_TO_FPSTACK[] = { | |||
0xDD, 0x04, 0x24, // fld qword (%rsp) | |||
0x48, 0x81, 0xC4, 16, 0,0,0, // add rsp, 16 | |||
}; | |||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (GLUE_SET_PX_FROM_WTP_SIZE + sizeof(GLUE_POP_FPSTACK_TO_WTP)) | |||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) | |||
{ | |||
GLUE_SET_PX_FROM_WTP(buf,wv); | |||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); | |||
}; | |||
const static unsigned char GLUE_RET=0xC3; | |||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr) | |||
{ | |||
if (out) | |||
{ | |||
*out++ = 0x48; | |||
*out++ = 0xBE; // mov rsi, constant64 | |||
*(void **)out = ptr; | |||
out+=sizeof(void *); | |||
} | |||
return 2+sizeof(void *); | |||
} | |||
extern void eel_callcode64(INT_PTR code, INT_PTR ram_tab); | |||
extern void eel_callcode64_fast(INT_PTR code, INT_PTR ram_tab); | |||
#define GLUE_CALL_CODE(bp, cp, rt) do { \ | |||
if (h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) eel_callcode64_fast(cp, rt); \ | |||
else eel_callcode64(cp, rt);\ | |||
} while(0) | |||
#define GLUE_TABPTR_IGNORED | |||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) | |||
{ | |||
char *p=(char*)_p; | |||
INT_PTR scan = 0xFEFEFEFEFEFEFEFE; | |||
while (*(INT_PTR *)p != scan) p++; | |||
*(INT_PTR *)p = newv; | |||
return (unsigned char *) (((INT_PTR*)p)+1); | |||
} | |||
#define INT_TO_LECHARS(x) ((x)&0xff),(((x)>>8)&0xff), (((x)>>16)&0xff), (((x)>>24)&0xff) | |||
#define GLUE_INLINE_LOOPS | |||
static const unsigned char GLUE_LOOP_LOADCNT[]={ | |||
0xDD, 0x0E, //fistTp qword [rsi] | |||
0x48, 0x8B, 0x0E, // mov rcx, [rsi] | |||
0x48, 0x81, 0xf9, 1,0,0,0, // cmp rcx, 1 | |||
0x0F, 0x8C, 0,0,0,0, // JL <skipptr> | |||
}; | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
#define GLUE_LOOP_CLAMPCNT_SIZE sizeof(GLUE_LOOP_CLAMPCNT) | |||
static const unsigned char GLUE_LOOP_CLAMPCNT[]={ | |||
0x48, 0x81, 0xf9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // cmp rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
0x0F, 0x8C, 10,0,0,0, // JL over-the-mov | |||
0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
}; | |||
#else | |||
#define GLUE_LOOP_CLAMPCNT_SIZE 0 | |||
#define GLUE_LOOP_CLAMPCNT "" | |||
#endif | |||
#define GLUE_LOOP_BEGIN_SIZE sizeof(GLUE_LOOP_BEGIN) | |||
static const unsigned char GLUE_LOOP_BEGIN[]={ | |||
0x56, //push rsi | |||
0x51, // push rcx | |||
}; | |||
static const unsigned char GLUE_LOOP_END[]={ | |||
0x59, //pop rcx | |||
0x5E, // pop rsi | |||
0xff, 0xc9, // dec rcx | |||
0x0f, 0x85, 0,0,0,0, // jnz ... | |||
}; | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
static const unsigned char GLUE_WHILE_SETUP[]={ | |||
0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
}; | |||
#define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) | |||
static const unsigned char GLUE_WHILE_BEGIN[]={ | |||
0x56, //push rsi | |||
0x51, // push rcx | |||
}; | |||
static const unsigned char GLUE_WHILE_END[]={ | |||
0x59, //pop rcx | |||
0x5E, // pop rsi | |||
0xff, 0xc9, // dec rcx | |||
0x0f, 0x84, 0,0,0,0, // jz endpt | |||
}; | |||
#else | |||
#define GLUE_WHILE_SETUP "" | |||
#define GLUE_WHILE_SETUP_SIZE 0 | |||
#define GLUE_WHILE_END_NOJUMP | |||
static const unsigned char GLUE_WHILE_BEGIN[]={ | |||
0x56, //push rsi | |||
0x51, // push rcx | |||
}; | |||
static const unsigned char GLUE_WHILE_END[]={ | |||
0x59, //pop rcx | |||
0x5E, // pop rsi | |||
}; | |||
#endif | |||
static const unsigned char GLUE_WHILE_CHECK_RV[] = { | |||
0x85, 0xC0, // test eax, eax | |||
0x0F, 0x85, 0,0,0,0 // jnz looppt | |||
}; | |||
static const unsigned char GLUE_SET_P1_Z[] = { 0x48, 0x29, 0xC0 }; // sub rax, rax | |||
static const unsigned char GLUE_SET_P1_NZ[] = { 0xb0, 0x01 }; // mov al, 1 | |||
static const unsigned char GLUE_FXCH[] = {0xd9, 0xc9}; | |||
#define GLUE_HAS_FLDZ | |||
static const unsigned char GLUE_FLDZ[] = {0xd9, 0xee}; | |||
#define GLUE_HAS_FLD1 | |||
static const unsigned char GLUE_FLD1[] = {0xd9, 0xe8}; | |||
static EEL_F negativezeropointfive=-0.5f; | |||
static EEL_F onepointfive=1.5f; | |||
#define GLUE_INVSQRT_NEEDREPL &negativezeropointfive, &onepointfive, | |||
#define GLUE_HAS_NATIVE_TRIGSQRTLOG | |||
static void *GLUE_realAddress(void *fn, void *fn_e, int *size) | |||
{ | |||
static const unsigned char sig[12] = { 0x89, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }; | |||
unsigned char *p = (unsigned char *)fn; | |||
while (memcmp(p,sig,sizeof(sig))) p++; | |||
p+=sizeof(sig); | |||
fn = p; | |||
while (memcmp(p,sig,sizeof(sig))) p++; | |||
*size = (int) (p - (unsigned char *)fn); | |||
return fn; | |||
} | |||
// end of x86-64 | |||
#endif |
@@ -0,0 +1,638 @@ | |||
#ifndef _NSEEL_GLUE_X86_64_SSE_H_ | |||
#define _NSEEL_GLUE_X86_64_SSE_H_ | |||
// SSE version (needs the appropriate .o linked!) | |||
#define GLUE_PREFER_NONFP_DV_ASSIGNS | |||
#define GLUE_HAS_FPREG2 1 | |||
static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { 0xc8100ff2 }; // movsd %xmm0,%xmm1 | |||
static unsigned char GLUE_POP_STACK_TO_FPREG2[] = { | |||
0xf2, 0x0f, 0x10, 0x0c, 0x24, // movsd (%rsp), %xmm1 | |||
0x48, 0x81, 0xC4, 16, 0,0,0, // add rsp, 16 | |||
}; | |||
// spill registers | |||
#define GLUE_MAX_SPILL_REGS 4 | |||
#ifdef _WIN32 | |||
// win64: xmm6-xmm15 are nonvolatile, so we use xmm6-xmm9 as spill registers (xmm8/xmm9 are 5 byte encodings) | |||
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4 + ((x)>1)) | |||
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4 + ((x)>1)) | |||
static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) | |||
{ | |||
if (ws < 2) | |||
{ | |||
*(unsigned int *)b = 0xce100ff2 + (ws<<24); // movsd xmm1, xmm6+ws | |||
} | |||
else | |||
{ | |||
// movsd xmm1, xmm8 + (ws-2) | |||
*(unsigned int *)b = 0x100f41f2; | |||
((unsigned char *)b)[4] = 0xc8 + (ws-2); | |||
} | |||
} | |||
static void GLUE_SAVE_TO_SPILL(void *b, int ws) | |||
{ | |||
if (ws < 2) | |||
{ | |||
*(unsigned int *)b = 0xf0100ff2 + (ws<<27); // movsd xmm6+ws, xmm0 | |||
} | |||
else | |||
{ | |||
// movsd xmm8+(ws-2), xmm0 | |||
*(unsigned int *)b = 0x100f44f2; | |||
((unsigned char *)b)[4] = 0xc0 + ((ws-2)<<3); | |||
} | |||
} | |||
#else | |||
// non-win32: our function stubs preserve xmm4-xmm7 | |||
#ifdef _DEBUG | |||
#define GLUE_VALIDATE_SPILLS | |||
#endif | |||
#ifdef GLUE_VALIDATE_SPILLS | |||
static unsigned char save_validate[]={ | |||
0x48,0x83,0xec,0x10, // subq $16, %rsp | |||
0xf2,0x0f,0x11,0x04,0x24, // movsd %xmm0, (%rsp) | |||
0x66,0x48,0x0f,0x6e,0xe4, // movq %rsp, %xmm4 (+ws<<3) | |||
}; | |||
static unsigned char restore_validate[] = { | |||
0xf2, 0x0f, 0x10, 0xcc, // movsd %xmm7, %xmm1 (+ws) | |||
0x66, 0x48, 0x0f, 0x6e, 0xdc, // movq %rsp, %xmm3 | |||
0x66, 0x0f, 0x2e, 0xd9, // ucomisd %xmm1, %xmm3 | |||
0x74, 0x02, // je 2 <skip> | |||
0xcd, 0x03, // int $3 | |||
0xf2, 0x0f, 0x10, 0x0c, 0x24, // movsd (%rsp), %xmm1 | |||
0x48, 0x83, 0xc4, 0x10, // addq $16, %rsp | |||
}; | |||
#define GLUE_SAVE_TO_SPILL_SIZE(x) (sizeof(save_validate)) | |||
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (sizeof(restore_validate)) | |||
#else | |||
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4) | |||
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4) | |||
#endif | |||
static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws) | |||
{ | |||
#ifdef GLUE_VALIDATE_SPILLS | |||
char *p = (char*) b; | |||
memcpy(p,restore_validate,sizeof(restore_validate)); | |||
p[3] += ws; | |||
#else | |||
*(unsigned int *)b = 0xcc100ff2 + (ws<<24); // movsd xmm1, xmm4+ws | |||
#endif | |||
} | |||
static void GLUE_SAVE_TO_SPILL(void *b, int ws) | |||
{ | |||
#ifdef GLUE_VALIDATE_SPILLS | |||
char *p = (char*) b; | |||
memcpy(p,save_validate,sizeof(save_validate)); | |||
p[sizeof(save_validate)-1] += ws<<3; | |||
#else | |||
*(unsigned int *)b = 0xe0100ff2 + (ws<<27); // movsd xmm4+ws, xmm0 | |||
#endif | |||
} | |||
#endif | |||
#define GLUE_MAX_FPSTACK_SIZE 0 | |||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (int) (offset)) | |||
static const unsigned char GLUE_JMP_NC[] = { 0xE9, 0,0,0,0, }; // jmp<offset> | |||
static const unsigned char GLUE_JMP_IF_P1_Z[] = {0x85, 0xC0, 0x0F, 0x84, 0,0,0,0 }; // test eax, eax, jz | |||
static const unsigned char GLUE_JMP_IF_P1_NZ[] = {0x85, 0xC0, 0x0F, 0x85, 0,0,0,0 }; // test eax, eax, jnz | |||
#define GLUE_FUNC_ENTER_SIZE 0 | |||
#define GLUE_FUNC_LEAVE_SIZE 0 | |||
const static unsigned int GLUE_FUNC_ENTER[1]; | |||
const static unsigned int GLUE_FUNC_LEAVE[1]; | |||
// on x86-64: | |||
// stack is always 16 byte aligned | |||
// pushing values to the stack (for eel functions) has alignment pushed first, then value (value is at the lower address) | |||
// pushing pointers to the stack has the pointer pushed first, then the alignment (pointer is at the higher address) | |||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 10 | |||
#define GLUE_MOV_PX_DIRECTVALUE_TOSTACK_SIZE 14 // wr=-1, sets xmm0 | |||
#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 14 // wr=-2, sets xmm1 | |||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wr) { | |||
const static unsigned short tab[3] = | |||
{ | |||
0xB848 /* mov rax, dv*/, | |||
0xBF48 /* mov rdi, dv */ , | |||
0xB948 /* mov rcx, dv */ | |||
}; | |||
unsigned short *bb = (unsigned short *)b; | |||
*bb++ = tab[wdl_max(wr,0)]; // mov rax, directvalue | |||
*(INT_PTR *)bb = v; | |||
if (wr == -2) *(unsigned int *)(bb + 4) = 0x08100ff2; // movsd (%rax), %xmm1 | |||
else if (wr == -1) *(unsigned int *)(bb + 4) = 0x00100ff2; // movsd (%rax), %xmm0 | |||
} | |||
const static unsigned char GLUE_PUSH_P1[2]={ 0x50,0x50}; // push rax (pointer); push rax (alignment) | |||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 8 | |||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs) | |||
{ | |||
((unsigned char *)b)[0] = 0x48; // mov [rsp+offs], rax | |||
((unsigned char *)b)[1] = 0x89; | |||
((unsigned char *)b)[2] = 0x84; | |||
((unsigned char *)b)[3] = 0x24; | |||
*(int *)((unsigned char *)b+4) = offs; | |||
} | |||
#define GLUE_MOVE_PX_STACKPTR_SIZE 3 | |||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_MOVE_PX_STACKPTR_SIZE]= | |||
{ | |||
{ 0x48, 0x89, 0xe0 }, // mov rax, rsp | |||
{ 0x48, 0x89, 0xe7 }, // mov rdi, rsp | |||
{ 0x48, 0x89, 0xe1 }, // mov rcx, rsp | |||
}; | |||
memcpy(b,tab[wv],GLUE_MOVE_PX_STACKPTR_SIZE); | |||
} | |||
#define GLUE_MOVE_STACK_SIZE 7 | |||
static void GLUE_MOVE_STACK(void *b, int amt) | |||
{ | |||
((unsigned char *)b)[0] = 0x48; | |||
((unsigned char *)b)[1] = 0x81; | |||
if (amt < 0) | |||
{ | |||
((unsigned char *)b)[2] = 0xEC; | |||
*(int *)((char*)b+3) = -amt; // sub rsp, -amt32 | |||
} | |||
else | |||
{ | |||
((unsigned char *)b)[2] = 0xc4; | |||
*(int *)((char*)b+3) = amt; // add rsp, amt32 | |||
} | |||
} | |||
#define GLUE_POP_PX_SIZE 2 | |||
static void GLUE_POP_PX(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_POP_PX_SIZE]= | |||
{ | |||
{0x58,/*pop rax*/ 0x58}, // pop alignment, then pop pointer | |||
{0x5F,/*pop rdi*/ 0x5F}, | |||
{0x59,/*pop rcx*/ 0x59}, | |||
}; | |||
memcpy(b,tab[wv],GLUE_POP_PX_SIZE); | |||
} | |||
static const unsigned char GLUE_PUSH_P1PTR_AS_VALUE[] = | |||
{ | |||
0x50, /*push rax - for alignment */ | |||
0xff, 0x30, /* push qword [rax] */ | |||
}; | |||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr) // trashes P2 (rdi) and P3 (rcx) | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue | |||
*buf++ = 0x8f; *buf++ = 0x01; // pop qword [rcx] | |||
*buf++ = 0x5F ; // pop rdi (alignment, safe to trash rdi though) | |||
} | |||
return 1+10+2; | |||
} | |||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr) // trashes P2/P3 | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0x48; *buf++ = 0xB9; *(void **) buf = destptr; buf+=8; // mov rcx, directvalue | |||
*buf++ = 0x48; *buf++ = 0x8B; *buf++ = 0x38; // mov rdi, [rax] | |||
*buf++ = 0x48; *buf++ = 0x89; *buf++ = 0x39; // mov [rcx], rdi | |||
} | |||
return 3 + 10 + 3; | |||
} | |||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr) | |||
{ | |||
if (buf) | |||
{ | |||
*buf++ = 0x48; | |||
*buf++ = 0xB8; | |||
*(void **) buf = destptr; buf+=8; // mov rax, directvalue | |||
*buf++ = 0xf2; // movsd %xmm0, (%rax) | |||
*buf++ = 0x0f; | |||
*buf++ = 0x11; | |||
*buf++ = 0x00; | |||
} | |||
return 2+8+4; | |||
} | |||
#define GLUE_SET_PX_FROM_P1_SIZE 3 | |||
static void GLUE_SET_PX_FROM_P1(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_SET_PX_FROM_P1_SIZE]={ | |||
{0x90,0x90,0x90}, // should never be used! (nopnop) | |||
{0x48,0x89,0xC7}, // mov rdi, rax | |||
{0x48,0x89,0xC1}, // mov rcx, rax | |||
}; | |||
memcpy(b,tab[wv],GLUE_SET_PX_FROM_P1_SIZE); | |||
} | |||
#define GLUE_POP_FPSTACK_SIZE 0 | |||
static const unsigned char GLUE_POP_FPSTACK[1] = { 0 }; | |||
static const unsigned char GLUE_POP_FPSTACK_TOSTACK[] = { | |||
0x48, 0x81, 0xEC, 16, 0,0,0, // sub rsp, 16 | |||
0xf2, 0x0f, 0x11, 0x04, 0x24, // movsd xmm0, (%rsp) | |||
}; | |||
static const unsigned char GLUE_POP_FPSTACK_TO_WTP[] = { | |||
0xf2, 0x0f, 0x11, 0x06, // movsd xmm0, (%rsi) | |||
0x48, 0x81, 0xC6, 8, 0,0,0,/* add rsi, 8 */ | |||
}; | |||
#define GLUE_SET_PX_FROM_WTP_SIZE 3 | |||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_SET_PX_FROM_WTP_SIZE]={ | |||
{0x48, 0x89,0xF0}, // mov rax, rsi | |||
{0x48, 0x89,0xF7}, // mov rdi, rsi | |||
{0x48, 0x89,0xF1}, // mov rcx, rsi | |||
}; | |||
memcpy(b,tab[wv],GLUE_SET_PX_FROM_WTP_SIZE); | |||
} | |||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4 | |||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv) | |||
{ | |||
static const unsigned char tab[3][GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE]={ | |||
{0xf2, 0x0f, 0x10, 0x00}, // movsd (%rax), %xmm0 | |||
{0xf2, 0x0f, 0x10, 0x07}, // movsd (%rdi), %xmm0 | |||
{0xf2, 0x0f, 0x10, 0x01}, // movsd (%rcx), %xmm0 | |||
}; | |||
memcpy(b,tab[wv],GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE); | |||
} | |||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (GLUE_SET_PX_FROM_WTP_SIZE + sizeof(GLUE_POP_FPSTACK_TO_WTP)) | |||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv) | |||
{ | |||
GLUE_SET_PX_FROM_WTP(buf,wv); | |||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP)); | |||
}; | |||
const static unsigned char GLUE_RET=0xC3; | |||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr) | |||
{ | |||
if (out) | |||
{ | |||
*out++ = 0x48; | |||
*out++ = 0xBE; // mov rsi, constant64 | |||
*(void **)out = ptr; | |||
out+=sizeof(void *); | |||
} | |||
return 2+sizeof(void *); | |||
} | |||
extern void eel_callcode64(INT_PTR code, INT_PTR ram_tab); | |||
extern void eel_callcode64_fast(INT_PTR code, INT_PTR ram_tab); | |||
#define GLUE_CALL_CODE(bp, cp, rt) do { \ | |||
if (h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) eel_callcode64_fast(cp, rt); \ | |||
else eel_callcode64(cp, rt);\ | |||
} while(0) | |||
#define GLUE_TABPTR_IGNORED | |||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv) | |||
{ | |||
char *p=(char*)_p; | |||
INT_PTR scan = 0xFEFEFEFEFEFEFEFE; | |||
while (*(INT_PTR *)p != scan) p++; | |||
*(INT_PTR *)p = newv; | |||
return (unsigned char *) (((INT_PTR*)p)+1); | |||
} | |||
#define INT_TO_LECHARS(x) ((x)&0xff),(((x)>>8)&0xff), (((x)>>16)&0xff), (((x)>>24)&0xff) | |||
#define GLUE_INLINE_LOOPS | |||
static const unsigned char GLUE_LOOP_LOADCNT[]={ | |||
0xf2, 0x48, 0x0f, 0x2c, 0xc8, // cvttsd2si %xmm0, %rcx | |||
0x48, 0x81, 0xf9, 1,0,0,0, // cmp rcx, 1 | |||
0x0F, 0x8C, 0,0,0,0, // JL <skipptr> | |||
}; | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
#define GLUE_LOOP_CLAMPCNT_SIZE sizeof(GLUE_LOOP_CLAMPCNT) | |||
static const unsigned char GLUE_LOOP_CLAMPCNT[]={ | |||
0x48, 0x81, 0xf9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), // cmp rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
0x0F, 0x8C, 10,0,0,0, // JL over-the-mov | |||
0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
}; | |||
#else | |||
#define GLUE_LOOP_CLAMPCNT_SIZE 0 | |||
#define GLUE_LOOP_CLAMPCNT "" | |||
#endif | |||
#define GLUE_LOOP_BEGIN_SIZE sizeof(GLUE_LOOP_BEGIN) | |||
static const unsigned char GLUE_LOOP_BEGIN[]={ | |||
0x56, //push rsi | |||
0x51, // push rcx | |||
}; | |||
static const unsigned char GLUE_LOOP_END[]={ | |||
0x59, //pop rcx | |||
0x5E, // pop rsi | |||
0xff, 0xc9, // dec rcx | |||
0x0f, 0x85, 0,0,0,0, // jnz ... | |||
}; | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN > 0 | |||
static const unsigned char GLUE_WHILE_SETUP[]={ | |||
0x48, 0xB9, INT_TO_LECHARS(NSEEL_LOOPFUNC_SUPPORT_MAXLEN), 0,0,0,0, // mov rcx, NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
}; | |||
#define GLUE_WHILE_SETUP_SIZE sizeof(GLUE_WHILE_SETUP) | |||
static const unsigned char GLUE_WHILE_BEGIN[]={ | |||
0x56, //push rsi | |||
0x51, // push rcx | |||
}; | |||
static const unsigned char GLUE_WHILE_END[]={ | |||
0x59, //pop rcx | |||
0x5E, // pop rsi | |||
0xff, 0xc9, // dec rcx | |||
0x0f, 0x84, 0,0,0,0, // jz endpt | |||
}; | |||
#else | |||
#define GLUE_WHILE_SETUP "" | |||
#define GLUE_WHILE_SETUP_SIZE 0 | |||
#define GLUE_WHILE_END_NOJUMP | |||
static const unsigned char GLUE_WHILE_BEGIN[]={ | |||
0x56, //push rsi | |||
0x51, // push rcx | |||
}; | |||
static const unsigned char GLUE_WHILE_END[]={ | |||
0x59, //pop rcx | |||
0x5E, // pop rsi | |||
}; | |||
#endif | |||
static const unsigned char GLUE_WHILE_CHECK_RV[] = { | |||
0x85, 0xC0, // test eax, eax | |||
0x0F, 0x85, 0,0,0,0 // jnz looppt | |||
}; | |||
static const unsigned char GLUE_SET_P1_Z[] = { 0x48, 0x29, 0xC0 }; // sub rax, rax | |||
static const unsigned char GLUE_SET_P1_NZ[] = { 0xb0, 0x01 }; // mov al, 1 | |||
#define GLUE_HAS_FLDZ | |||
static const unsigned char GLUE_FLDZ[] = { | |||
0x0f, 0x57, 0xc0 //xorps %xmm0, %xmm0 | |||
}; | |||
static EEL_F negativezeropointfive=-0.5f; | |||
static EEL_F onepointfive=1.5f; | |||
#define GLUE_INVSQRT_NEEDREPL &negativezeropointfive, &onepointfive, | |||
static void *GLUE_realAddress(void *fn, int *size) | |||
{ | |||
static const unsigned char new_sig[8] = { 0x89, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00 }; | |||
int sz = 0; | |||
while (memcmp((char*)fn + sz,new_sig,sizeof(new_sig))) sz++; | |||
*size = sz; | |||
return fn; | |||
} | |||
#define GLUE_HAS_FUSE 1 | |||
static int GLUE_FUSE(compileContext *ctx, unsigned char *code, int left_size, int right_size, int fuse_flags, int spill_reg) | |||
{ | |||
const UINT_PTR base = (UINT_PTR) ctx->ram_state->blocks; | |||
const int is_sse_op = right_size == 4 && // add/mul/sub/min/max | |||
code[0] == 0xf2 && | |||
code[1] == 0x0f && | |||
code[3] == 0xc1 && // low nibble is xmm1 | |||
(code[2] == 0x58 || code[2] == 0x59 || code[2] == 0x5c || code[2]==0x5d || code[2] == 0x5f); | |||
if (spill_reg >= 0) | |||
{ | |||
#ifndef GLUE_VALIDATE_SPILLS | |||
if (is_sse_op) | |||
{ | |||
char tmp[32]; | |||
const int sz = GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(spill_reg); | |||
GLUE_RESTORE_SPILL_TO_FPREG2(tmp,spill_reg); | |||
if (left_size>=sz && !memcmp(code-sz,tmp,sz)) | |||
{ | |||
code[-2] = code[2]; // modify the movsd into an addsd | |||
code[-1] -= 8; // movsd uses 0xc8+(xmmX&7), addsd etc use 0xc0 | |||
return -4; | |||
} | |||
} | |||
#endif | |||
} | |||
else | |||
{ | |||
if (left_size==28) | |||
{ | |||
// if const64_1 is within a 32-bit offset of ctx->ram_blocks->blocks, we can use [r12+offs] | |||
if (code[-28] == 0x48 && code[-27] == 0xb8 && // mov rax, const64_1 | |||
*(int *)(code - 18) == 0x08100ff2 && // movsd xmm1, [rax] | |||
code[-14] == 0x48 && code[-13] == 0xb8 && // mov rax, const64_2 | |||
*(int *)(code - 4) == 0x00100ff2 // movsd xmm0, [rax] | |||
) | |||
{ | |||
UINT_PTR c1, c2; | |||
INT_PTR c2offs,c1offs; | |||
unsigned char opc[3]; | |||
int wrpos = -28; | |||
if (is_sse_op) memcpy(opc,code,3); | |||
memcpy(&c1,code-26,8); | |||
memcpy(&c2,code-12,8); | |||
#define PTR_32_OK(x) ((x) == (INT_PTR)(int)(x)) | |||
c2offs = c2-base; | |||
if (!PTR_32_OK(c2offs)) | |||
{ | |||
code[wrpos++] = 0x48; | |||
code[wrpos++] = 0xb8; | |||
memcpy(code+wrpos,&c2,8); // mov rax, const64_2 | |||
wrpos += 8; | |||
} | |||
c1offs = c1-base; | |||
if (!PTR_32_OK(c1offs)) | |||
{ | |||
code[wrpos++] = 0x48; | |||
code[wrpos++] = 0xbf; | |||
memcpy(code+wrpos,&c1,8); // mov rdi, const64_1 | |||
wrpos += 8; | |||
} | |||
if (!PTR_32_OK(c2offs)) | |||
{ | |||
*(int *)(code+wrpos) = 0x00100ff2; // movsd xmm0, [rax] | |||
wrpos += 4; | |||
} | |||
else | |||
{ | |||
// movsd xmm0, [r12+offs] | |||
code[wrpos++] = 0xf2; | |||
code[wrpos++] = 0x41; | |||
code[wrpos++] = 0x0f; | |||
code[wrpos++] = 0x10; | |||
code[wrpos++] = 0x84; | |||
code[wrpos++] = 0x24; | |||
*(int *)(code+wrpos) = (int)c2offs; | |||
wrpos += 4; | |||
} | |||
if (!is_sse_op) | |||
{ | |||
// load xmm1 from rdi/c1offs | |||
if (!PTR_32_OK(c1offs)) | |||
{ | |||
*(int *)(code+wrpos) = 0x0f100ff2; // movsd xmm1, [rdi] | |||
wrpos += 4; | |||
} | |||
else | |||
{ | |||
// movsd xmm1, [r12+offs] | |||
code[wrpos++] = 0xf2; | |||
code[wrpos++] = 0x41; | |||
code[wrpos++] = 0x0f; | |||
code[wrpos++] = 0x10; | |||
code[wrpos++] = 0x8c; | |||
code[wrpos++] = 0x24; | |||
*(int *)(code+wrpos) = (int)c1offs; | |||
wrpos += 4; | |||
} | |||
if (wrpos<0) memmove(code+wrpos,code,right_size); | |||
return wrpos; | |||
} | |||
// fuse to sse op | |||
if (!PTR_32_OK(c1offs)) | |||
{ | |||
memcpy(code+wrpos,opc,3); | |||
code[wrpos+3] = 0x07; // [rdi] | |||
wrpos += 4; | |||
} | |||
else | |||
{ | |||
// mul/add/sub/min/max/sd xmm0, [r12+offs] | |||
code[wrpos++] = opc[0]; // 0xf2 | |||
code[wrpos++] = 0x41; | |||
code[wrpos++] = opc[1]; // 0x0f | |||
code[wrpos++] = opc[2]; // 0x58 etc | |||
code[wrpos++] = 0x84; | |||
code[wrpos++] = 0x24; | |||
*(int *)(code+wrpos) = (int)c1offs; | |||
wrpos += 4; | |||
} | |||
return wrpos - right_size; | |||
} | |||
} | |||
if ((fuse_flags&1) && left_size >= 14) | |||
{ | |||
if (code[-14] == 0x48 && code[-13] == 0xb8 && // mov rax, const64_2 | |||
*(int *)(code - 4) == 0x00100ff2) // movsd xmm0, [rax] | |||
{ | |||
INT_PTR c1; | |||
memcpy(&c1,code-12,8); | |||
c1 -= base; | |||
if (PTR_32_OK(c1)) | |||
{ | |||
// movsd xmm0, [r12+offs] | |||
int wrpos = -14; | |||
code[wrpos++] = 0xf2; | |||
code[wrpos++] = 0x41; | |||
code[wrpos++] = 0x0f; | |||
code[wrpos++] = 0x10; | |||
code[wrpos++] = 0x84; | |||
code[wrpos++] = 0x24; | |||
*(int *)(code+wrpos) = (int)c1; | |||
wrpos += 4; | |||
if (wrpos<0) memmove(code+wrpos,code,right_size); | |||
return wrpos; | |||
} | |||
} | |||
} | |||
if (left_size == 20 && right_size == 9 && | |||
code[-20]==0x48 && code[-19] == 0xbf && // mov rdi, const64_1 | |||
code[-10]==0x48 && code[-9] == 0xb8 // mov rax, const64_2 | |||
) | |||
{ | |||
static unsigned char assign_copy[9] = { 0x48, 0x8b, 0x10, // mov rdx, [rax] | |||
0x48, 0x89, 0x17, // mov [rdi], rdx | |||
0x48, 0x89, 0xf8, // mov rax, rdi | |||
}; | |||
if (!memcmp(code,assign_copy,9)) | |||
{ | |||
int wrpos = -20; | |||
INT_PTR c1,c2; // c1 is dest, c2 is src | |||
memcpy(&c1,code-18,8); | |||
memcpy(&c2,code-8,8); | |||
if (!PTR_32_OK(c2-base)) | |||
{ | |||
code[wrpos++] = 0x48; // mov rdi, src | |||
code[wrpos++] = 0xbf; | |||
memcpy(code+wrpos,&c2,8); | |||
wrpos +=8; | |||
} | |||
code[wrpos++] = 0x48; // mov rax, dest | |||
code[wrpos++] = 0xb8; | |||
memcpy(code+wrpos,&c1,8); | |||
wrpos +=8; | |||
if (PTR_32_OK(c2-base)) | |||
{ | |||
// mov rdx, [r12+offs] | |||
code[wrpos++] = 0x49; | |||
code[wrpos++] = 0x8b; | |||
code[wrpos++] = 0x94; | |||
code[wrpos++] = 0x24; | |||
*(int *)(code+wrpos) = (int)(c2-base); | |||
wrpos += 4; | |||
} | |||
else | |||
{ | |||
code[wrpos++] = 0x48; // mov rdx, [rdi] | |||
code[wrpos++] = 0x8b; | |||
code[wrpos++] = 0x17; | |||
} | |||
code[wrpos++] = 0x48; // mov [rax], rdx | |||
code[wrpos++] = 0x89; | |||
code[wrpos++] = 0x10; | |||
return wrpos - right_size; | |||
} | |||
} | |||
} | |||
return 0; | |||
} | |||
#endif |
@@ -0,0 +1,63 @@ | |||
/* | |||
Nullsoft Expression Evaluator Library (NS-EEL) | |||
Copyright (C) 1999-2003 Nullsoft, Inc. | |||
ns-eel-addfuncs.h: defines macros useful for adding functions to the compiler | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#ifndef __NS_EEL_ADDFUNCS_H__ | |||
#define __NS_EEL_ADDFUNCS_H__ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
struct _compileContext; | |||
void *NSEEL_PProc_RAM(void *data, int data_size, struct _compileContext *ctx); | |||
void *NSEEL_PProc_THIS(void *data, int data_size, struct _compileContext *ctx); | |||
#ifdef EEL_TARGET_PORTABLE | |||
extern EEL_BC_TYPE _asm_generic3parm[]; // 3 double * parms, returning double * | |||
extern EEL_BC_TYPE _asm_generic3parm_retd[]; // 3 double * parms, returning double | |||
extern EEL_BC_TYPE _asm_generic2parm[]; // 2 double * parms, returning double * | |||
extern EEL_BC_TYPE _asm_generic2parm_retd[]; // 2 double * parms, returning double | |||
extern EEL_BC_TYPE _asm_generic2xparm_retd[]; // 2 double * parms, returning double | |||
extern EEL_BC_TYPE _asm_generic1parm[]; // 1 double * parms, returning double * | |||
extern EEL_BC_TYPE _asm_generic1parm_retd[]; // 1 double * parms, returning double | |||
#else | |||
void _asm_generic3parm(void); // 3 double * parms, returning double * | |||
void _asm_generic3parm_retd(void); // 3 double * parms, returning double | |||
void _asm_generic2parm(void); // 2 double * parms, returning double * | |||
void _asm_generic2parm_retd(void); // 2 double * parms, returning double | |||
void _asm_generic2xparm_retd(void); // 2 double * parms, returning double | |||
void _asm_generic1parm(void); // 1 double * parms, returning double * | |||
void _asm_generic1parm_retd(void); // 1 double * parms, returning double | |||
#endif | |||
#ifdef __cplusplus | |||
}; | |||
#endif | |||
#endif//__NS_EEL_ADDFUNCS_H__ |
@@ -0,0 +1,62 @@ | |||
#ifndef _NSEEL_FUNC_REF_H_ | |||
#define _NSEEL_FUNC_REF_H_ | |||
#include "ns-eel.h" | |||
#define TMP_MKSTR2(x) #x | |||
#define TMP_MKSTR(x) TMP_MKSTR2(x) | |||
const char *nseel_builtin_function_reference= | |||
"while\texpression\tExecutes expression until expression evaluates to zero" | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
", or until " TMP_MKSTR(NSEEL_LOOPFUNC_SUPPORT_MAXLEN) "iterations occur" | |||
#endif | |||
". An alternate and more useful syntax is while (expression) ( statements ), which evaluates statements after " | |||
"every non-zero evaluation of expression.\0" | |||
"loop\tcount,expression\tEvaluates count once, and then executes expression count" | |||
#if NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
", but not more than " TMP_MKSTR(NSEEL_LOOPFUNC_SUPPORT_MAXLEN) "," | |||
#endif | |||
" times.\0" | |||
"sin\tangle\tReturns the sine of the angle specified (specified in radians -- to convert from degrees to radians, multiply by $pi/180, or 0.017453).\0" | |||
"cos\tangle\tReturns the cosine of the angle specified (specified in radians).\0" | |||
"tan\tangle\tReturns the tangent of the angle specified (specified in radians).\0" | |||
"sqrt\tvalue\tReturns the square root of the parameter. If the parameter is negative, the return value is undefined.\0" | |||
"log\tvalue\tReturns the natural logarithm (base e) of the parameter. If the value is not greater than 0, the return value is undefined.\0" | |||
"log10\tvalue\tReturns the base-10 logarithm of the parameter. If the value is not greater than 0, the return value is undefined.\0" | |||
"asin\tvalue\tReturns the arc sine of the value specified (return value is in radians). If the parameter is not between -1.0 and 1.0 inclusive, the return value is undefined.\0" | |||
"acos\tvalue\tReturns the arc cosine of the value specified (return value is in radians). If the parameter is not between -1.0 and 1.0 inclusive, the return value is undefined.\0" | |||
"atan\tvalue\tReturns the arc tangent of the value specified (return value is in radians). If the parameter is not between -1.0 and 1.0 inclusive, the return value is undefined.\0" | |||
"atan2\tnumerator,denominator\tReturns the arc tangent of the numerator divided by the denominator, allowing the denominator to be 0, and using their signs to produce a more meaningful result.\0" | |||
"exp\texponent\tReturns the number e ($e, approximately 2.718) raised to the parameter-th power. This function is significantly faster than pow() or the ^ operator.\0" | |||
"abs\tvalue\tReturns the absolute value of the parameter.\0" | |||
"sqr\tvalue\tReturns the square of the parameter (similar to value*value, but only evaluating value once).\0" | |||
"min\t&value,&value\tReturns (by reference) the minimum value of the two parameters. Since min() returns by reference, expressions such as min(x,y) = 5 are possible.\0" | |||
"max\t&value,&value\tReturns (by reference) the maximum value of the two parameters. Since max() returns by reference, expressions such as max(x,y) = 5 are possible.\0" | |||
"sign\tvalue\tReturns 1.0 if the parameter is greater than 0, -1.0 if the parameter is less than 0, or 0 if the parameter is 0.\0" | |||
"floor\tvalue\tReturns the value rounded to the next lowest integer (floor(3.9)==3, floor(-3.1)==-4).\0" | |||
"ceil\tvalue\tReturns the value rounded to the next highest integer (ceil(3.1)==4, ceil(-3.9)==-3).\0" | |||
"invsqrt\tvalue\tReturns a fast inverse square root (1/sqrt(x)) approximation of the parameter.\0" | |||
"freembuf\taddress\tHints the runtime that memory above the address specified may no longer be used. The runtime may, at its leisure, choose to lose the contents of memory above the address specified.\0" | |||
"memcpy\tdest,src,length\tCopies length items of memory from src to dest. Regions are permitted to overlap.\0" | |||
"memset\toffset,value,length\tSets length items of memory at offset to value.\0" | |||
"mem_get_values\toffset, ...\tReads values from memory starting at offset into variables specified. Slower than regular memory reads for less than a few variables, faster for more than a few. Undefined behavior if used with more than 32767 variables.\0" | |||
"mem_set_values\toffset, ...\tWrites values to memory starting at offset from variables specified. Slower than regular memory writes for less than a few variables, faster for more than a few. Undefined behavior if used with more than 32767 variables.\0" | |||
"stack_push\t&value\tPushes value onto the user stack, returns a reference to the parameter.\0" | |||
"stack_pop\t&value\tPops a value from the user stack into value, or into a temporary buffer if value is not specified, and returns a reference to where the stack was popped. Note that no checking is done to determine if the stack is empty, and as such stack_pop() will never fail.\0" | |||
"stack_peek\tindex\tReturns a reference to the item on the top of the stack (if index is 0), or to the Nth item on the stack if index is greater than 0. \0" | |||
"stack_exch\t&value\tExchanges a value with the top of the stack, and returns a reference to the parameter (with the new value).\0" | |||
#ifdef NSEEL_EEL1_COMPAT_MODE | |||
"rand\tmax\tReturns a psuedorandom non-negative integer number less than the parameter.\0" | |||
"sigmoid\tvalue,constraint\tReturns 1.0/(1+exp(-x * (constraint))), or 0 if a divide by 0 would occur.\0" | |||
"band\tx,y\tReturns 1 if both x and y evaluate to nonzero, 0 if otherwise. Both parameters are always evaluated.\0" | |||
"bor\tx,y\tReturns 1 if either x or y evaluate to nonzero, 0 if otherwise. Both parameters are always evaluated.\0" | |||
"exec2\tx,y\tEvaluates x, then evaluates and returns y.\0" | |||
"exec3\tx,y,z\tEvaluates x, evaluates y, then evaluates and returns z.\0" | |||
#else | |||
"rand\t[max]\tReturns a psuedorandom real number between 0 and the parameter, inclusive. If the parameter is omitted or less than 1.0, 1.0 is used as a maximum instead.\0" | |||
#endif | |||
; | |||
#undef TMP_MKSTR | |||
#endif |
@@ -0,0 +1,329 @@ | |||
/* | |||
Nullsoft Expression Evaluator Library (NS-EEL) | |||
Copyright (C) 1999-2003 Nullsoft, Inc. | |||
ns-eel-int.h: internal code definition header. | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#ifndef __NS_EELINT_H__ | |||
#define __NS_EELINT_H__ | |||
#ifdef _WIN32 | |||
#include <windows.h> | |||
#else | |||
#include "../wdltypes.h" | |||
#endif | |||
#include "ns-eel.h" | |||
#include "ns-eel-addfuncs.h" | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
enum { | |||
// these ignore fn in opcodes, just use fntype to determine function | |||
FN_MULTIPLY=0, | |||
FN_DIVIDE, | |||
FN_JOIN_STATEMENTS, | |||
FN_DENORMAL_LIKELY, | |||
FN_DENORMAL_UNLIKELY, | |||
FN_ADD, | |||
FN_SUB, | |||
FN_AND, | |||
FN_OR, | |||
FN_UMINUS, | |||
FN_NOT, | |||
FN_NOTNOT, | |||
FN_XOR, | |||
FN_SHL, | |||
FN_SHR, | |||
FN_MOD, | |||
FN_POW, | |||
FN_LT, | |||
FN_GT, | |||
FN_LTE, | |||
FN_GTE, | |||
FN_EQ, | |||
FN_EQ_EXACT, | |||
FN_NE, | |||
FN_NE_EXACT, | |||
FN_LOGICAL_AND, | |||
FN_LOGICAL_OR, | |||
FN_IF_ELSE, | |||
FN_MEMORY, | |||
FN_GMEMORY, | |||
FN_NONCONST_BEGIN, | |||
FN_ASSIGN=FN_NONCONST_BEGIN, | |||
FN_ADD_OP, | |||
FN_SUB_OP, | |||
FN_MOD_OP, | |||
FN_OR_OP, | |||
FN_AND_OP, | |||
FN_XOR_OP, | |||
FN_DIV_OP, | |||
FN_MUL_OP, | |||
FN_POW_OP, | |||
FN_WHILE, | |||
FN_LOOP, | |||
FUNCTYPE_SIMPLEMAX, | |||
FUNCTYPE_FUNCTIONTYPEREC=1000, // fn is a functionType * | |||
FUNCTYPE_EELFUNC, // fn is a _codeHandleFunctionRec * | |||
}; | |||
#define YYSTYPE opcodeRec * | |||
#define NSEEL_CLOSEFACTOR 0.00001 | |||
typedef struct opcodeRec opcodeRec; | |||
typedef struct _codeHandleFunctionRec | |||
{ | |||
struct _codeHandleFunctionRec *next; // main linked list (only used for high level functions) | |||
struct _codeHandleFunctionRec *derivedCopies; // separate linked list, head being the main function, other copies being derived versions | |||
void *startptr; // compiled code (may be cleared + recompiled when shared) | |||
opcodeRec *opcodes; | |||
int startptr_size; // 0=no code. -1 = needs calculation. >0 = size. | |||
int startptr_base_size; // initially calculated size of root function | |||
int tmpspace_req; | |||
int num_params; | |||
int rvMode; // RETURNVALUE_* | |||
int fpStackUsage; // 0-8, usually | |||
int canHaveDenormalOutput; | |||
// local storage's first items are the parameters, then locals. Note that the opcodes will reference localstorage[] via VARPTRPTR, but | |||
// the values localstorage[x] points are reallocated from context-to-context, if it is a common function. | |||
// separately allocated list of pointers, the contents of the list should be zeroed on context changes if a common function | |||
// note that when making variations on a function (context), it is shared, but since it is zeroed on context changes, it is context-local | |||
int localstorage_size; | |||
EEL_F **localstorage; | |||
int isCommonFunction; | |||
int usesNamespaces; | |||
unsigned int parameterAsNamespaceMask; | |||
char fname[NSEEL_MAX_FUNCSIG_NAME+1]; | |||
} _codeHandleFunctionRec; | |||
typedef struct _llBlock { | |||
struct _llBlock *next; | |||
int sizeused; | |||
int sizealloc; | |||
// data follows | |||
} llBlock; | |||
typedef struct { | |||
llBlock *blocks_code, *blocks_data; | |||
void *workTable; // references a chunk in blocks_data | |||
void *code; | |||
int code_size; // in case the caller wants to write it out | |||
int code_stats[4]; | |||
int want_stack; | |||
void *stack; // references a chunk in blocks_data, somewhere within the complete NSEEL_STACK_SIZE aligned at NSEEL_STACK_SIZE | |||
void *ramPtr; | |||
int workTable_size; // size (minus padding/extra space) of workTable -- only used if EEL_VALIDATE_WORKTABLE_USE set, but might be handy to have around too | |||
int compile_flags; | |||
} codeHandleType; | |||
typedef struct | |||
{ | |||
EEL_F *value; | |||
int refcnt; | |||
char isreg; | |||
char str[1]; | |||
} varNameRec; | |||
typedef struct | |||
{ | |||
void *ptr; | |||
int size, alloc; | |||
} eel_growbuf; | |||
#define EEL_GROWBUF(type) union { eel_growbuf _growbuf; type *_tval; } | |||
#define EEL_GROWBUF_RESIZE(gb, newsz) __growbuf_resize(&(gb)->_growbuf, (newsz)*(int)sizeof((gb)->_tval[0])) // <0 to free, does not realloc down otherwise | |||
#define EEL_GROWBUF_GET(gb) ((gb)->_tval) | |||
#define EEL_GROWBUF_GET_SIZE(gb) ((gb)->_growbuf.size/(int)sizeof((gb)->_tval[0])) | |||
typedef struct _compileContext | |||
{ | |||
eel_function_table *registered_func_tab; | |||
const char *(*func_check)(const char *fn_name, void *user); // return error message if not permitted | |||
void *func_check_user; | |||
EEL_GROWBUF(varNameRec *) varNameList; | |||
EEL_F *varValueStore; | |||
int varValueStore_left; | |||
int errVar,gotEndOfInput; | |||
opcodeRec *result; | |||
char last_error_string[256]; | |||
void *scanner; | |||
const char *rdbuf_start, *rdbuf, *rdbuf_end; | |||
llBlock *tmpblocks, // used while compiling, and freed after compiling | |||
*blocks_head_code, // used while compiling, transferred to code context (whole pages marked as executable) | |||
*blocks_head_data, // used while compiling, transferred to code context | |||
*ctx_pblocks; // persistent blocks, stores data used by varTable_Names, varTable_Values, etc. | |||
int l_stats[4]; // source bytes, static code bytes, call code bytes, data bytes | |||
int has_used_global_vars; | |||
_codeHandleFunctionRec *functions_local, *functions_common; | |||
// state used while generating functions | |||
int optimizeDisableFlags; | |||
int current_compile_flags; | |||
struct opcodeRec *directValueCache; // linked list using fn as next | |||
int isSharedFunctions; | |||
int isGeneratingCommonFunction; | |||
int function_usesNamespaces; | |||
int function_globalFlag; // set if restrict globals to function_localTable_Names[2] | |||
// [0] is parameter+local symbols (combined space) | |||
// [1] is symbols which get implied "this." if used | |||
// [2] is globals permitted | |||
int function_localTable_Size[3]; // for parameters only | |||
char **function_localTable_Names[3]; // lists of pointers | |||
EEL_F **function_localTable_ValuePtrs; | |||
const char *function_curName; // name of current function | |||
EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list); | |||
EEL_F (*onNamedString)(void *caller_this, const char *name); | |||
EEL_F *(*getVariable)(void *userctx, const char *name); | |||
void *getVariable_userctx; | |||
codeHandleType *tmpCodeHandle; | |||
struct | |||
{ | |||
WDL_UINT64 sign_mask[2]; | |||
WDL_UINT64 abs_mask[2]; | |||
int needfree; | |||
int maxblocks; | |||
double closefact; | |||
EEL_F *blocks[NSEEL_RAM_BLOCKS]; | |||
} *ram_state; // allocated from blocks with 16 byte alignment | |||
void *gram_blocks; | |||
void *caller_this; | |||
} | |||
compileContext; | |||
#define NSEEL_NPARAMS_FLAG_CONST 0x80000 | |||
typedef struct functionType { | |||
const char *name; | |||
void *afunc; | |||
int nParams; | |||
void *replptrs[4]; | |||
NSEEL_PPPROC pProc; | |||
} functionType; | |||
functionType *nseel_getFunctionByName(compileContext *ctx, const char *name, int *mchk); // sets mchk (if non-NULL) to how far allowed to scan forward for duplicate names | |||
functionType *nseel_enumFunctions(compileContext *ctx, int idx); | |||
opcodeRec *nseel_createCompiledValue(compileContext *ctx, EEL_F value); | |||
opcodeRec *nseel_createCompiledValuePtr(compileContext *ctx, EEL_F *addrValue, const char *namestr); | |||
opcodeRec *nseel_createMoreParametersOpcode(compileContext *ctx, opcodeRec *code1, opcodeRec *code2); | |||
opcodeRec *nseel_createSimpleCompiledFunction(compileContext *ctx, int fn, int np, opcodeRec *code1, opcodeRec *code2); | |||
opcodeRec *nseel_createMemoryAccess(compileContext *ctx, opcodeRec *code1, opcodeRec *code2); | |||
opcodeRec *nseel_createIfElse(compileContext *ctx, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3); | |||
opcodeRec *nseel_createFunctionByName(compileContext *ctx, const char *name, int np, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3); | |||
// converts a generic identifier (VARPTR) opcode into either an actual variable reference (parmcnt = -1), | |||
// or if parmcnt >= 0, to a function call (see nseel_setCompiledFunctionCallParameters()) | |||
opcodeRec *nseel_resolve_named_symbol(compileContext *ctx, opcodeRec *rec, int parmcnt, int *errOut); | |||
// sets parameters and calculates parameter count for opcode, and calls nseel_resolve_named_symbol() with the right | |||
// parameter count | |||
opcodeRec *nseel_setCompiledFunctionCallParameters(compileContext *ctx, opcodeRec *fn, opcodeRec *code1, opcodeRec *code2, opcodeRec *code3, opcodeRec *postCode, int *errOut); | |||
// errOut will be set if return NULL: | |||
// -1 if postCode set when not wanted (i.e. not while()) | |||
// 0 if func not found, | |||
// 1 if function requires 2+ parameters but was given more | |||
// 2 if function needs more parameters | |||
// 4 if function requires 1 parameter but was given more | |||
struct eelStringSegmentRec *nseel_createStringSegmentRec(compileContext *ctx, const char *str, int len); | |||
opcodeRec *nseel_eelMakeOpcodeFromStringSegments(compileContext *ctx, struct eelStringSegmentRec *rec); | |||
EEL_F *nseel_int_register_var(compileContext *ctx, const char *name, int isReg, const char **namePtrOut); | |||
_codeHandleFunctionRec *eel_createFunctionNamespacedInstance(compileContext *ctx, _codeHandleFunctionRec *fr, const char *nameptr); | |||
typedef struct nseel_globalVarItem | |||
{ | |||
EEL_F data; | |||
struct nseel_globalVarItem *_next; | |||
char name[1]; // varlen, does not include _global. prefix | |||
} nseel_globalVarItem; | |||
extern nseel_globalVarItem *nseel_globalreg_list; // if NSEEL_EEL1_COMPAT_MODE, must use NSEEL_getglobalregs() for regxx values | |||
#include "y.tab.h" | |||
// nseel_simple_tokenizer will return comments as tokens if state is non-NULL | |||
const char *nseel_simple_tokenizer(const char **ptr, const char *endptr, int *lenOut, int *state); | |||
int nseel_filter_escaped_string(char *outbuf, int outbuf_sz, const char *rdptr, size_t rdptr_size, char delim_char); // returns length used, minus NUL char | |||
opcodeRec *nseel_translate(compileContext *ctx, const char *tmp, size_t tmplen); // tmplen=0 for nul-term | |||
int nseel_lookup(compileContext *ctx, opcodeRec **opOut, const char *sname); | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAlloc(EEL_F **blocks, unsigned int w); | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAllocGMEM(EEL_F ***blocks, unsigned int w); | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemSet(EEL_F **blocks,EEL_F *dest, EEL_F *v, EEL_F *lenptr); | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemFree(void *blocks, EEL_F *which); | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemTop(void *blocks, EEL_F *which); | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemCpy(EEL_F **blocks,EEL_F *dest, EEL_F *src, EEL_F *lenptr); | |||
EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_SetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms); | |||
EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_GetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms); | |||
extern EEL_F nseel_ramalloc_onfail; // address returned by __NSEEL_RAMAlloc et al on failure | |||
extern EEL_F * volatile nseel_gmembuf_default; // can free/zero this on DLL unload if needed | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif//__NS_EELINT_H__ |
@@ -0,0 +1,263 @@ | |||
/* | |||
Nullsoft Expression Evaluator Library (NS-EEL) | |||
Copyright (C) 1999-2003 Nullsoft, Inc. | |||
ns-eel.h: main application interface header | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#ifndef __NS_EEL_H__ | |||
#define __NS_EEL_H__ | |||
// put standard includes here | |||
#include <stdlib.h> | |||
#include <stdio.h> | |||
#ifndef EEL_F_SIZE | |||
#define EEL_F_SIZE 8 | |||
#endif | |||
#include "../wdltypes.h" | |||
typedef double EEL_F WDL_FIXALIGN; | |||
typedef double *EEL_F_PTR; | |||
#ifdef _MSC_VER | |||
#define NSEEL_CGEN_CALL __cdecl | |||
#else | |||
#define NSEEL_CGEN_CALL | |||
#endif | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
// host should implement these (can be empty stub functions if no VM will execute code in multiple threads at once) | |||
// implement if you will be running the code in same VM from multiple threads, | |||
// or VMs that have the same GRAM pointer from different threads, or multiple | |||
// VMs that have a NULL GRAM pointer from multiple threads. | |||
// if you give each VM it's own unique GRAM and only run each VM in one thread, then you can leave it blank. | |||
// or if you're daring.... | |||
void NSEEL_HOSTSTUB_EnterMutex(); | |||
void NSEEL_HOSTSTUB_LeaveMutex(); | |||
int NSEEL_init(); // returns nonzero on failure (only if EEL_VALIDATE_FSTUBS defined), otherwise the same as NSEEL_quit(), and completely optional | |||
void NSEEL_quit(); // clears any added functions | |||
// adds a function that returns a value (EEL_F) | |||
#define NSEEL_addfunc_retval(name,np,pproc,fptr) \ | |||
NSEEL_addfunc_ret_type(name,np,1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) | |||
// adds a function that returns a pointer (EEL_F*) | |||
#define NSEEL_addfunc_retptr(name,np,pproc,fptr) \ | |||
NSEEL_addfunc_ret_type(name,np,0,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) | |||
// adds a void or bool function | |||
#define NSEEL_addfunc_retbool(name,np,pproc,fptr) \ | |||
NSEEL_addfunc_ret_type(name,np,-1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION) | |||
// adds a function that takes min_np or more parameters (func sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) | |||
#define NSEEL_addfunc_varparm(name, min_np, pproc, fptr) \ | |||
NSEEL_addfunc_varparm_ex(name,min_np,0,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) | |||
// adds a function that takes np parameters via func: sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms) | |||
#define NSEEL_addfunc_exparms(name, np, pproc, fptr) \ | |||
NSEEL_addfunc_varparm_ex(name,np,1,pproc,fptr,NSEEL_ADDFUNC_DESTINATION) | |||
// deprecated | |||
#define NSEEL_addfunction(name,nparms,code,len) NSEEL_addfunctionex((name),(nparms),(code),(len),0,0) | |||
#define NSEEL_addfunctionex(name,nparms,code,len,pproc,fptr) NSEEL_addfunctionex2((name),(nparms),(code),(len),(pproc),(fptr),0, NSEEL_ADDFUNC_DESTINATION) | |||
#ifndef NSEEL_ADDFUNC_DESTINATION | |||
#define NSEEL_ADDFUNC_DESTINATION (NULL) | |||
#endif | |||
struct functionType; | |||
typedef struct | |||
{ | |||
struct functionType *list; | |||
int list_size; | |||
} eel_function_table; | |||
struct _compileContext; | |||
typedef void *(*NSEEL_PPPROC)(void *data, int data_size, struct _compileContext *userfunc_data); | |||
void NSEEL_addfunctionex2(const char *name, int nparms, char *code_startaddr, int code_len, NSEEL_PPPROC pproc, void *fptr, void *fptr2, eel_function_table *destination); | |||
void NSEEL_addfunc_ret_type(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination); // ret_type=-1 for bool, 1 for value, 0 for ptr | |||
void NSEEL_addfunc_varparm_ex(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination); | |||
void NSEEL_addfunc_varparm_ctxptr(const char *name, int min_np, int want_exact, void *ctxptr, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination); | |||
void NSEEL_addfunc_varparm_ctxptr2(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, void *ctx, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, void *,INT_PTR, EEL_F **), eel_function_table *destination); | |||
int *NSEEL_getstats(); // returns a pointer to 5 ints... source bytes, static code bytes, call code bytes, data bytes, number of code handles | |||
typedef void *NSEEL_VMCTX; | |||
typedef void *NSEEL_CODEHANDLE; | |||
NSEEL_VMCTX NSEEL_VM_alloc(); // return a handle | |||
void NSEEL_VM_free(NSEEL_VMCTX ctx); // free when done with a VM and ALL of its code have been freed, as well | |||
void NSEEL_VM_SetFunctionTable(NSEEL_VMCTX, eel_function_table *tab); // use NULL to use default (global) table | |||
// validateFunc can return error message if not permitted | |||
void NSEEL_VM_SetFunctionValidator(NSEEL_VMCTX, const char * (*validateFunc)(const char *fn_name, void *user), void *user); | |||
void NSEEL_VM_remove_unused_vars(NSEEL_VMCTX _ctx); | |||
void NSEEL_VM_clear_var_refcnts(NSEEL_VMCTX _ctx); | |||
void NSEEL_VM_remove_all_nonreg_vars(NSEEL_VMCTX _ctx); | |||
void NSEEL_VM_enumallvars(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx); // return false from func to stop | |||
EEL_F *NSEEL_VM_regvar(NSEEL_VMCTX ctx, const char *name); // register a variable (before compilation) | |||
EEL_F *NSEEL_VM_getvar(NSEEL_VMCTX ctx, const char *name); // get a variable (if registered or created by code) | |||
int NSEEL_VM_get_var_refcnt(NSEEL_VMCTX _ctx, const char *name); // returns -1 if not registered, or >=0 | |||
void NSEEL_VM_set_var_resolver(NSEEL_VMCTX ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx); | |||
void NSEEL_VM_freeRAM(NSEEL_VMCTX ctx); // clears and frees all (VM) RAM used | |||
void NSEEL_VM_freeRAMIfCodeRequested(NSEEL_VMCTX); // call after code to free the script-requested memory | |||
int NSEEL_VM_wantfreeRAM(NSEEL_VMCTX ctx); // want NSEEL_VM_freeRAMIfCodeRequested? | |||
// if you set this, it uses a local GMEM context. | |||
// Must be set before compilation. | |||
// void *p=NULL; | |||
// NSEEL_VM_SetGRAM(ctx,&p); | |||
// .. do stuff | |||
// NSEEL_VM_FreeGRAM(&p); | |||
void NSEEL_VM_SetGRAM(NSEEL_VMCTX ctx, void **gram); | |||
void NSEEL_VM_FreeGRAM(void **ufd); // frees a gmem context. | |||
void NSEEL_VM_SetCustomFuncThis(NSEEL_VMCTX ctx, void *thisptr); | |||
EEL_F *NSEEL_VM_getramptr(NSEEL_VMCTX ctx, unsigned int offs, int *validCount); | |||
EEL_F *NSEEL_VM_getramptr_noalloc(NSEEL_VMCTX ctx, unsigned int offs, int *validCount); | |||
// set 0 to query. returns actual value used (limits, granularity apply -- see NSEEL_RAM_BLOCKS) | |||
int NSEEL_VM_setramsize(NSEEL_VMCTX ctx, int maxent); | |||
struct eelStringSegmentRec { | |||
struct eelStringSegmentRec *_next; | |||
const char *str_start; // escaped characters, including opening/trailing characters | |||
int str_len; | |||
}; | |||
void NSEEL_VM_SetStringFunc(NSEEL_VMCTX ctx, | |||
EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list), | |||
EEL_F (*onNamedString)(void *caller_this, const char *name)); | |||
// call with NULL to calculate size, or non-null to generate to buffer (returning size used -- will not null terminate, caller responsibility) | |||
int nseel_stringsegments_tobuf(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list); | |||
NSEEL_CODEHANDLE NSEEL_code_compile(NSEEL_VMCTX ctx, const char *code, int lineoffs); | |||
#define NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS 1 // allows that code's functions to be used in other code (note you shouldn't destroy that codehandle without destroying others first if used) | |||
#define NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS_RESET 2 // resets common code functions | |||
#define NSEEL_CODE_COMPILE_FLAG_NOFPSTATE 4 // hint that the FPU/SSE state should be good-to-go | |||
#define NSEEL_CODE_COMPILE_FLAG_ONLY_BUILTIN_FUNCTIONS 8 // very restrictive mode (only math functions really) | |||
NSEEL_CODEHANDLE NSEEL_code_compile_ex(NSEEL_VMCTX ctx, const char *code, int lineoffs, int flags); | |||
char *NSEEL_code_getcodeerror(NSEEL_VMCTX ctx); | |||
int NSEEL_code_geterror_flag(NSEEL_VMCTX ctx); | |||
void NSEEL_code_execute(NSEEL_CODEHANDLE code); | |||
void NSEEL_code_free(NSEEL_CODEHANDLE code); | |||
int *NSEEL_code_getstats(NSEEL_CODEHANDLE code); // 4 ints...source bytes, static code bytes, call code bytes, data bytes | |||
// global memory control/view | |||
extern unsigned int NSEEL_RAM_limitmem; // if nonzero, memory limit for user data, in bytes | |||
extern unsigned int NSEEL_RAM_memused; | |||
extern int NSEEL_RAM_memused_errors; | |||
// configuration: | |||
// use the handwritten lexer -- the flex (eel2.l generated) lexer mostly works, but doesn't support string parsing at the moment | |||
// this mode is faster and uses less ram than eel2.l anyway, so leave it on | |||
#define NSEEL_SUPER_MINIMAL_LEXER | |||
// #define NSEEL_EEL1_COMPAT_MODE // supports old behaviors (continue after failed compile), old functions _bnot etc. disables string support (strings were used as comments in eel1 etc) | |||
#define NSEEL_MAX_VARIABLE_NAMELEN 128 // define this to override the max variable length | |||
#define NSEEL_MAX_EELFUNC_PARAMETERS 40 | |||
#define NSEEL_MAX_FUNCSIG_NAME 2048 // longer than variable maxlen, due to multiple namespaces | |||
// maximum loop length (0 for unlimited) | |||
#ifndef NSEEL_LOOPFUNC_SUPPORT_MAXLEN | |||
#define NSEEL_LOOPFUNC_SUPPORT_MAXLEN 1048576 | |||
#endif | |||
#define NSEEL_MAX_FUNCTION_SIZE_FOR_INLINE 2048 | |||
// when a VM ctx doesn't have a GRAM context set, make the global one this big | |||
#define NSEEL_SHARED_GRAM_SIZE (1<<20) | |||
//#define EEL_DUMP_OPS // used for testing frontend parser/logic changes | |||
// note: if you wish to change NSEEL_RAM_*, and your target is x86-64, you will | |||
// need to edit asm-nseel-x64-sse.asm to match | |||
// 512 * 65536 = 32 million entries maximum (256MB RAM) | |||
// default is limited to 128 * 65536 = 8 million entries (64MB RAM) | |||
// default to 8 million entries, use NSEEL_VM_setramsize() to change at runtime | |||
#define NSEEL_RAM_BLOCKS_DEFAULTMAX 128 | |||
// 512 entry block table maximum (2k/4k per VM) | |||
#define NSEEL_RAM_BLOCKS_LOG2 9 | |||
// 65536 items per block (512KB) | |||
#define NSEEL_RAM_ITEMSPERBLOCK_LOG2 16 | |||
#define NSEEL_RAM_BLOCKS (1 << NSEEL_RAM_BLOCKS_LOG2) | |||
#define NSEEL_RAM_ITEMSPERBLOCK (1<<NSEEL_RAM_ITEMSPERBLOCK_LOG2) | |||
#define NSEEL_STACK_SIZE 4096 // about 64k overhead if the stack functions are used in a given code handle | |||
// arch neutral mode, runs about 1/8th speed or so | |||
//#define EEL_TARGET_PORTABLE | |||
#ifdef EEL_TARGET_PORTABLE | |||
#ifdef EEL_PORTABLE_TAILCALL | |||
typedef void (*EEL_BC_TYPE)(void *next_inst, void *state); | |||
#else | |||
#define EEL_BC_TYPE int | |||
#endif | |||
#endif | |||
#ifdef NSEEL_EEL1_COMPAT_MODE | |||
double *NSEEL_getglobalregs(); | |||
#endif | |||
void eel_enterfp(int s[2]); | |||
void eel_leavefp(int s[2]); | |||
extern void *(*nseel_gmem_calloc)(size_t,size_t); // set this to the calloc() implementation used by the context that will call NSEEL_VM_FreeGRAM() | |||
#ifdef __cplusplus | |||
} | |||
#endif | |||
#endif//__NS_EEL_H__ |
@@ -0,0 +1 @@ | |||
// no longer used |
@@ -0,0 +1,145 @@ | |||
/* | |||
Expression Evaluator Library (NS-EEL) v2 | |||
Copyright (C) 2004-2013 Cockos Incorporated | |||
Copyright (C) 1999-2003 Nullsoft, Inc. | |||
nseel-cfunc.c: assembly/C implementation of operator/function templates | |||
This file should be ideally compiled with optimizations towards "minimize size" | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#include "ns-eel-int.h" | |||
#include <math.h> | |||
#include <stdio.h> | |||
// these are used by our assembly code | |||
#define N 624 | |||
#define M 397 | |||
#define MATRIX_A 0x9908b0dfUL /* constant vector a */ | |||
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ | |||
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ | |||
static unsigned int genrand_int32(void) | |||
{ | |||
unsigned int y; | |||
static unsigned int mag01[2]={0x0UL, MATRIX_A}; | |||
/* mag01[x] = x * MATRIX_A for x=0,1 */ | |||
static unsigned int mt[N]; /* the array for the state vector */ | |||
static unsigned int __idx; | |||
unsigned int mti = __idx; | |||
if (!mti) | |||
{ | |||
unsigned int s=0x4141f00d; | |||
mt[0]= s & 0xffffffffUL; | |||
for (mti=1; mti<N; mti++) | |||
{ | |||
mt[mti] = | |||
(1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); | |||
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ | |||
/* In the previous versions, MSBs of the seed affect */ | |||
/* only MSBs of the array mt[]. */ | |||
/* 2002/01/09 modified by Makoto Matsumoto */ | |||
mt[mti] &= 0xffffffffUL; | |||
/* for >32 bit machines */ | |||
} | |||
__idx = N; // mti = N (from loop) | |||
} | |||
if (mti >= N) { /* generate N words at one time */ | |||
int kk; | |||
__idx = 1; | |||
for (kk=0;kk<N-M;kk++) { | |||
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); | |||
mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL]; | |||
} | |||
for (;kk<N-1;kk++) { | |||
y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK); | |||
mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; | |||
} | |||
y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK); | |||
mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL]; | |||
mti = 0; | |||
} | |||
else | |||
__idx++; | |||
y = mt[mti]; | |||
/* Tempering */ | |||
y ^= (y >> 11); | |||
y ^= (y << 7) & 0x9d2c5680UL; | |||
y ^= (y << 15) & 0xefc60000UL; | |||
y ^= (y >> 18); | |||
return y; | |||
} | |||
//--------------------------------------------------------------------------------------------------------------- | |||
EEL_F NSEEL_CGEN_CALL nseel_int_rand(EEL_F f) | |||
{ | |||
EEL_F x=floor(f); | |||
if (x < 1.0) x=1.0; | |||
#ifdef NSEEL_EEL1_COMPAT_MODE | |||
return (EEL_F)(genrand_int32()%(int)x); | |||
#else | |||
return (EEL_F) (genrand_int32()*(1.0/(double)0xFFFFFFFF)*x); | |||
#endif | |||
} | |||
//--------------------------------------------------------------------------------------------------------------- | |||
#ifndef EEL_TARGET_PORTABLE | |||
#ifdef __ppc__ | |||
#include "asm-nseel-ppc-gcc.c" | |||
#elif defined(__aarch64__) | |||
#include "asm-nseel-aarch64-gcc.c" | |||
#elif defined(__arm__) | |||
#include "asm-nseel-arm-gcc.c" | |||
#elif defined (_M_ARM) && _M_ARM == 7 | |||
// vc on ARM, tbd | |||
#else | |||
#ifdef _MSC_VER | |||
#ifdef _WIN64 | |||
//nasm | |||
#else | |||
#include "asm-nseel-x86-msvc.c" | |||
#endif | |||
#elif !(defined(_WIN64) || defined(__LP64__)) | |||
#define EEL_F_SUFFIX "l" | |||
#define FUNCTION_MARKER "\n.byte 0x89,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90\n" | |||
#include "asm-nseel-x86-gcc.c" | |||
#endif | |||
#endif | |||
#endif |
@@ -0,0 +1,471 @@ | |||
/* | |||
Expression Evaluator Library (NS-EEL) v2 | |||
Copyright (C) 2004-2013 Cockos Incorporated | |||
Copyright (C) 1999-2003 Nullsoft, Inc. | |||
nseel-eval.c | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#include <string.h> | |||
#include <ctype.h> | |||
#include "ns-eel-int.h" | |||
#include "../wdlcstring.h" | |||
static const char *nseel_skip_space_and_comments(const char *p, const char *endptr) | |||
{ | |||
for (;;) | |||
{ | |||
while (p < endptr && isspace((unsigned char)p[0])) p++; | |||
if (p >= endptr-1 || *p != '/') return p; | |||
if (p[1]=='/') | |||
{ | |||
while (p < endptr && *p != '\r' && *p != '\n') p++; | |||
} | |||
else if (p[1] == '*') | |||
{ | |||
p+=2; | |||
while (p < endptr-1 && (p[0] != '*' || p[1] != '/')) p++; | |||
p+=2; | |||
if (p>=endptr) return endptr; | |||
} | |||
else return p; | |||
} | |||
} | |||
// removes any escaped characters, also will convert pairs delim_char into single delim_chars | |||
int nseel_filter_escaped_string(char *outbuf, int outbuf_sz, const char *rdptr, size_t rdptr_size, char delim_char) | |||
{ | |||
int outpos = 0; | |||
const char *rdptr_end = rdptr + rdptr_size; | |||
while (rdptr < rdptr_end && outpos < outbuf_sz-1) | |||
{ | |||
char thisc=*rdptr; | |||
if (thisc == '\\' && rdptr < rdptr_end-1) | |||
{ | |||
const char nc = rdptr[1]; | |||
if (nc == 'r' || nc == 'R') { thisc = '\r'; } | |||
else if (nc == 'n' || nc == 'N') { thisc = '\n'; } | |||
else if (nc == 't' || nc == 'T') { thisc = '\t'; } | |||
else if (nc == 'b' || nc == 'B') { thisc = '\b'; } | |||
else if ((nc >= '0' && nc <= '9') || nc == 'x' || nc == 'X') | |||
{ | |||
unsigned char c=0; | |||
char base_shift = 3; | |||
char num_top = '7'; | |||
rdptr++; // skip backslash | |||
if (nc > '9') // implies xX | |||
{ | |||
base_shift = 4; | |||
num_top = '9'; | |||
rdptr ++; // skip x | |||
} | |||
while (rdptr < rdptr_end) | |||
{ | |||
char tc=*rdptr; | |||
if (tc >= '0' && tc <= num_top) | |||
{ | |||
c = (c<<base_shift) + tc - '0'; | |||
} | |||
else if (base_shift==4) | |||
{ | |||
if (tc >= 'a' && tc <= 'f') | |||
{ | |||
c = (c<<base_shift) + (tc - 'a' + 10); | |||
} | |||
else if (tc >= 'A' && tc <= 'F') | |||
{ | |||
c = (c<<base_shift) + (tc - 'A' + 10); | |||
} | |||
else break; | |||
} | |||
else break; | |||
rdptr++; | |||
} | |||
outbuf[outpos++] = (char)c; | |||
continue; | |||
} | |||
else // \c where c is an unknown character drops the backslash -- works for \, ', ", etc | |||
{ | |||
thisc = nc; | |||
} | |||
rdptr+=2; | |||
} | |||
else | |||
{ | |||
if (thisc == delim_char) break; | |||
rdptr++; | |||
} | |||
outbuf[outpos++] = thisc; | |||
} | |||
outbuf[outpos]=0; | |||
return outpos; | |||
} | |||
int nseel_stringsegments_tobuf(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list) // call with NULL to calculate size, or non-null to generate to buffer (returning size used) | |||
{ | |||
int pos=0; | |||
while (list) | |||
{ | |||
if (!bufOut) | |||
{ | |||
pos += list->str_len; | |||
} | |||
else if (list->str_len > 1) | |||
{ | |||
if (pos >= bufout_sz) break; | |||
pos += nseel_filter_escaped_string(bufOut + pos, bufout_sz-pos, list->str_start+1, list->str_len-1, list->str_start[0]); | |||
} | |||
list = list->_next; | |||
} | |||
return pos; | |||
} | |||
// state can be NULL, it will be set if finished with unterminated thing: 1 for multiline comment, ' or " for string | |||
const char *nseel_simple_tokenizer(const char **ptr, const char *endptr, int *lenOut, int *state) | |||
{ | |||
const char *p = *ptr; | |||
const char *rv = p; | |||
char delim; | |||
if (state) // if state set, returns comments as tokens | |||
{ | |||
if (*state == 1) goto in_comment; | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
if (*state == '\'' || *state == '\"') | |||
{ | |||
delim = (char)*state; | |||
goto in_string; | |||
} | |||
#endif | |||
// skip any whitespace | |||
while (p < endptr && isspace((unsigned char)p[0])) p++; | |||
} | |||
else | |||
{ | |||
// state not passed, skip comments (do not return them as tokens) | |||
p = nseel_skip_space_and_comments(p,endptr); | |||
} | |||
if (p >= endptr) | |||
{ | |||
*ptr = endptr; | |||
*lenOut = 0; | |||
return NULL; | |||
} | |||
rv=p; | |||
if (*p == '$' && p+3 < endptr && p[1] == '\'' && p[3] == '\'') | |||
{ | |||
p+=4; | |||
} | |||
else if (state && *p == '/' && p < endptr-1 && (p[1] == '/' || p[1] == '*')) | |||
{ | |||
if (p[1] == '/') | |||
{ | |||
while (p < endptr && *p != '\r' && *p != '\n') p++; // advance to end of line | |||
} | |||
else | |||
{ | |||
if (state) *state=1; | |||
p+=2; | |||
in_comment: | |||
while (p < endptr) | |||
{ | |||
const char c = *p++; | |||
if (c == '*' && p < endptr && *p == '/') | |||
{ | |||
p++; | |||
if (state) *state=0; | |||
break; | |||
} | |||
} | |||
} | |||
} | |||
else if (isalnum((unsigned char)*p) || *p == '_' || *p == '#' || *p == '$' || (*p == '.' && p < endptr-1 && p[1] >= '0' && p[1] <= '9')) | |||
{ | |||
if (*p == '$' && p < endptr-1 && p[1] == '~') p++; | |||
p++; | |||
while (p < endptr && (isalnum((unsigned char)*p) || *p == '_' || *p == '.')) p++; | |||
} | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
else if (*p == '\'' || *p == '\"') | |||
{ | |||
delim = *p++; | |||
if (state) *state=delim; | |||
in_string: | |||
while (p < endptr) | |||
{ | |||
const char c = *p++; | |||
if (p < endptr && c == '\\') p++; // skip escaped characters | |||
else if (c == delim) | |||
{ | |||
if (state) *state=0; | |||
break; | |||
} | |||
} | |||
} | |||
#endif | |||
else | |||
{ | |||
p++; | |||
} | |||
*ptr = p; | |||
*lenOut = (int) (p - rv); | |||
return p>rv ? rv : NULL; | |||
} | |||
#ifdef NSEEL_SUPER_MINIMAL_LEXER | |||
int nseellex(opcodeRec **output, YYLTYPE * yylloc_param, compileContext *scctx) | |||
{ | |||
int rv=0,toklen=0; | |||
const char *rdptr = scctx->rdbuf; | |||
const char *endptr = scctx->rdbuf_end; | |||
const char *tok = nseel_simple_tokenizer(&rdptr,endptr,&toklen,NULL); | |||
*output = 0; | |||
if (tok) | |||
{ | |||
rv = tok[0]; | |||
if ((rv == '0' || rv == '$') && toklen > 1 && (tok[1] == 'x' || tok[1] == 'X')) // 0xf00 or $xf00 | |||
{ | |||
int x; | |||
for (x = 2; x < toklen; x ++) | |||
if (!((tok[x] >= '0' && tok[x] <= '9') || | |||
(tok[x] >= 'a' && tok[x] <= 'f') || | |||
(tok[x] >= 'A' && tok[x] <= 'F'))) | |||
{ | |||
tok += x; | |||
break; | |||
} | |||
*output = x == toklen && toklen > 2 ? nseel_translate(scctx,tok,toklen) : NULL; | |||
if (*output) rv=VALUE; | |||
} | |||
#ifndef NSEEL_EEL1_COMPAT_MODE | |||
else if (rv == '#' && scctx->onNamedString) | |||
{ | |||
*output = nseel_translate(scctx,tok,toklen); | |||
if (*output) rv=STRING_IDENTIFIER; | |||
} | |||
else if (rv == '\'') | |||
{ | |||
if (toklen > 1 && tok[toklen-1] == '\'') | |||
{ | |||
*output = nseel_translate(scctx, tok, toklen); | |||
if (*output) rv = VALUE; | |||
} | |||
else scctx->gotEndOfInput|=8; | |||
} | |||
else if (rv == '\"' && scctx->onString) | |||
{ | |||
if (toklen > 1 && tok[toklen-1] == '\"') | |||
{ | |||
*output = (opcodeRec *)nseel_createStringSegmentRec(scctx,tok,toklen); | |||
if (*output) rv = STRING_LITERAL; | |||
} | |||
else scctx->gotEndOfInput|=16; | |||
} | |||
#endif | |||
else if (isalpha((unsigned char)rv) || rv == '_') | |||
{ | |||
char buf[NSEEL_MAX_VARIABLE_NAMELEN*2]; | |||
if (toklen > sizeof(buf) - 1) toklen=sizeof(buf) - 1; | |||
memcpy(buf,tok,toklen); | |||
buf[toklen]=0; | |||
*output = nseel_createCompiledValuePtr(scctx, NULL, buf); | |||
if (*output) rv = IDENTIFIER; | |||
} | |||
else if ((rv >= '0' && rv <= '9') || (rv == '.' && toklen > 1 && tok[1] >= '0' && tok[1] <= '9')) // 123.45 or .45 | |||
{ | |||
int x, pcnt = 0; | |||
for (x = 0; x < toklen; x ++) | |||
{ | |||
if (tok[x] == '.' ? (++pcnt > 1) : (tok[x] < '0' || tok[x] > '9')) | |||
{ | |||
tok += x; | |||
break; | |||
} | |||
} | |||
*output = x == toklen ? nseel_translate(scctx,tok,toklen) : NULL; | |||
if (*output) rv=VALUE; | |||
} | |||
else if (rv == '$' && toklen > 1) | |||
{ | |||
if (tok[1] == '~') | |||
{ | |||
int x; | |||
for (x = 2; x < toklen; x ++) | |||
if (tok[x] < '0' || tok[x] > '9') | |||
{ | |||
tok += x; | |||
break; | |||
} | |||
if (x<toklen || toklen == 2) toklen = 0; | |||
} | |||
*output = toklen > 1 ? nseel_translate(scctx,tok,toklen) : NULL; | |||
if (*output) rv=VALUE; | |||
} | |||
else if (rv == '<') | |||
{ | |||
const char nc=*rdptr; | |||
if (nc == '<') | |||
{ | |||
rdptr++; | |||
rv=TOKEN_SHL; | |||
} | |||
else if (nc == '=') | |||
{ | |||
rdptr++; | |||
rv=TOKEN_LTE; | |||
} | |||
} | |||
else if (rv == '>') | |||
{ | |||
const char nc=*rdptr; | |||
if (nc == '>') | |||
{ | |||
rdptr++; | |||
rv=TOKEN_SHR; | |||
} | |||
else if (nc == '=') | |||
{ | |||
rdptr++; | |||
rv=TOKEN_GTE; | |||
} | |||
} | |||
else if (rv == '&' && *rdptr == '&') | |||
{ | |||
rdptr++; | |||
rv = TOKEN_LOGICAL_AND; | |||
} | |||
else if (rv == '|' && *rdptr == '|') | |||
{ | |||
rdptr++; | |||
rv = TOKEN_LOGICAL_OR; | |||
} | |||
else if (*rdptr == '=') | |||
{ | |||
switch (rv) | |||
{ | |||
case '+': rv=TOKEN_ADD_OP; rdptr++; break; | |||
case '-': rv=TOKEN_SUB_OP; rdptr++; break; | |||
case '%': rv=TOKEN_MOD_OP; rdptr++; break; | |||
case '|': rv=TOKEN_OR_OP; rdptr++; break; | |||
case '&': rv=TOKEN_AND_OP; rdptr++; break; | |||
case '~': rv=TOKEN_XOR_OP; rdptr++; break; | |||
case '/': rv=TOKEN_DIV_OP; rdptr++; break; | |||
case '*': rv=TOKEN_MUL_OP; rdptr++; break; | |||
case '^': rv=TOKEN_POW_OP; rdptr++; break; | |||
case '!': | |||
rdptr++; | |||
if (rdptr < endptr && *rdptr == '=') | |||
{ | |||
rdptr++; | |||
rv=TOKEN_NE_EXACT; | |||
} | |||
else | |||
rv=TOKEN_NE; | |||
break; | |||
case '=': | |||
rdptr++; | |||
if (rdptr < endptr && *rdptr == '=') | |||
{ | |||
rdptr++; | |||
rv=TOKEN_EQ_EXACT; | |||
} | |||
else | |||
rv=TOKEN_EQ; | |||
break; | |||
} | |||
} | |||
} | |||
scctx->rdbuf = rdptr; | |||
yylloc_param->first_column = (int)(tok - scctx->rdbuf_start); | |||
return rv; | |||
} | |||
void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str) | |||
{ | |||
ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start); | |||
} | |||
#else | |||
int nseel_gets(compileContext *ctx, char *buf, size_t sz) | |||
{ | |||
int n=0; | |||
const char *endptr = ctx->rdbuf_end; | |||
const char *rdptr = ctx->rdbuf; | |||
if (!rdptr) return 0; | |||
while (n < sz && rdptr < endptr) buf[n++] = *rdptr++; | |||
ctx->rdbuf=rdptr; | |||
return n; | |||
} | |||
//#define EEL_TRACE_LEX | |||
#ifdef EEL_TRACE_LEX | |||
#define nseellex nseellex2 | |||
#endif | |||
#include "lex.nseel.c" | |||
#ifdef EEL_TRACE_LEX | |||
#undef nseellex | |||
int nseellex(YYSTYPE * yylval_param, YYLTYPE * yylloc_param , yyscan_t yyscanner) | |||
{ | |||
int a=nseellex2(yylval_param,yylloc_param,yyscanner); | |||
char buf[512]; | |||
sprintf(buf,"tok: %c (%d)\n",a,a); | |||
OutputDebugString(buf); | |||
return a; | |||
} | |||
#endif//EEL_TRACE_LEX | |||
void nseelerror(YYLTYPE *pos,compileContext *ctx, const char *str) | |||
{ | |||
ctx->errVar=pos->first_column>0?pos->first_column:(int)(ctx->rdbuf_end - ctx->rdbuf_start); | |||
} | |||
#endif // !NSEEL_SUPER_MINIMAL_LEXER |
@@ -0,0 +1 @@ | |||
// no longer used |
@@ -0,0 +1,463 @@ | |||
/* | |||
Expression Evaluator Library (NS-EEL) v2 | |||
Copyright (C) 2004-2013 Cockos Incorporated | |||
Copyright (C) 1999-2003 Nullsoft, Inc. | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#include "ns-eel.h" | |||
#include "ns-eel-int.h" | |||
#include <math.h> | |||
#include <stdio.h> | |||
#include <string.h> | |||
#include <stdlib.h> | |||
#ifdef _WIN32 | |||
#include <malloc.h> | |||
#ifdef _MSC_VER | |||
#define inline __inline | |||
#endif | |||
#endif | |||
unsigned int NSEEL_RAM_limitmem=0; | |||
unsigned int NSEEL_RAM_memused=0; | |||
int NSEEL_RAM_memused_errors=0; | |||
int NSEEL_VM_wantfreeRAM(NSEEL_VMCTX ctx) | |||
{ | |||
if (ctx) | |||
{ | |||
compileContext *c=(compileContext*)ctx; | |||
if (c->ram_state->needfree) | |||
return 1; | |||
} | |||
return 0; | |||
} | |||
void NSEEL_VM_freeRAMIfCodeRequested(NSEEL_VMCTX ctx) // check to see if our free flag was set | |||
{ | |||
if (ctx) | |||
{ | |||
compileContext *c=(compileContext*)ctx; | |||
if (c->ram_state->needfree) | |||
{ | |||
NSEEL_HOSTSTUB_EnterMutex(); | |||
{ | |||
INT_PTR startpos=((INT_PTR)c->ram_state->needfree)-1; | |||
EEL_F **blocks = c->ram_state->blocks; | |||
INT_PTR pos=0; | |||
int x; | |||
for (x = 0; x < NSEEL_RAM_BLOCKS; x ++) | |||
{ | |||
if (pos >= startpos) | |||
{ | |||
if (blocks[x]) | |||
{ | |||
if (NSEEL_RAM_memused >= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK) | |||
NSEEL_RAM_memused -= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; | |||
else NSEEL_RAM_memused_errors++; | |||
free(blocks[x]); | |||
blocks[x]=0; | |||
} | |||
} | |||
pos+=NSEEL_RAM_ITEMSPERBLOCK; | |||
} | |||
c->ram_state->needfree=0; | |||
} | |||
NSEEL_HOSTSTUB_LeaveMutex(); | |||
} | |||
} | |||
} | |||
EEL_F nseel_ramalloc_onfail; | |||
EEL_F * volatile nseel_gmembuf_default; | |||
void *(*nseel_gmem_calloc)(size_t a, size_t b); | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAllocGMEM(EEL_F ***blocks, unsigned int w) | |||
{ | |||
if (blocks) | |||
{ | |||
EEL_F **pblocks=*blocks; | |||
if (w < NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) | |||
{ | |||
const unsigned int whichblock = w/NSEEL_RAM_ITEMSPERBLOCK; | |||
EEL_F *p=NULL; | |||
if (!pblocks || !(p=pblocks[whichblock])) | |||
{ | |||
NSEEL_HOSTSTUB_EnterMutex(); | |||
if (!nseel_gmem_calloc) nseel_gmem_calloc=calloc; | |||
if (!(pblocks=*blocks)) pblocks = *blocks = (EEL_F **)nseel_gmem_calloc(sizeof(EEL_F *),NSEEL_RAM_BLOCKS); | |||
else p = pblocks[whichblock]; | |||
if (!p && pblocks) | |||
{ | |||
p=pblocks[whichblock]=(EEL_F *)nseel_gmem_calloc(sizeof(EEL_F),NSEEL_RAM_ITEMSPERBLOCK); | |||
} | |||
NSEEL_HOSTSTUB_LeaveMutex(); | |||
} | |||
if (p) return p + (w&(NSEEL_RAM_ITEMSPERBLOCK-1)); | |||
} | |||
return &nseel_ramalloc_onfail; | |||
} | |||
if (!nseel_gmembuf_default) | |||
{ | |||
NSEEL_HOSTSTUB_EnterMutex(); | |||
if (!nseel_gmembuf_default) nseel_gmembuf_default=(EEL_F*)calloc(sizeof(EEL_F),NSEEL_SHARED_GRAM_SIZE); | |||
NSEEL_HOSTSTUB_LeaveMutex(); | |||
if (!nseel_gmembuf_default) return &nseel_ramalloc_onfail; | |||
} | |||
return nseel_gmembuf_default+(((unsigned int)w)&((NSEEL_SHARED_GRAM_SIZE)-1)); | |||
} | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAMAlloc(EEL_F **pblocks, unsigned int w) | |||
{ | |||
// fprintf(stderr,"got request at %d, %d\n",w/NSEEL_RAM_ITEMSPERBLOCK, w&(NSEEL_RAM_ITEMSPERBLOCK-1)); | |||
if (w < NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) | |||
{ | |||
unsigned int whichblock = w/NSEEL_RAM_ITEMSPERBLOCK; | |||
EEL_F *p=pblocks[whichblock]; | |||
if (!p && whichblock < ((unsigned int *)pblocks)[-3]) // pblocks -1/-2 are closefact, -3 is maxblocks | |||
{ | |||
NSEEL_HOSTSTUB_EnterMutex(); | |||
if (!(p=pblocks[whichblock])) | |||
{ | |||
const int msize=sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; | |||
if (!NSEEL_RAM_limitmem || NSEEL_RAM_memused+msize < NSEEL_RAM_limitmem) | |||
{ | |||
p=pblocks[whichblock]=(EEL_F *)calloc(sizeof(EEL_F),NSEEL_RAM_ITEMSPERBLOCK); | |||
if (p) NSEEL_RAM_memused+=msize; | |||
} | |||
} | |||
NSEEL_HOSTSTUB_LeaveMutex(); | |||
} | |||
if (p) return p + (w&(NSEEL_RAM_ITEMSPERBLOCK-1)); | |||
} | |||
// fprintf(stderr,"ret 0\n"); | |||
return &nseel_ramalloc_onfail; | |||
} | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemFree(void *blocks, EEL_F *which) | |||
{ | |||
// blocks points to ram_state->blocks, so back it up past closefact and maxblocks to needfree | |||
int *flag = (int *)((char *)blocks - sizeof(double) - 2*sizeof(int)); | |||
int d=(int)(*which); | |||
if (d < 0) d=0; | |||
if (d < flag[1]*NSEEL_RAM_ITEMSPERBLOCK) flag[0]=1+d; | |||
return which; | |||
} | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemTop(void *blocks, EEL_F *which) | |||
{ | |||
// blocks points to ram_state->blocks, so back it up past closefact to maxblocks | |||
const int *flag = (int *)((char *)blocks - sizeof(double) - sizeof(int)); | |||
*which = flag[0]*NSEEL_RAM_ITEMSPERBLOCK; | |||
return which; | |||
} | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemCpy(EEL_F **blocks,EEL_F *dest, EEL_F *src, EEL_F *lenptr) | |||
{ | |||
const int mem_size=NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK; | |||
int dest_offs = (int)(*dest + 0.0001); | |||
int src_offs = (int)(*src + 0.0001); | |||
int len = (int)(*lenptr + 0.0001); | |||
int want_mmove=0; | |||
// trim to front | |||
if (src_offs<0) | |||
{ | |||
len += src_offs; | |||
dest_offs -= src_offs; | |||
src_offs=0; | |||
} | |||
if (dest_offs<0) | |||
{ | |||
len += dest_offs; | |||
src_offs -= dest_offs; | |||
dest_offs=0; | |||
} | |||
if (src_offs + len > mem_size) len = mem_size-src_offs; | |||
if (dest_offs + len > mem_size) len = mem_size-dest_offs; | |||
if (src_offs == dest_offs || len < 1) return dest; | |||
if (src_offs < dest_offs && src_offs+len > dest_offs) | |||
{ | |||
// if src_offs < dest_offs and overlapping, must copy right to left | |||
if ((dest_offs - src_offs) < NSEEL_RAM_ITEMSPERBLOCK) want_mmove = 1; | |||
src_offs += len; | |||
dest_offs += len; | |||
while (len > 0) | |||
{ | |||
const int maxdlen=((dest_offs-1)&(NSEEL_RAM_ITEMSPERBLOCK-1)) + 1; | |||
const int maxslen=((src_offs-1)&(NSEEL_RAM_ITEMSPERBLOCK-1)) + 1; | |||
int copy_len = len; | |||
EEL_F *srcptr,*destptr; | |||
if (copy_len > maxdlen) copy_len=maxdlen; | |||
if (copy_len > maxslen) copy_len=maxslen; | |||
srcptr = __NSEEL_RAMAlloc(blocks,src_offs - copy_len); | |||
destptr = __NSEEL_RAMAlloc(blocks,dest_offs - copy_len); | |||
if (srcptr==&nseel_ramalloc_onfail || destptr==&nseel_ramalloc_onfail) break; | |||
if (want_mmove) memmove(destptr,srcptr,sizeof(EEL_F)*copy_len); | |||
else memcpy(destptr,srcptr,sizeof(EEL_F)*copy_len); | |||
src_offs-=copy_len; | |||
dest_offs-=copy_len; | |||
len-=copy_len; | |||
} | |||
return dest; | |||
} | |||
if (dest_offs < src_offs && dest_offs+len > src_offs) | |||
{ | |||
// if dest_offs < src_offs and overlapping, and less than NSEEL_RAM_ITEMSPERBLOCK apart, use memmove() | |||
if ((src_offs-dest_offs) < NSEEL_RAM_ITEMSPERBLOCK) want_mmove = 1; | |||
} | |||
while (len > 0) | |||
{ | |||
const int maxdlen=NSEEL_RAM_ITEMSPERBLOCK - (dest_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)); | |||
const int maxslen=NSEEL_RAM_ITEMSPERBLOCK - (src_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)); | |||
int copy_len = len; | |||
EEL_F *srcptr,*destptr; | |||
if (copy_len > maxdlen) copy_len=maxdlen; | |||
if (copy_len > maxslen) copy_len=maxslen; | |||
srcptr = __NSEEL_RAMAlloc(blocks,src_offs); | |||
destptr = __NSEEL_RAMAlloc(blocks,dest_offs); | |||
if (srcptr==&nseel_ramalloc_onfail || destptr==&nseel_ramalloc_onfail) break; | |||
if (want_mmove) memmove(destptr,srcptr,sizeof(EEL_F)*copy_len); | |||
else memcpy(destptr,srcptr,sizeof(EEL_F)*copy_len); | |||
src_offs+=copy_len; | |||
dest_offs+=copy_len; | |||
len-=copy_len; | |||
} | |||
return dest; | |||
} | |||
EEL_F * NSEEL_CGEN_CALL __NSEEL_RAM_MemSet(EEL_F **blocks,EEL_F *dest, EEL_F *v, EEL_F *lenptr) | |||
{ | |||
int offs = (int)(*dest + 0.0001); | |||
int len = (int)(*lenptr + 0.0001); | |||
EEL_F t; | |||
if (offs<0) | |||
{ | |||
len += offs; | |||
offs=0; | |||
} | |||
if (offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return dest; | |||
if (offs+len > NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) len = NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK - offs; | |||
if (len < 1) return dest; | |||
t=*v; // set value | |||
// int lastBlock=-1; | |||
while (len > 0) | |||
{ | |||
int lcnt; | |||
EEL_F *ptr=__NSEEL_RAMAlloc(blocks,offs); | |||
if (ptr==&nseel_ramalloc_onfail) break; | |||
lcnt=NSEEL_RAM_ITEMSPERBLOCK-(offs&(NSEEL_RAM_ITEMSPERBLOCK-1)); | |||
if (lcnt > len) lcnt=len; | |||
len -= lcnt; | |||
offs += lcnt; | |||
while (lcnt--) | |||
{ | |||
*ptr++=t; | |||
} | |||
} | |||
return dest; | |||
} | |||
static inline int __getset_values(EEL_F **blocks, int isset, int len, EEL_F **parms) | |||
{ | |||
int offs, lout=0; | |||
unsigned int pageidx, sub_offs; | |||
if (--len < 1) return 0; | |||
offs = (int)(parms++[0][0] + 0.0001); | |||
if (offs<=0) | |||
{ | |||
len += offs; | |||
parms -= offs; | |||
offs=0; | |||
pageidx=sub_offs=0; | |||
if (len<1) return 0; | |||
} | |||
else | |||
{ | |||
sub_offs = ((unsigned int)offs) & (NSEEL_RAM_ITEMSPERBLOCK-1); | |||
pageidx = ((unsigned int)offs)>>NSEEL_RAM_ITEMSPERBLOCK_LOG2; | |||
if (pageidx>=NSEEL_RAM_BLOCKS) return 0; | |||
} | |||
for (;;) | |||
{ | |||
int lcnt=NSEEL_RAM_ITEMSPERBLOCK-sub_offs; | |||
EEL_F *ptr=blocks[pageidx]; | |||
if (!ptr) | |||
{ | |||
ptr = __NSEEL_RAMAlloc(blocks,offs + lout); | |||
if (ptr==&nseel_ramalloc_onfail) return lout; | |||
} | |||
else | |||
{ | |||
ptr += sub_offs; | |||
} | |||
if (lcnt >= len) | |||
{ | |||
// this page satisfies the request (normal behavior) | |||
lout += len; | |||
if (isset) while (len--) *ptr++=parms++[0][0]; | |||
else while (len--) parms++[0][0] = *ptr++; | |||
return lout; | |||
} | |||
// crossing a page boundary | |||
len -= lcnt; | |||
lout += lcnt; | |||
if (isset) while (lcnt--) *ptr++=parms++[0][0]; | |||
else while (lcnt--) parms++[0][0] = *ptr++; | |||
if (len <= 0 || ++pageidx >= NSEEL_RAM_BLOCKS) return lout; | |||
sub_offs=0; | |||
} | |||
} | |||
EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_SetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms) | |||
{ | |||
return __getset_values(blocks,1,(int)np,parms); | |||
} | |||
EEL_F NSEEL_CGEN_CALL __NSEEL_RAM_Mem_GetValues(EEL_F **blocks, INT_PTR np, EEL_F **parms) | |||
{ | |||
return __getset_values(blocks,0,(int)np,parms); | |||
} | |||
void NSEEL_VM_SetGRAM(NSEEL_VMCTX ctx, void **gram) | |||
{ | |||
if (ctx) | |||
{ | |||
compileContext *c=(compileContext*)ctx; | |||
c->gram_blocks = gram; | |||
} | |||
} | |||
void NSEEL_VM_freeRAM(NSEEL_VMCTX ctx) | |||
{ | |||
if (ctx) | |||
{ | |||
int x; | |||
compileContext *c=(compileContext*)ctx; | |||
EEL_F **blocks = c->ram_state->blocks; | |||
for (x = 0; x < NSEEL_RAM_BLOCKS; x ++) | |||
{ | |||
if (blocks[x]) | |||
{ | |||
if (NSEEL_RAM_memused >= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK) | |||
NSEEL_RAM_memused -= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; | |||
else NSEEL_RAM_memused_errors++; | |||
free(blocks[x]); | |||
blocks[x]=0; | |||
} | |||
} | |||
c->ram_state->needfree=0; // no need to free anymore | |||
} | |||
} | |||
void NSEEL_VM_FreeGRAM(void **ufd) | |||
{ | |||
if (ufd[0]) | |||
{ | |||
EEL_F **blocks = (EEL_F **)ufd[0]; | |||
int x; | |||
for (x = 0; x < NSEEL_RAM_BLOCKS; x ++) | |||
{ | |||
if (blocks[x]) | |||
{ | |||
if (NSEEL_RAM_memused >= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK) | |||
NSEEL_RAM_memused -= sizeof(EEL_F) * NSEEL_RAM_ITEMSPERBLOCK; | |||
else NSEEL_RAM_memused_errors++; | |||
} | |||
free(blocks[x]); | |||
blocks[x]=0; | |||
} | |||
free(blocks); | |||
ufd[0]=0; | |||
} | |||
} | |||
EEL_F *NSEEL_VM_getramptr(NSEEL_VMCTX ctx, unsigned int offs, int *validCount) | |||
{ | |||
EEL_F *d=__NSEEL_RAMAlloc(ctx ? ((compileContext*)ctx)->ram_state->blocks : 0,offs); | |||
if (!d || d == &nseel_ramalloc_onfail) return NULL; | |||
if (validCount) *validCount = NSEEL_RAM_ITEMSPERBLOCK - (offs%NSEEL_RAM_ITEMSPERBLOCK); | |||
return d; | |||
} | |||
EEL_F *NSEEL_VM_getramptr_noalloc(NSEEL_VMCTX ctx, unsigned int offs, int *validCount) | |||
{ | |||
EEL_F *d; | |||
compileContext *cc = (compileContext *)ctx; | |||
if (!cc || | |||
offs >= NSEEL_RAM_ITEMSPERBLOCK*NSEEL_RAM_BLOCKS || | |||
NULL == (d = cc->ram_state->blocks[offs/NSEEL_RAM_ITEMSPERBLOCK]) | |||
) | |||
{ | |||
if (validCount) *validCount = 0; | |||
return NULL; | |||
} | |||
offs %= NSEEL_RAM_ITEMSPERBLOCK; | |||
if (validCount) *validCount = NSEEL_RAM_ITEMSPERBLOCK - offs; | |||
return d + offs; | |||
} |
@@ -0,0 +1,43 @@ | |||
/* | |||
Expression Evaluator Library (NS-EEL) | |||
Copyright (C) 2004-2013 Cockos Incorporated | |||
Copyright (C) 1999-2003 Nullsoft, Inc. | |||
nseel-yylex.c | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#include "ns-eel-int.h" | |||
# define YYMALLOC malloc | |||
# define YYFREE free | |||
int nseellex(void * yylval_param,void * yylloc_param ,void *yyscanner); | |||
void nseelerror(void *pos,compileContext *ctx, const char *str); | |||
// inhibit a warning: | |||
static void WDL_STATICFUNC_UNUSED yydestruct(const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocationp, compileContext* context); | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include "y.tab.c" | |||
@@ -0,0 +1,117 @@ | |||
/* A Bison parser, made by GNU Bison 2.3. */ | |||
/* Skeleton interface for Bison's Yacc-like parsers in C | |||
Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 | |||
Free Software Foundation, Inc. | |||
This program is free software; you can redistribute it and/or modify | |||
it under the terms of the GNU General Public License as published by | |||
the Free Software Foundation; either version 2, or (at your option) | |||
any later version. | |||
This program is distributed in the hope that it will be useful, | |||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
GNU General Public License for more details. | |||
You should have received a copy of the GNU General Public License | |||
along with this program; if not, write to the Free Software | |||
Foundation, Inc., 51 Franklin Street, Fifth Floor, | |||
Boston, MA 02110-1301, USA. */ | |||
/* As a special exception, you may create a larger work that contains | |||
part or all of the Bison parser skeleton and distribute that work | |||
under terms of your choice, so long as that work isn't itself a | |||
parser generator using the skeleton or a modified version thereof | |||
as a parser skeleton. Alternatively, if you modify or redistribute | |||
the parser skeleton itself, you may (at your option) remove this | |||
special exception, which will cause the skeleton and the resulting | |||
Bison output files to be licensed under the GNU General Public | |||
License without this special exception. | |||
This special exception was added by the Free Software Foundation in | |||
version 2.2 of Bison. */ | |||
/* Tokens. */ | |||
#ifndef YYTOKENTYPE | |||
# define YYTOKENTYPE | |||
/* Put the tokens into the symbol table, so that GDB and other debuggers | |||
know about them. */ | |||
enum yytokentype { | |||
VALUE = 258, | |||
IDENTIFIER = 259, | |||
TOKEN_SHL = 260, | |||
TOKEN_SHR = 261, | |||
TOKEN_LTE = 262, | |||
TOKEN_GTE = 263, | |||
TOKEN_EQ = 264, | |||
TOKEN_EQ_EXACT = 265, | |||
TOKEN_NE = 266, | |||
TOKEN_NE_EXACT = 267, | |||
TOKEN_LOGICAL_AND = 268, | |||
TOKEN_LOGICAL_OR = 269, | |||
TOKEN_ADD_OP = 270, | |||
TOKEN_SUB_OP = 271, | |||
TOKEN_MOD_OP = 272, | |||
TOKEN_OR_OP = 273, | |||
TOKEN_AND_OP = 274, | |||
TOKEN_XOR_OP = 275, | |||
TOKEN_DIV_OP = 276, | |||
TOKEN_MUL_OP = 277, | |||
TOKEN_POW_OP = 278, | |||
STRING_LITERAL = 279, | |||
STRING_IDENTIFIER = 280 | |||
}; | |||
#endif | |||
/* Tokens. */ | |||
#define VALUE 258 | |||
#define IDENTIFIER 259 | |||
#define TOKEN_SHL 260 | |||
#define TOKEN_SHR 261 | |||
#define TOKEN_LTE 262 | |||
#define TOKEN_GTE 263 | |||
#define TOKEN_EQ 264 | |||
#define TOKEN_EQ_EXACT 265 | |||
#define TOKEN_NE 266 | |||
#define TOKEN_NE_EXACT 267 | |||
#define TOKEN_LOGICAL_AND 268 | |||
#define TOKEN_LOGICAL_OR 269 | |||
#define TOKEN_ADD_OP 270 | |||
#define TOKEN_SUB_OP 271 | |||
#define TOKEN_MOD_OP 272 | |||
#define TOKEN_OR_OP 273 | |||
#define TOKEN_AND_OP 274 | |||
#define TOKEN_XOR_OP 275 | |||
#define TOKEN_DIV_OP 276 | |||
#define TOKEN_MUL_OP 277 | |||
#define TOKEN_POW_OP 278 | |||
#define STRING_LITERAL 279 | |||
#define STRING_IDENTIFIER 280 | |||
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED | |||
typedef int YYSTYPE; | |||
# define yystype YYSTYPE /* obsolescent; will be withdrawn */ | |||
# define YYSTYPE_IS_DECLARED 1 | |||
# define YYSTYPE_IS_TRIVIAL 1 | |||
#endif | |||
#if ! defined YYLTYPE && ! defined YYLTYPE_IS_DECLARED | |||
typedef struct YYLTYPE | |||
{ | |||
int first_line; | |||
int first_column; | |||
int last_line; | |||
int last_column; | |||
} YYLTYPE; | |||
# define yyltype YYLTYPE /* obsolescent; will be withdrawn */ | |||
# define YYLTYPE_IS_DECLARED 1 | |||
# define YYLTYPE_IS_TRIVIAL 1 | |||
#endif | |||
@@ -0,0 +1,77 @@ | |||
/* | |||
WDL - fft.h | |||
Copyright (C) 2006 and later Cockos Incorporated | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
This file defines the interface to the WDL FFT library. These routines are based on the | |||
DJBFFT library, which are Copyright 1999 D. J. Bernstein, djb@pobox.com | |||
The DJB FFT web page is: http://cr.yp.to/djbfft.html | |||
*/ | |||
#ifndef _WDL_FFT_H_ | |||
#define _WDL_FFT_H_ | |||
#ifdef __cplusplus | |||
extern "C" { | |||
#endif | |||
#ifndef WDL_FFT_REALSIZE | |||
#define WDL_FFT_REALSIZE 4 | |||
#endif | |||
#if WDL_FFT_REALSIZE == 4 | |||
typedef float WDL_FFT_REAL; | |||
#elif WDL_FFT_REALSIZE == 8 | |||
typedef double WDL_FFT_REAL; | |||
#else | |||
#error invalid FFT item size | |||
#endif | |||
typedef struct { | |||
WDL_FFT_REAL re; | |||
WDL_FFT_REAL im; | |||
} WDL_FFT_COMPLEX; | |||
extern void WDL_fft_init(); | |||
extern void WDL_fft_complexmul(WDL_FFT_COMPLEX *dest, WDL_FFT_COMPLEX *src, int len); | |||
extern void WDL_fft_complexmul2(WDL_FFT_COMPLEX *dest, WDL_FFT_COMPLEX *src, WDL_FFT_COMPLEX *src2, int len); | |||
extern void WDL_fft_complexmul3(WDL_FFT_COMPLEX *destAdd, WDL_FFT_COMPLEX *src, WDL_FFT_COMPLEX *src2, int len); | |||
/* Expects WDL_FFT_COMPLEX input[0..len-1] scaled by 1.0/len, returns | |||
WDL_FFT_COMPLEX output[0..len-1] order by WDL_fft_permute(len). */ | |||
extern void WDL_fft(WDL_FFT_COMPLEX *, int len, int isInverse); | |||
/* Expects WDL_FFT_REAL input[0..len-1] scaled by 0.5/len, returns | |||
WDL_FFT_COMPLEX output[0..len/2-1], for len >= 4 order by | |||
WDL_fft_permute(len/2). Note that output[len/2].re is stored in | |||
output[0].im. */ | |||
extern void WDL_real_fft(WDL_FFT_REAL *, int len, int isInverse); | |||
extern int WDL_fft_permute(int fftsize, int idx); | |||
extern int *WDL_fft_permute_tab(int fftsize); | |||
#ifdef __cplusplus | |||
}; | |||
#endif | |||
#endif |
@@ -0,0 +1,222 @@ | |||
#ifndef _WDL_HASSTRINGS_H_ | |||
#define _WDL_HASSTRINGS_H_ | |||
#ifndef WDL_HASSTRINGS_EXPORT | |||
#define WDL_HASSTRINGS_EXPORT | |||
#endif | |||
WDL_HASSTRINGS_EXPORT bool hasStrings_isNonWordChar(int c) | |||
{ | |||
// treat as whitespace when searching for " foo " | |||
switch (c) | |||
{ | |||
case 0: | |||
case 1: | |||
case ' ': | |||
case '\t': | |||
case '.': | |||
case '/': | |||
case '\\': | |||
return true; | |||
default: | |||
return false; | |||
} | |||
} | |||
WDL_HASSTRINGS_EXPORT bool WDL_hasStringsEx(const char *name, const LineParser *lp, | |||
int (*cmp_func)(const char *a, int apos, const char *b, int blen) // if set, returns length of matched string (0 if no match) | |||
) | |||
{ | |||
if (!lp) return true; | |||
const int ntok = lp->getnumtokens(); | |||
if (ntok<1) return true; | |||
char stack[1024]; // &1=not bit, 0x10 = ignoring subscopes, &2= state when 0x10 set | |||
int stacktop = 0; | |||
stack[0]=0; | |||
const int strlen_name = (int)strlen(name); | |||
char matched_local=-1; // -1 = first eval for scope, 0=did not pass scope, 1=OK, 2=ignore rest of scope | |||
for (int x = 0; x < ntok; x ++) | |||
{ | |||
const char *n=lp->gettoken_str(x); | |||
if (n[0] == '(' && !n[1] && !lp->gettoken_quotingchar(x)) | |||
{ | |||
if (!(matched_local&1)) | |||
{ | |||
stack[stacktop] |= matched_local | 0x10; | |||
matched_local=2; // ignore subscope | |||
} | |||
else | |||
{ | |||
matched_local = -1; // new scope | |||
} | |||
if (stacktop < (int)sizeof(stack) - 1) stack[++stacktop] = 0; | |||
} | |||
else if (stacktop && n[0] == ')' && !n[1] && !lp->gettoken_quotingchar(x)) | |||
{ | |||
if (stack[--stacktop]&0x10) | |||
{ | |||
// restore state | |||
matched_local = stack[stacktop]&2; | |||
} | |||
else | |||
{ | |||
matched_local = (matched_local != 0 ? 1 : 0) ^ (stack[stacktop]&1); | |||
} | |||
stack[stacktop] = 0; | |||
} | |||
else if (matched_local != 2 && !strcmp(n,"OR")) | |||
{ | |||
matched_local = (matched_local > 0) ? 2 : -1; | |||
stack[stacktop] = 0; | |||
} | |||
else if (matched_local&1) // matches 1, -1 | |||
{ | |||
int ln; | |||
if (!strcmp(n,"NOT")) | |||
{ | |||
stack[stacktop]^=1; | |||
} | |||
else if (!strcmp(n,"AND") && !lp->gettoken_quotingchar(x)) | |||
{ | |||
// ignore unquoted uppercase AND | |||
} | |||
else if ((ln=(int)strlen(n))>0) | |||
{ | |||
int lt=strlen_name; | |||
const char *t=name; | |||
// ^foo -- string starts (or follows \1 separator with) foo | |||
// foo$ -- string ends with foo (or is immediately followed by \1 separator) | |||
// " foo ", "foo ", " foo" include end of string/start of string has whitespace | |||
int wc_left = 0; // 1=require \1 or start of string, 2=require space or \1 or start | |||
int wc_right = 0; // 1=require \1 or \0, 2 = require space or \1 or \0 | |||
// perhaps wc_left/wc_right of 2 should also match non-alnum characters in addition to space? | |||
if (ln>1) | |||
{ | |||
switch (*n) | |||
{ | |||
case ' ': | |||
if (*++n != ' ') wc_left=2; | |||
// else { multiple characters of whitespace = literal whitespace search (two spaces requires a single space, etc) } | |||
ln--; | |||
break; | |||
case '^': | |||
ln--; | |||
n++; | |||
wc_left=1; | |||
break; | |||
} | |||
} | |||
if (ln>1) | |||
{ | |||
switch (n[ln-1]) | |||
{ | |||
case ' ': | |||
if (n[--ln - 1] != ' ') wc_right=2; | |||
// else { multiple characters of whitespace = literal whitespace search (two spaces requires a single space, etc) } | |||
break; | |||
case '$': | |||
ln--; | |||
wc_right++; | |||
break; | |||
} | |||
} | |||
bool use_cmp_func = cmp_func != NULL && !(stack[stacktop]&1); | |||
if (!wc_left && !wc_right && *n) | |||
{ | |||
switch (lp->gettoken_quotingchar(x)) | |||
{ | |||
case '\'': | |||
case '"': | |||
{ // if a quoted string has no whitespace in it, treat as whole word search | |||
const char *p = n; | |||
while (*p && *p != ' ' && *p != '\t') p++; | |||
if (!*p) | |||
{ | |||
wc_left=wc_right=2; | |||
use_cmp_func = false; | |||
} | |||
} | |||
break; | |||
} | |||
} | |||
const int min_len = use_cmp_func ? 1 : ln; | |||
if (wc_left>0) | |||
{ | |||
unsigned char lastchar = 1; | |||
while (lt>=min_len) | |||
{ | |||
int lln; | |||
if ((lastchar < 2 || (wc_left>1 && hasStrings_isNonWordChar(lastchar))) && (lln = use_cmp_func ? cmp_func(t,t-name,n,ln) : strnicmp(t,n,ln) ? 0 : ln)) | |||
{ | |||
if (wc_right == 0) break; | |||
const unsigned char nc=((const unsigned char*)t)[lln]; | |||
if (nc < 2 || (wc_right > 1 && hasStrings_isNonWordChar(nc))) break; | |||
} | |||
lastchar = *(unsigned char*)t++; | |||
lt--; | |||
} | |||
} | |||
else | |||
{ | |||
while (lt>=min_len) | |||
{ | |||
const int lln = use_cmp_func ? cmp_func(t,t-name,n,ln) : strnicmp(t,n,ln) ? 0 : ln; | |||
if (lln) | |||
{ | |||
if (wc_right == 0) break; | |||
const unsigned char nc=((const unsigned char*)t)[lln]; | |||
if (nc < 2 || (wc_right > 1 && hasStrings_isNonWordChar(nc))) break; | |||
} | |||
t++; | |||
lt--; | |||
} | |||
} | |||
matched_local = ((lt-min_len)>=0) ^ (stack[stacktop]&1); | |||
stack[stacktop]=0; | |||
} | |||
} | |||
} | |||
while (stacktop > 0) | |||
{ | |||
if (stack[--stacktop] & 0x10) matched_local=stack[stacktop]&2; | |||
else matched_local = (matched_local > 0 ? 1 : 0) ^ (stack[stacktop]&1); | |||
} | |||
return matched_local!=0; | |||
} | |||
WDL_HASSTRINGS_EXPORT bool WDL_hasStrings(const char *name, const LineParser *lp) | |||
{ | |||
return WDL_hasStringsEx(name,lp,NULL); | |||
} | |||
WDL_HASSTRINGS_EXPORT bool WDL_makeSearchFilter(const char *flt, LineParser *lp) | |||
{ | |||
if (WDL_NOT_NORMALLY(!lp)) return false; | |||
if (WDL_NOT_NORMALLY(!flt)) flt=""; | |||
#ifdef WDL_LINEPARSER_HAS_LINEPARSERINT | |||
if (lp->parse_ex(flt,true,false,true)) // allow unterminated quotes | |||
#else | |||
if (lp->parse_ex(flt,true,false)) | |||
#endif | |||
{ | |||
if (*flt) lp->set_one_token(flt); // failed parsing search string, search as a single token | |||
} | |||
return lp->getnumtokens()>0; | |||
} | |||
#endif |
@@ -0,0 +1,386 @@ | |||
/* | |||
WDL - heapbuf.h | |||
Copyright (C) 2005 and later Cockos Incorporated | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
/* | |||
This file provides the interface and implementation for WDL_HeapBuf, a simple | |||
malloc() wrapper for resizeable blocks. | |||
Also in this file is WDL_TypedBuf which is a templated version WDL_HeapBuf | |||
that manages type and type-size. | |||
*/ | |||
#ifndef _WDL_HEAPBUF_H_ | |||
#define _WDL_HEAPBUF_H_ | |||
#ifndef WDL_HEAPBUF_IMPL_ONLY | |||
#ifdef WDL_HEAPBUF_TRACE | |||
#include <windows.h> | |||
#define WDL_HEAPBUF_TRACEPARM(x) ,(x) | |||
#else | |||
#define WDL_HEAPBUF_TRACEPARM(x) | |||
#endif | |||
#include "wdltypes.h" | |||
class WDL_HeapBuf | |||
{ | |||
public: | |||
// interface | |||
#ifdef WDL_HEAPBUF_INTF_ONLY | |||
void *Resize(int newsize, bool resizedown=true); | |||
void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false); | |||
#endif | |||
void *Get() const { return m_size?m_buf:NULL; } // returns NULL if size is 0 | |||
void *GetFast() const { return m_buf; } // returns last buffer if size is 0 | |||
int GetSize() const { return m_size; } | |||
void *GetAligned(int align) const { return (void *)(((UINT_PTR)Get() + (align-1)) & ~(UINT_PTR)(align-1)); } | |||
void SetGranul(int granul) { m_granul = granul; } | |||
int GetGranul() const { return m_granul; } | |||
void *ResizeOK(int newsize, bool resizedown = true) { void *p=Resize(newsize, resizedown); return GetSize() == newsize ? p : NULL; } | |||
WDL_HeapBuf(const WDL_HeapBuf &cp) | |||
{ | |||
m_buf=0; | |||
CopyFrom(&cp,true); | |||
} | |||
WDL_HeapBuf &operator=(const WDL_HeapBuf &cp) | |||
{ | |||
CopyFrom(&cp,false); | |||
return *this; | |||
} | |||
#ifndef WDL_HEAPBUF_TRACE | |||
explicit WDL_HeapBuf(int granul=4096) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) | |||
{ | |||
} | |||
~WDL_HeapBuf() | |||
{ | |||
free(m_buf); | |||
} | |||
#else | |||
explicit WDL_HeapBuf(int granul=4096, const char *tracetype="WDL_HeapBuf" | |||
) : m_buf(NULL), m_alloc(0), m_size(0), m_granul(granul) | |||
{ | |||
m_tracetype = tracetype; | |||
char tmp[512]; | |||
wsprintf(tmp,"WDL_HeapBuf: created type: %s granul=%d\n",tracetype,granul); | |||
OutputDebugString(tmp); | |||
} | |||
~WDL_HeapBuf() | |||
{ | |||
char tmp[512]; | |||
wsprintf(tmp,"WDL_HeapBuf: destroying type: %s (alloc=%d, size=%d)\n",m_tracetype,m_alloc,m_size); | |||
OutputDebugString(tmp); | |||
free(m_buf); | |||
} | |||
#endif | |||
#endif // !WDL_HEAPBUF_IMPL_ONLY | |||
// implementation bits | |||
#ifndef WDL_HEAPBUF_INTF_ONLY | |||
#ifdef WDL_HEAPBUF_IMPL_ONLY | |||
void *WDL_HeapBuf::Resize(int newsize, bool resizedown) | |||
#else | |||
void *Resize(int newsize, bool resizedown=true) | |||
#endif | |||
{ | |||
if (newsize<0) newsize=0; | |||
#ifdef DEBUG_TIGHT_ALLOC // horribly slow, do not use for release builds | |||
if (newsize == m_size) return m_buf; | |||
int a = newsize; | |||
if (a > m_size) a=m_size; | |||
void *newbuf = newsize ? malloc(newsize) : 0; | |||
if (!newbuf && newsize) | |||
{ | |||
#ifdef WDL_HEAPBUF_ONMALLOCFAIL | |||
WDL_HEAPBUF_ONMALLOCFAIL(newsize) | |||
#endif | |||
return m_buf; | |||
} | |||
if (newbuf&&m_buf) memcpy(newbuf,m_buf,a); | |||
m_size=m_alloc=newsize; | |||
free(m_buf); | |||
return m_buf=newbuf; | |||
#endif | |||
if (newsize!=m_size || (resizedown && newsize < m_alloc/2)) | |||
{ | |||
int resizedown_under = 0; | |||
if (resizedown && newsize < m_size) | |||
{ | |||
// shrinking buffer: only shrink if allocation decreases to min(alloc/2, alloc-granul*4) or 0 | |||
resizedown_under = m_alloc - (m_granul<<2); | |||
if (resizedown_under > m_alloc/2) resizedown_under = m_alloc/2; | |||
if (resizedown_under < 1) resizedown_under=1; | |||
} | |||
if (newsize > m_alloc || newsize < resizedown_under) | |||
{ | |||
int granul=newsize/2; | |||
int newalloc; | |||
if (granul < m_granul) granul=m_granul; | |||
if (newsize<1) newalloc=0; | |||
else if (m_granul<4096) newalloc=newsize+granul; | |||
else | |||
{ | |||
granul &= ~4095; | |||
if (granul< 4096) granul=4096; | |||
else if (granul>4*1024*1024) granul=4*1024*1024; | |||
newalloc = ((newsize + granul + 96)&~4095)-96; | |||
} | |||
if (newalloc != m_alloc) | |||
{ | |||
#ifdef WDL_HEAPBUF_TRACE | |||
char tmp[512]; | |||
wsprintf(tmp,"WDL_HeapBuf: type %s realloc(%d) from %d\n",m_tracetype,newalloc,m_alloc); | |||
OutputDebugString(tmp); | |||
#endif | |||
if (newalloc <= 0) | |||
{ | |||
free(m_buf); | |||
m_buf=0; | |||
m_alloc=0; | |||
m_size=0; | |||
return 0; | |||
} | |||
void *nbuf=realloc(m_buf,newalloc); | |||
if (!nbuf) | |||
{ | |||
if (!(nbuf=malloc(newalloc))) | |||
{ | |||
#ifdef WDL_HEAPBUF_ONMALLOCFAIL | |||
WDL_HEAPBUF_ONMALLOCFAIL(newalloc); | |||
#endif | |||
return m_size?m_buf:0; // failed, do not resize | |||
} | |||
if (m_buf) | |||
{ | |||
int sz=newsize<m_size?newsize:m_size; | |||
if (sz>0) memcpy(nbuf,m_buf,sz); | |||
free(m_buf); | |||
} | |||
} | |||
m_buf=nbuf; | |||
m_alloc=newalloc; | |||
} // alloc size change | |||
} // need size up or down | |||
m_size=newsize; | |||
} // size change | |||
return m_size?m_buf:0; | |||
} | |||
#ifdef WDL_HEAPBUF_IMPL_ONLY | |||
void WDL_HeapBuf::CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig) | |||
#else | |||
void CopyFrom(const WDL_HeapBuf *hb, bool exactCopyOfConfig=false) | |||
#endif | |||
{ | |||
if (exactCopyOfConfig) // copy all settings | |||
{ | |||
free(m_buf); | |||
#ifdef WDL_HEAPBUF_TRACE | |||
m_tracetype = hb->m_tracetype; | |||
#endif | |||
m_granul = hb->m_granul; | |||
m_size=m_alloc=0; | |||
m_buf=hb->m_buf && hb->m_alloc>0 ? malloc(m_alloc = hb->m_alloc) : NULL; | |||
#ifdef WDL_HEAPBUF_ONMALLOCFAIL | |||
if (!m_buf && m_alloc) { WDL_HEAPBUF_ONMALLOCFAIL(m_alloc) } ; | |||
#endif | |||
if (m_buf) memcpy(m_buf,hb->m_buf,m_size = hb->m_size); | |||
else m_alloc=0; | |||
} | |||
else // copy just the data + size | |||
{ | |||
const int newsz=hb->GetSize(); | |||
Resize(newsz,true); | |||
if (GetSize()!=newsz) Resize(0); | |||
else memcpy(Get(),hb->Get(),newsz); | |||
} | |||
} | |||
#endif // ! WDL_HEAPBUF_INTF_ONLY | |||
#ifndef WDL_HEAPBUF_IMPL_ONLY | |||
private: | |||
void *m_buf; | |||
int m_alloc; | |||
int m_size; | |||
int m_granul; | |||
#if defined(_WIN64) || defined(__LP64__) | |||
public: | |||
int ___pad; // keep size 8 byte aligned | |||
#endif | |||
#ifdef WDL_HEAPBUF_TRACE | |||
const char *m_tracetype; | |||
#endif | |||
}; | |||
template<class PTRTYPE> class WDL_TypedBuf | |||
{ | |||
public: | |||
PTRTYPE *Get() const { return (PTRTYPE *) m_hb.Get(); } | |||
PTRTYPE *GetFast() const { return (PTRTYPE *) m_hb.GetFast(); } | |||
int GetSize() const { return m_hb.GetSize()/(unsigned int)sizeof(PTRTYPE); } | |||
PTRTYPE *Resize(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.Resize(newsize*sizeof(PTRTYPE),resizedown); } | |||
PTRTYPE *ResizeOK(int newsize, bool resizedown = true) { return (PTRTYPE *)m_hb.ResizeOK(newsize*sizeof(PTRTYPE), resizedown); } | |||
PTRTYPE *GetAligned(int align) const { return (PTRTYPE *) m_hb.GetAligned(align); } | |||
PTRTYPE *Add(PTRTYPE val) | |||
{ | |||
const int sz=GetSize(); | |||
PTRTYPE* p=ResizeOK(sz+1,false); | |||
if (p) | |||
{ | |||
p[sz]=val; | |||
return p+sz; | |||
} | |||
return NULL; | |||
} | |||
PTRTYPE *Add(const PTRTYPE *buf, int bufsz) | |||
{ | |||
if (bufsz>0) | |||
{ | |||
const int sz=GetSize(); | |||
PTRTYPE* p=ResizeOK(sz+bufsz,false); | |||
if (p) | |||
{ | |||
p+=sz; | |||
if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); | |||
else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); | |||
return p; | |||
} | |||
} | |||
return NULL; | |||
} | |||
PTRTYPE *Set(const PTRTYPE *buf, int bufsz) | |||
{ | |||
if (bufsz>=0) | |||
{ | |||
PTRTYPE* p=ResizeOK(bufsz,false); | |||
if (p) | |||
{ | |||
if (buf) memcpy(p,buf,bufsz*sizeof(PTRTYPE)); | |||
else memset((char*)p,0,bufsz*sizeof(PTRTYPE)); | |||
return p; | |||
} | |||
} | |||
return NULL; | |||
} | |||
PTRTYPE* Insert(PTRTYPE val, int idx) | |||
{ | |||
const int sz=GetSize(); | |||
if (idx >= 0 && idx <= sz) | |||
{ | |||
PTRTYPE* p=ResizeOK(sz+1,false); | |||
if (p) | |||
{ | |||
memmove(p+idx+1, p+idx, (sz-idx)*sizeof(PTRTYPE)); | |||
p[idx]=val; | |||
return p+idx; | |||
} | |||
} | |||
return NULL; | |||
} | |||
void Delete(int idx) | |||
{ | |||
PTRTYPE* p=Get(); | |||
const int sz=GetSize(); | |||
if (idx >= 0 && idx < sz) | |||
{ | |||
memmove(p+idx, p+idx+1, (sz-idx-1)*sizeof(PTRTYPE)); | |||
Resize(sz-1,false); | |||
} | |||
} | |||
void SetGranul(int gran) { m_hb.SetGranul(gran); } | |||
int Find(PTRTYPE val) const | |||
{ | |||
const PTRTYPE* p=Get(); | |||
const int sz=GetSize(); | |||
int i; | |||
for (i=0; i < sz; ++i) if (p[i] == val) return i; | |||
return -1; | |||
} | |||
#ifndef WDL_HEAPBUF_TRACE | |||
explicit WDL_TypedBuf(int granul=4096) : m_hb(granul) { } | |||
#else | |||
explicit WDL_TypedBuf(int granul=4096, const char *tracetype="WDL_TypedBuf") : m_hb(granul WDL_HEAPBUF_TRACEPARM(tracetype)) { } | |||
#endif | |||
~WDL_TypedBuf() | |||
{ | |||
} | |||
WDL_HeapBuf *GetHeapBuf() { return &m_hb; } | |||
const WDL_HeapBuf *GetHeapBuf() const { return &m_hb; } | |||
int DeleteBatch(bool (*proc)(PTRTYPE *p, void *ctx), void *ctx=NULL) // proc returns true to delete item. returns number deleted | |||
{ | |||
const int sz = GetSize(); | |||
int cnt=0; | |||
PTRTYPE *rd = Get(), *wr = rd; | |||
for (int x = 0; x < sz; x ++) | |||
{ | |||
if (!proc(rd,ctx)) | |||
{ | |||
if (rd != wr) *wr=*rd; | |||
wr++; | |||
cnt++; | |||
} | |||
rd++; | |||
} | |||
if (cnt < sz) Resize(cnt,false); | |||
return sz - cnt; | |||
} | |||
private: | |||
WDL_HeapBuf m_hb; | |||
}; | |||
#endif // ! WDL_HEAPBUF_IMPL_ONLY | |||
#endif // _WDL_HEAPBUF_H_ |
@@ -0,0 +1,639 @@ | |||
#ifndef _LICE_H | |||
#define _LICE_H | |||
/* | |||
Cockos WDL - LICE - Lightweight Image Compositing Engine | |||
Copyright (C) 2007 and later, Cockos Incorporated | |||
Portions Copyright (C) 2007 "schwa" | |||
This software is provided 'as-is', without any express or implied | |||
warranty. In no event will the authors be held liable for any damages | |||
arising from the use of this software. | |||
Permission is granted to anyone to use this software for any purpose, | |||
including commercial applications, and to alter it and redistribute it | |||
freely, subject to the following restrictions: | |||
1. The origin of this software must not be misrepresented; you must not | |||
claim that you wrote the original software. If you use this software | |||
in a product, an acknowledgment in the product documentation would be | |||
appreciated but is not required. | |||
2. Altered source versions must be plainly marked as such, and must not be | |||
misrepresented as being the original software. | |||
3. This notice may not be removed or altered from any source distribution. | |||
*/ | |||
#ifdef _WIN32 | |||
#include <windows.h> | |||
#else | |||
#include "../swell/swell-types.h" // use SWELL on other systems | |||
#endif | |||
// one of these can be defined in your project if you choose: | |||
//#define LICE_FAVOR_SPEED // optimizes some stuff that doesnt seem to benefit much (like LICE_DeltaBlit/LICE_RotatedBlit/LICE_TransformBlit) | |||
// (nothing) default probably good overall | |||
//#define LICE_FAVOR_SIZE // reduces code size of normal/scaled blit functions | |||
//#define LICE_FAVOR_SIZE_EXTREME // same as LICE_FAVOR_SIZE w/ smaller gains with bigger perf penalties (solid fills etc) | |||
#ifdef LICE_FAVOR_SPEED | |||
#ifdef LICE_FAVOR_SIZE_EXTREME | |||
#undef LICE_FAVOR_SIZE_EXTREME | |||
#endif | |||
#ifdef LICE_FAVOR_SIZE | |||
#undef LICE_FAVOR_SIZE | |||
#endif | |||
#endif | |||
#if defined(LICE_FAVOR_SIZE_EXTREME) && !defined(LICE_FAVOR_SIZE) | |||
#define LICE_FAVOR_SIZE | |||
#endif | |||
typedef unsigned int LICE_pixel; | |||
typedef unsigned char LICE_pixel_chan; | |||
#define LICE_RGBA(r,g,b,a) (((b)&0xff)|(((g)&0xff)<<8)|(((r)&0xff)<<16)|(((a)&0xff)<<24)) | |||
#define LICE_GETB(v) ((v)&0xff) | |||
#define LICE_GETG(v) (((v)>>8)&0xff) | |||
#define LICE_GETR(v) (((v)>>16)&0xff) | |||
#define LICE_GETA(v) (((v)>>24)&0xff) | |||
#if defined(__APPLE__) && defined(__ppc__) | |||
#define LICE_PIXEL_A 0 | |||
#define LICE_PIXEL_R 1 | |||
#define LICE_PIXEL_G 2 | |||
#define LICE_PIXEL_B 3 | |||
#else | |||
#define LICE_PIXEL_B 0 | |||
#define LICE_PIXEL_G 1 | |||
#define LICE_PIXEL_R 2 | |||
#define LICE_PIXEL_A 3 | |||
#endif | |||
static inline LICE_pixel LICE_RGBA_FROMNATIVE(LICE_pixel col, int alpha=0) | |||
{ | |||
return LICE_RGBA(GetRValue(col),GetGValue(col),GetBValue(col),alpha); | |||
} | |||
// bitmap interface, and some built-in types (memory bitmap and system bitmap) | |||
class LICE_IBitmap | |||
{ | |||
public: | |||
virtual ~LICE_IBitmap() { } | |||
virtual LICE_pixel *getBits()=0; | |||
virtual int getWidth()=0; | |||
virtual int getHeight()=0; | |||
virtual int getRowSpan()=0; // includes any off-bitmap data | |||
virtual bool isFlipped() { return false; } | |||
virtual bool resize(int w, int h)=0; | |||
virtual HDC getDC() { return 0; } // only sysbitmaps have to implement this | |||
virtual INT_PTR Extended(int id, void* data) { return 0; } | |||
}; | |||
#define LICE_EXT_SET_SCALING 0x2000 // data = int *, scaling is .8 fixed point. returns true if supported. affects LICE_*() draw operations | |||
#define LICE_EXT_GET_SCALING 0x2001 // data ignored, returns .8 fixed point, returns 0 if unscaled | |||
#define LICE_EXT_SET_ADVISORY_SCALING 0x2002 // data = int *, scaling is .8 fixed point. returns true if supported. does not affect draw operations | |||
#define LICE_EXT_GET_ADVISORY_SCALING 0x2003 // data ignored, returns .8 fixed point. returns 0 if unscaled | |||
#define LICE_EXT_GET_ANY_SCALING 0x2004 // data ignored, returns .8 fixed point, 0 if unscaled | |||
#define LICE_MEMBITMAP_ALIGNAMT 63 | |||
class LICE_MemBitmap : public LICE_IBitmap | |||
{ | |||
public: | |||
LICE_MemBitmap(int w=0, int h=0, unsigned int linealign=4); | |||
virtual ~LICE_MemBitmap(); | |||
// LICE_IBitmap interface | |||
virtual LICE_pixel *getBits() | |||
{ | |||
const UINT_PTR extra=LICE_MEMBITMAP_ALIGNAMT; | |||
return (LICE_pixel *) (((UINT_PTR)m_fb + extra)&~extra); | |||
} | |||
virtual int getWidth() { return m_width; } | |||
virtual int getHeight() { return m_height; } | |||
virtual int getRowSpan() { return (m_width+m_linealign)&~m_linealign; } | |||
virtual bool resize(int w, int h) { return __resize(w,h); } // returns TRUE if a resize occurred | |||
// todo: LICE_EXT_SET_SCALING ? | |||
private: | |||
bool __resize(int w, int h); | |||
LICE_pixel *m_fb; | |||
int m_width, m_height; | |||
int m_allocsize; | |||
unsigned int m_linealign; | |||
}; | |||
class LICE_SysBitmap : public LICE_IBitmap | |||
{ | |||
public: | |||
LICE_SysBitmap(int w=0, int h=0); | |||
virtual ~LICE_SysBitmap(); | |||
// LICE_IBitmap interface | |||
virtual LICE_pixel *getBits() { return m_bits; } | |||
virtual int getWidth() { return m_width; } | |||
virtual int getHeight() { return m_height; } | |||
virtual int getRowSpan() { return m_allocw; }; | |||
virtual bool resize(int w, int h) { return __resize(w,h); } // returns TRUE if a resize occurred | |||
virtual INT_PTR Extended(int id, void* data) | |||
{ | |||
switch (id) | |||
{ | |||
case LICE_EXT_SET_ADVISORY_SCALING: | |||
{ | |||
int sc = data && *(int*)data != 256 ? *(int *)data : 0; | |||
if (sc < 0) sc = 0; | |||
m_adv_scaling = sc; | |||
} | |||
return 1; | |||
case LICE_EXT_SET_SCALING: | |||
{ | |||
int sc = data && *(int*)data != 256 ? *(int *)data : 0; | |||
if (sc < 0) sc = 0; | |||
if (m_draw_scaling != sc) | |||
{ | |||
const int tmp=m_width; | |||
m_draw_scaling = sc; | |||
m_width=0; | |||
resize(tmp,m_height); | |||
} | |||
} | |||
return 1; | |||
case LICE_EXT_GET_SCALING: | |||
return m_draw_scaling; | |||
case LICE_EXT_GET_ADVISORY_SCALING: | |||
return m_adv_scaling; | |||
case LICE_EXT_GET_ANY_SCALING: | |||
if (m_draw_scaling > 0) | |||
{ | |||
if (m_adv_scaling > 0) | |||
return (m_adv_scaling * m_draw_scaling) >> 8; | |||
return m_draw_scaling; | |||
} | |||
return m_adv_scaling; | |||
} | |||
return 0; | |||
} | |||
// sysbitmap specific calls | |||
virtual HDC getDC() { return m_dc; } | |||
private: | |||
bool __resize(int w, int h); | |||
int m_width, m_height; | |||
HDC m_dc; | |||
LICE_pixel *m_bits; | |||
int m_allocw, m_alloch; | |||
#ifdef _WIN32 | |||
HBITMAP m_bitmap; | |||
HGDIOBJ m_oldbitmap; | |||
#endif | |||
int m_draw_scaling, m_adv_scaling; | |||
}; | |||
class LICE_WrapperBitmap : public LICE_IBitmap | |||
{ | |||
public: | |||
LICE_WrapperBitmap(LICE_pixel *buf, int w, int h, int span, bool flipped) | |||
{ | |||
m_buf=buf; | |||
m_w=w; | |||
m_h=h; | |||
m_span=span; | |||
m_flipped=flipped; | |||
} | |||
virtual ~LICE_WrapperBitmap() {} | |||
virtual bool resize(int w, int h) { return false; } | |||
virtual LICE_pixel *getBits() { return m_buf; } | |||
virtual int getWidth() { return m_w; } | |||
virtual int getHeight() { return m_h; } | |||
virtual int getRowSpan() { return m_span; } | |||
virtual HDC getDC() { return NULL; } | |||
virtual bool isFlipped() { return m_flipped; } | |||
LICE_pixel *m_buf; | |||
int m_w,m_h,m_span; | |||
bool m_flipped; | |||
}; | |||
class LICE_SubBitmap : public LICE_IBitmap // note: you should only keep these around as long as they are needed, and don't resize the parent while this is allocated | |||
{ | |||
public: | |||
LICE_SubBitmap(LICE_IBitmap *parent, int x, int y, int w, int h) | |||
{ | |||
m_parent=parent; | |||
if(x<0)x=0; | |||
if(y<0)y=0; | |||
m_x=x;m_y=y; | |||
__resize(w,h); | |||
} | |||
virtual ~LICE_SubBitmap() { } | |||
virtual bool resize(int w, int h) { return __resize(w,h); } | |||
bool __resize(int w, int h) | |||
{ | |||
m_w=0;m_h=0; | |||
if (m_parent && m_x >= 0 && m_y >= 0 && m_x < m_parent->getWidth() && m_y < m_parent->getHeight()) | |||
{ | |||
if (w > m_parent->getWidth()-m_x) w=m_parent->getWidth()-m_x; | |||
if (h > m_parent->getHeight()-m_y) h=m_parent->getHeight()-m_y; | |||
m_w=w; | |||
m_h=h; | |||
} | |||
return true; | |||
} | |||
virtual bool isFlipped() { return m_parent && m_parent->isFlipped(); } | |||
virtual LICE_pixel *getBits() | |||
{ | |||
if (!m_parent) return 0; | |||
int xc = m_x, yc = m_y, h = m_h; | |||
const int scale = (int)m_parent->Extended(LICE_EXT_GET_SCALING,NULL); | |||
if (scale > 0) | |||
{ | |||
xc = (xc*scale)>>8; | |||
yc = (yc*scale)>>8; | |||
h = (h*scale)>>8; | |||
} | |||
LICE_pixel* parentptr=m_parent->getBits(); | |||
if (m_parent->isFlipped()) parentptr += (m_parent->getHeight() - (yc+h))*m_parent->getRowSpan()+xc; | |||
else parentptr += yc*m_parent->getRowSpan()+xc; | |||
return parentptr; | |||
} | |||
enum { | |||
LICE_GET_SUBBITMAP_VERSION = 0x51b7000, | |||
LICE_SUBBITMAP_VERSION = 0x1000 // if we change any of this struct, then we *must* increment this version. | |||
}; | |||
virtual INT_PTR Extended(int id, void* data) | |||
{ | |||
if (id == LICE_GET_SUBBITMAP_VERSION) return LICE_SUBBITMAP_VERSION; | |||
if (!m_parent) return 0; | |||
return m_parent->Extended(id, data); | |||
} | |||
virtual int getWidth() { return m_w; } | |||
virtual int getHeight() { return m_h; } | |||
virtual int getRowSpan() { return m_parent ? m_parent->getRowSpan() : 0; } | |||
virtual HDC getDC() { return NULL; } | |||
int m_w,m_h,m_x,m_y; | |||
LICE_IBitmap *m_parent; | |||
}; | |||
// flags that most blit functions can take | |||
#define LICE_BLIT_MODE_MASK 0xff | |||
#define LICE_BLIT_MODE_COPY 0 | |||
#define LICE_BLIT_MODE_ADD 1 | |||
#define LICE_BLIT_MODE_DODGE 2 | |||
#define LICE_BLIT_MODE_MUL 3 | |||
#define LICE_BLIT_MODE_OVERLAY 4 | |||
#define LICE_BLIT_MODE_HSVADJ 5 | |||
#define LICE_BLIT_MODE_CHANCOPY 0xf0 // in this mode, only available for LICE_Blit(), the low nibble is 2 bits of source channel (low 2), 2 bits of dest channel (high 2) | |||
#define LICE_BLIT_FILTER_MASK 0xff00 | |||
#define LICE_BLIT_FILTER_NONE 0 | |||
#define LICE_BLIT_FILTER_BILINEAR 0x100 // currently pretty slow! ack | |||
#define LICE_BLIT_IGNORE_SCALING 0x20000 | |||
#define LICE_BLIT_USE_ALPHA 0x10000 // use source's alpha channel | |||
#ifndef lice_max | |||
#define lice_max(x,y) ((x)<(y)?(y):(x)) | |||
#define lice_min(x,y) ((x)<(y)?(x):(y)) | |||
#endif | |||
#ifdef _MSC_VER | |||
#include <float.h> | |||
#define lice_isfinite(x) _finite(x) | |||
#else | |||
#define lice_isfinite(x) isfinite(x) | |||
#endif | |||
// Reaper exports most LICE functions, so the function declarations below | |||
// will collide with reaper_plugin.h | |||
#ifndef LICE_PROVIDED_BY_APP | |||
// bitmap loaders | |||
// dispatch to a linked loader implementation based on file extension | |||
LICE_IBitmap* LICE_LoadImage(const char* filename, LICE_IBitmap* bmp=NULL, bool tryIgnoreExtension=false); | |||
char *LICE_GetImageExtensionList(bool wantAllSup=true, bool wantAllFiles=true); // returns doublenull terminated GetOpenFileName() style list -- free() when done. | |||
bool LICE_ImageIsSupported(const char *filename); // must be a filename that ends in .jpg, etc. if you want to check the extension, pass .ext | |||
// pass a bmp if you wish to load it into that bitmap. note that if it fails bmp will not be deleted. | |||
LICE_IBitmap *LICE_LoadPNG(const char *filename, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
LICE_IBitmap *LICE_LoadPNGFromMemory(const void *data_in, int buflen, LICE_IBitmap *bmp=NULL); | |||
LICE_IBitmap *LICE_LoadPNGFromResource(HINSTANCE hInst, const char *resid, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
#ifndef _WIN32 | |||
LICE_IBitmap *LICE_LoadPNGFromNamedResource(const char *name, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
#endif | |||
LICE_IBitmap *LICE_LoadBMP(const char *filename, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
LICE_IBitmap *LICE_LoadBMPFromResource(HINSTANCE hInst, const char *resid, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
LICE_IBitmap *LICE_LoadIcon(const char *filename, int reqiconsz=16, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
LICE_IBitmap *LICE_LoadIconFromResource(HINSTANCE hInst, const char *resid, int reqiconsz=16, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
LICE_IBitmap *LICE_LoadJPG(const char *filename, LICE_IBitmap *bmp=NULL); | |||
LICE_IBitmap *LICE_LoadJPGFromMemory(const void *data_in, int buflen, LICE_IBitmap *bmp = NULL); | |||
LICE_IBitmap* LICE_LoadJPGFromResource(HINSTANCE hInst, const char *resid, LICE_IBitmap* bmp = 0); | |||
LICE_IBitmap *LICE_LoadGIF(const char *filename, LICE_IBitmap *bmp=NULL, int *nframes=NULL); // if nframes set, will be set to number of images (stacked vertically), otherwise first frame used | |||
LICE_IBitmap *LICE_LoadPCX(const char *filename, LICE_IBitmap *bmp=NULL); // returns a bitmap (bmp if nonzero) on success | |||
// bitmap saving | |||
bool LICE_WritePNG(const char *filename, LICE_IBitmap *bmp, bool wantalpha=true); | |||
bool LICE_WriteJPG(const char *filename, LICE_IBitmap *bmp, int quality=95, bool force_baseline=true); | |||
bool LICE_WriteGIF(const char *filename, LICE_IBitmap *bmp, int transparent_alpha=0, bool dither=true); // if alpha<transparent_alpha then transparent. if transparent_alpha<0, then intra-frame checking is used | |||
// animated GIF API. use transparent_alpha=-1 to encode unchanged pixels as transparent | |||
void *LICE_WriteGIFBegin(const char *filename, LICE_IBitmap *firstframe, int transparent_alpha=0, int frame_delay=0, bool dither=true, int nreps=0); // nreps=0 for infinite | |||
void *LICE_WriteGIFBeginNoFrame(const char *filename, int w, int h, int transparent_alpha=0, bool dither=true, bool is_append=false); | |||
bool LICE_WriteGIFFrame(void *handle, LICE_IBitmap *frame, int xpos, int ypos, bool perImageColorMap=false, int frame_delay=0, int nreps=0); // nreps only used on the first frame, 0=infinite | |||
unsigned int LICE_WriteGIFGetSize(void *handle); // gets current output size | |||
bool LICE_WriteGIFEnd(void *handle); | |||
int LICE_SetGIFColorMapFromOctree(void *wr, void *octree, int numcolors); // can use after LICE_WriteGIFBeginNoFrame and before LICE_WriteGIFFrame | |||
// animated GIF reading | |||
void *LICE_GIF_LoadEx(const char *filename); | |||
void LICE_GIF_Close(void *handle); | |||
void LICE_GIF_Rewind(void *handle); | |||
unsigned int LICE_GIF_GetFilePos(void *handle); // gets current read position | |||
int LICE_GIF_UpdateFrame(void *handle, LICE_IBitmap *bm); // returns duration in msec (0 or more), or <0 if no more frames. bm will be modified/resized with new frame data | |||
// basic primitives | |||
void LICE_PutPixel(LICE_IBitmap *bm, int x, int y, LICE_pixel color, float alpha, int mode); | |||
LICE_pixel LICE_GetPixel(LICE_IBitmap *bm, int x, int y); | |||
// blit functions | |||
void LICE_Copy(LICE_IBitmap *dest, LICE_IBitmap *src); // resizes dest to fit | |||
//alpha parameter = const alpha (combined with source alpha if spcified) | |||
void LICE_Blit(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, const RECT *srcrect, float alpha, int mode); | |||
void LICE_Blit(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch, float alpha, int mode); | |||
void LICE_Blur(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int srcx, int srcy, int srcw, int srch); | |||
// dstw/dsty can be negative, srcw/srch can be as well (for flipping) | |||
void LICE_ScaledBlit(LICE_IBitmap *dest, LICE_IBitmap *src, int dstx, int dsty, int dstw, int dsth, | |||
float srcx, float srcy, float srcw, float srch, float alpha, int mode); | |||
void LICE_HalveBlitAA(LICE_IBitmap *dest, LICE_IBitmap *src); // AA's src down to dest. uses the minimum size of both (use with LICE_SubBitmap to do sections) | |||
// if cliptosourcerect is false, then areas outside the source rect can get in (otherwise they are not drawn) | |||
void LICE_RotatedBlit(LICE_IBitmap *dest, LICE_IBitmap *src, | |||
int dstx, int dsty, int dstw, int dsth, | |||
float srcx, float srcy, float srcw, float srch, | |||
float angle, | |||
bool cliptosourcerect, float alpha, int mode, | |||
float rotxcent=0.0, float rotycent=0.0); // these coordinates are offset from the center of the image, in source pixel coordinates | |||
void LICE_TransformBlit(LICE_IBitmap *dest, LICE_IBitmap *src, | |||
int dstx, int dsty, int dstw, int dsth, | |||
const float *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates | |||
float alpha, int mode); | |||
void LICE_TransformBlit2(LICE_IBitmap *dest, LICE_IBitmap *src, | |||
int dstx, int dsty, int dstw, int dsth, | |||
const double *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*2 long, and be in source image coordinates | |||
float alpha, int mode); | |||
void LICE_TransformBlit2Alpha(LICE_IBitmap *dest, LICE_IBitmap *src, | |||
int dstx, int dsty, int dstw, int dsth, | |||
const double *srcpoints, int div_w, int div_h, // srcpoints coords should be div_w*div_h*3 long, and be in source image coordinates + alpha | |||
int mode); | |||
// if cliptosourcerect is false, then areas outside the source rect can get in (otherwise they are not drawn) | |||
void LICE_DeltaBlit(LICE_IBitmap *dest, LICE_IBitmap *src, | |||
int dstx, int dsty, int dstw, int dsth, | |||
float srcx, float srcy, float srcw, float srch, | |||
double dsdx, double dtdx, double dsdy, double dtdy, | |||
double dsdxdy, double dtdxdy, | |||
bool cliptosourcerect, float alpha, int mode); | |||
void LICE_DeltaBlitAlpha(LICE_IBitmap *dest, LICE_IBitmap *src, | |||
int dstx, int dsty, int dstw, int dsth, | |||
float srcx, float srcy, float srcw, float srch, | |||
double dsdx, double dtdx, double dsdy, double dtdy, | |||
double dsdxdy, double dtdxdy, | |||
bool cliptosourcerect, float alpha, int mode, double dadx, double dady, double dadxdy); | |||
// only LICE_BLIT_MODE_ADD or LICE_BLIT_MODE_COPY are used by this, for flags | |||
// ir-ia should be 0.0..1.0 (or outside that and they'll be clamped) | |||
// drdx should be X/dstw, drdy X/dsth etc | |||
void LICE_GradRect(LICE_IBitmap *dest, int dstx, int dsty, int dstw, int dsth, | |||
float ir, float ig, float ib, float ia, | |||
float drdx, float dgdx, float dbdx, float dadx, | |||
float drdy, float dgdy, float dbdy, float dady, | |||
int mode); | |||
void LICE_FillRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha = 1.0f, int mode = 0); | |||
void LICE_ProcessRect(LICE_IBitmap *dest, int x, int y, int w, int h, void (*procFunc)(LICE_pixel *p, void *parm), void *parm); | |||
void LICE_Clear(LICE_IBitmap *dest, LICE_pixel color); | |||
void LICE_ClearRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel mask=0, LICE_pixel orbits=0); | |||
void LICE_MultiplyAddRect(LICE_IBitmap *dest, int x, int y, int w, int h, | |||
float rsc, float gsc, float bsc, float asc, // 0-1, or -100 .. +100 if you really are insane | |||
float radd, float gadd, float badd, float aadd); // 0-255 is the normal range on these.. of course its clamped | |||
void LICE_SetAlphaFromColorMask(LICE_IBitmap *dest, LICE_pixel color); | |||
// non-flood fill. simply scans up/down and left/right | |||
void LICE_SimpleFill(LICE_IBitmap *dest, int x, int y, LICE_pixel newcolor, | |||
LICE_pixel comparemask=LICE_RGBA(255,255,255,0), | |||
LICE_pixel keepmask=LICE_RGBA(0,0,0,0)); | |||
// texture generators | |||
void LICE_TexGen_Marble(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float intensity); //fills whole bitmap if rect == NULL | |||
//this function generates a Perlin noise | |||
//fills whole bitmap if rect == NULL | |||
//smooth needs to be a multiple of 2 | |||
enum | |||
{ | |||
NOISE_MODE_NORMAL = 0, | |||
NOISE_MODE_WOOD, | |||
}; | |||
void LICE_TexGen_Noise(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float intensity, int mode=NOISE_MODE_NORMAL, int smooth=1); | |||
//this function generates a Perlin noise in a circular fashion | |||
//fills whole bitmap if rect == NULL | |||
//size needs to be a multiple of 2 | |||
void LICE_TexGen_CircNoise(LICE_IBitmap *dest, const RECT *rect, float rv, float gv, float bv, float nrings, float power, int size); | |||
// bitmapped text drawing: | |||
void LICE_DrawChar(LICE_IBitmap *bm, int x, int y, char c, | |||
LICE_pixel color, float alpha, int mode); | |||
void LICE_DrawText(LICE_IBitmap *bm, int x, int y, const char *string, | |||
LICE_pixel color, float alpha, int mode); | |||
void LICE_MeasureText(const char *string, int *w, int *h); | |||
// line drawing functions | |||
void LICE_Line(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=true); | |||
void LICE_FLine(LICE_IBitmap* dest, float x1, float y1, float x2, float y2, LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=true); | |||
void LICE_ThickFLine(LICE_IBitmap* dest, double x1, double y1, double x2, double y2, LICE_pixel color, float alpha, int mode, int wid); // always AA. wid is not affected by scaling (1 is always normal line, 2 is always 2 physical pixels, etc) | |||
void LICE_DashedLine(LICE_IBitmap* dest, int x1, int y1, int x2, int y2, int pxon, int pxoff, LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=false); // straight lines only for now | |||
void LICE_FillTrapezoidF(LICE_IBitmap* dest, double fx1a, double fx1b, int y1, double fx2a, double fx2b, int y2, LICE_pixel color, float alpha, int mode); | |||
void LICE_FillTrapezoid(LICE_IBitmap* dest, int x1a, int x1b, int y1, int x2a, int x2b, int y2, LICE_pixel color, float alpha, int mode); | |||
void LICE_FillConvexPolygon(LICE_IBitmap* dest, const int* x, const int* y, int npoints, LICE_pixel color, float alpha, int mode); | |||
void LICE_FillTriangle(LICE_IBitmap *dest, int x1, int y1, int x2, int y2, int x3, int y3, LICE_pixel color, float alpha=1.0f, int mode=0); | |||
// Returns false if the line is entirely offscreen. | |||
bool LICE_ClipLine(int* pX1, int* pY1, int* pX2, int* pY2, int xLo, int yLo, int xHi, int yHi); | |||
bool LICE_ClipFLine(float* px1, float* py1, float* px2, float* py2, float xlo, float ylo, float xhi, float yhi); | |||
void LICE_Arc(LICE_IBitmap* dest, float cx, float cy, float r, float minAngle, float maxAngle, | |||
LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=true); | |||
void LICE_Circle(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=true); | |||
void LICE_FillCircle(LICE_IBitmap* dest, float cx, float cy, float r, LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=true); | |||
void LICE_RoundRect(LICE_IBitmap *drawbm, float xpos, float ypos, float w, float h, int cornerradius, | |||
LICE_pixel col, float alpha, int mode, bool aa); | |||
// useful for drawing shapes from a cache | |||
void LICE_DrawGlyph(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const LICE_pixel_chan* alphas, int glyph_w, int glyph_h, float alpha=1.0f, int mode = 0); | |||
void LICE_DrawGlyphEx(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const LICE_pixel_chan* alphas, int glyph_w, int glyph_span, int glyph_h, float alpha=1.0f, int mode = 0); | |||
void LICE_DrawMonoGlyph(LICE_IBitmap* dest, int x, int y, LICE_pixel color, const unsigned char* alphas, int glyph_w, int glyph_span, int glyph_h, float alpha=1.0f, int mode = 0); | |||
// quadratic bezier | |||
// tol means try to draw segments no longer than tol px | |||
void LICE_DrawQBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl, double yctl, double xend, double yend, | |||
LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=true, double tol=0.0); | |||
// cubic bezier | |||
// tol means try to draw segments no longer than tol px | |||
void LICE_DrawCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, | |||
double xctl2, double yctl2, double xend, double yend, LICE_pixel color, float alpha=1.0f, int mode=0, bool aa=true, double tol=0.0); | |||
void LICE_DrawThickCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, | |||
double xctl2, double yctl2, double xend, double yend, LICE_pixel color, float alpha=1.0f, int mode=0, int wid=2, double tol=0.0); | |||
// vertical fill from y=yfill | |||
void LICE_FillCBezier(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, | |||
double xctl2, double yctl2, double xend, double yend, int yfill, LICE_pixel color, float alpha=1.0f, int mode=0, double tol=0.0); | |||
// horizontal fill from x=xfill | |||
void LICE_FillCBezierX(LICE_IBitmap* dest, double xstart, double ystart, double xctl1, double yctl1, | |||
double xctl2, double yctl2, double xend, double yend, int xfill, LICE_pixel color, float alpha=1.0f, int mode=0, double tol=0.0); | |||
// convenience functions | |||
void LICE_DrawRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel color, float alpha=1.0f, int mode=0); | |||
void LICE_BorderedRect(LICE_IBitmap *dest, int x, int y, int w, int h, LICE_pixel bgcolor, LICE_pixel fgcolor, float alpha=1.0f, int mode=0); | |||
// bitmap compare-by-value function | |||
int LICE_BitmapCmp(LICE_IBitmap* a, LICE_IBitmap* b, int *coordsOut=NULL); | |||
int LICE_BitmapCmpEx(LICE_IBitmap* a, LICE_IBitmap* b, LICE_pixel mask, int *coordsOut=NULL); | |||
// colorspace functions | |||
void LICE_RGB2HSV(int r, int g, int b, int* h, int* s, int* v); // rgb, sv: [0,256), h: [0,384) | |||
void LICE_HSV2RGB(int h, int s, int v, int* r, int* g, int* b); // rgb, sv: [0,256), h: [0,384) | |||
LICE_pixel LICE_HSV2Pix(int h, int s, int v, int alpha); // sv: [0,256), h: [0,384) | |||
LICE_pixel LICE_AlterColorHSV(LICE_pixel color, float d_hue, float d_saturation, float d_value); // hue is rolled over, saturation and value are clamped, all 0..1 | |||
void LICE_AlterBitmapHSV(LICE_IBitmap* src, float d_hue, float d_saturation, float d_value); // hue is rolled over, saturation and value are clamped, all 0..1 | |||
void LICE_AlterRectHSV(LICE_IBitmap* src, int x, int y, int w, int h, float d_hue, float d_saturation, float d_value, int mode=0); // hue is rolled over, saturation and value are clamped, all 0..1. mode only used for scaling disable | |||
LICE_pixel LICE_CombinePixels(LICE_pixel dest, LICE_pixel src, float alpha, int mode); | |||
void LICE_CombinePixels2(LICE_pixel *destptr, int r, int g, int b, int a, int ia, int mode); // does not clamp | |||
void LICE_CombinePixels2Clamp(LICE_pixel *destptr, int r, int g, int b, int a, int ia, int mode); | |||
//// LVG | |||
class ProjectStateContext; | |||
void *LICE_LoadLVG(const char *filename); | |||
void *LICE_LoadLVGFromContext(ProjectStateContext *ctx, const char *nameInfo=NULL, int defw=0, int defh=0); | |||
void *LICE_GetSubLVG(void *lvg, const char *subname); | |||
LICE_IBitmap *LICE_RenderLVG(void *lvg, int reqw=0, int reqh=0, LICE_IBitmap *useBM=NULL); | |||
void LICE_DestroyLVG(void *lvg); | |||
/// palette | |||
void* LICE_CreateOctree(int maxcolors); | |||
void LICE_DestroyOctree(void* octree); | |||
void LICE_ResetOctree(void *octree, int maxcolors); // resets back to stock, but with spares (to avoid mallocs) | |||
int LICE_BuildOctree(void* octree, LICE_IBitmap* bmp); | |||
int LICE_BuildOctreeForAlpha(void* octree, LICE_IBitmap* bmp, unsigned int minalpha); | |||
int LICE_BuildOctreeForDiff(void* octree, LICE_IBitmap* bmp, LICE_IBitmap* refbmp, LICE_pixel mask=LICE_RGBA(255,255,255,0)); | |||
int LICE_FindInOctree(void* octree, LICE_pixel color); | |||
int LICE_ExtractOctreePalette(void* octree, LICE_pixel* palette); | |||
// wrapper | |||
int LICE_BuildPalette(LICE_IBitmap* bmp, LICE_pixel* palette, int maxcolors); | |||
void LICE_TestPalette(LICE_IBitmap* bmp, LICE_pixel* palette, int numcolors); | |||
struct _LICE_ImageLoader_rec | |||
{ | |||
LICE_IBitmap *(*loadfunc)(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase); | |||
const char *(*get_extlist)(); // returns GetOpenFileName sort of list "JPEG files (*.jpg)\0*.jpg\0" | |||
struct _LICE_ImageLoader_rec *_next; | |||
}; | |||
extern _LICE_ImageLoader_rec *LICE_ImageLoader_list; | |||
#endif // LICE_PROVIDED_BY_APP | |||
#ifdef __APPLE__ | |||
#define LICE_Scale_BitBlt(hdc, x,y,w,h, src, sx,sy, mode) do { \ | |||
const int _x=(x), _y=(y), _w=(w), _h=(h), _sx = (sx), _sy = (sy), _mode=(mode); \ | |||
const int rsc = (int) (src)->Extended(LICE_EXT_GET_SCALING,NULL); \ | |||
if (rsc>0) \ | |||
StretchBlt(hdc,_x,_y,_w,_h,(src)->getDC(),(_sx*rsc)/256,(_sy*rsc)/256,(_w*rsc)>>8,(_h*rsc)>>8,_mode); \ | |||
else BitBlt(hdc,_x,_y,_w,_h,(src)->getDC(),_sx,_sy,_mode); \ | |||
} while (0) | |||
#else | |||
#define LICE_Scale_BitBlt(hdc, x,y,w,h, src, sx,sy, mode) BitBlt(hdc,x,y,w,h,(src)->getDC(),sx,sy,mode) | |||
#endif | |||
#endif |