diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1d63cb8..be013f5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -210,26 +210,6 @@ jobs: path: | cjit-Darwin-arm64 - # virustotal: - # name: ๐Ÿฆ  Virus scan of released binaries - # needs: [osx-native-release, win-native-release] - # runs-on: ubuntu-latest - # if: ${{ needs.semantic-release.outputs.new_release_published == 'true' }} - # steps: - # - name: download binary artifacts - # uses: actions/download-artifact@v4 - # with: - # path: | - # cjit-bin - # - name: VirusTotal Scan - # uses: crazy-max/ghaction-virustotal@v4 - # with: - # vt_api_key: ${{ secrets.VIRUSTOTAL_API_KEY }} - # update_release: true - # files: | - # cjit-bin/release-win-native-x86_64/* - # cjit-bin/release-osx-native/* - binary-release: name: ๐Ÿ“ข Public release needs: [semantic-release, osx-native-release, musl-release, linux-release, win-native-release] @@ -243,14 +223,10 @@ jobs: with: path: | cjit-bin - - name: zip the tutorial archive + - name: pack the cjit-demo archive run: | - mkdir -p cjit-tutorial - cp -ra cjit-bin/release* cjit-tutorial/ - cp -ra examples cjit-tutorial/ - tar cvfz cjit-tutorial.tar.gz cjit-tutorial - mkdir -p cjit-bin/release-tutorial - mv cjit-tutorial.tar.gz cjit-bin/release-tutorial/ + bash build/pack-examples.sh + mv cjit-demo.tar.gz cjit-bin/release-tutorial/ - name: show release directory structure run: tree -dL 3 - name: relase all binary artifacts @@ -265,15 +241,38 @@ jobs: prerelease: false fail_on_unmatched_files: true generate_release_notes: true + # - name: VirusTotal Scan + # uses: crazy-max/ghaction-virustotal@v4 + # with: + # vt_api_key: ${{ secrets.VIRUSTOTAL_API_KEY }} + # update_release_body: true + # files: | + # cjit-bin/release-osx-native/* + # cjit-bin/release-win-native-x86_64/* + # cjit-bin/release-shared-linux-x86_64/* + # cjit-bin/release-musl-linux-x86_64/* + + virustotal: + name: ๐Ÿฆ  Virus scan of released binaries + needs: [binary-release] + runs-on: ubuntu-latest + if: ${{ needs.semantic-release.outputs.new_release_published == 'true' }} + steps: + - name: download binary artifacts + uses: actions/download-artifact@v4 + with: + path: | + cjit-bin - name: VirusTotal Scan uses: crazy-max/ghaction-virustotal@v4 with: vt_api_key: ${{ secrets.VIRUSTOTAL_API_KEY }} update_release_body: true files: | - cjit-bin/release-win-native-x86_64/* cjit-bin/release-osx-native/* - cjit-bin/release-tutorial/cjit-tutorial.tar.gz + cjit-bin/release-win-native-x86_64/* + cjit-bin/release-shared-linux-x86_64/* + cjit-bin/release-musl-linux-x86_64/* remove-tag-on-fail: name: ๐Ÿงป Remove tag on fail diff --git a/README.md b/README.md index 4812c3c..9b8c727 100644 --- a/README.md +++ b/README.md @@ -4,25 +4,32 @@ CJIT is a C interpreter based on tinyCC that compiles C code in-memory and runs More info on [Dyne.org/CJIT](https://dyne.org/cjit). -## Downloads +## ๐Ÿš€ Quick start -We provide ready to execute binary builds on [github releases](https://github.com/dyne/cjit/releases). - -Beware windows defender will warn you that there is a virus in the file. +Download the CJIT executable for your system -There isn't, this is the [0.6.2 release analysis on VirusTotal](https://www.virustotal.com/gui/file/77054b14b5960eaa655bb5c3d5f4f1ddd3ddbd9756136f029074bbef83e168fd/). +- Windows x86 64bit: [cjit.exe](https://github.com/dyne/cjit/releases/latest/download/cjit.exe) +- Apple/OSX: [cjit-Darwin-arm64](https://github.com/dyne/cjit/releases/download/v0.10.5/cjit-Darwin-arm64) +- GNU/Linux: [cjit-Linux-x86_64-static](https://github.com/dyne/cjit/releases/download/v0.10.5/cjit-Linux-x86_64-static) -## Quick start - -Just download the CJIT executable for your system and run it with c source files as well dynamic libraries as arguments: +and run it with c source files as well dynamic libraries as arguments: ``` ./cjit.exe mysource.c mylib.dll ``` -### [Read the CJIT Manual](https://dyne.org/docs/cjit) +CJIT can do a lot more! continue reading its tutorial for a hands-on introduction. + +### ๐Ÿ“– [The CJIT tutorial](https://dyne.org/docs/cjit) + +## ๐Ÿ’พ Downloads + +We provide ready to execute binary builds on [github releases](https://github.com/dyne/cjit/releases). -## Build from source +Some systems may warn you about a virus in the file. There isn't, we submit each built executable to Virustotal via [github actions](https://github.com/dyne/cjit/actions). + + +## โš™๏ธ Build from source There are various build targets, just type `make` to have a list: ``` @@ -46,7 +53,19 @@ There are various build targets, just type `make` to have a list: clean ๐Ÿงน Clean the source from all built objects ``` -## License +## ๐Ÿ”ฌ Internals + +CJIT is a bit complex inside. + +1. It relies on [tinycc](https://bellard.org/tcc/) to compile C code in-memory and run it immediately. +2. It detects automatically the system on which its running and auto-configures to support most features. +3. It embeds all C code and headers in [cjit/assets](https://github.com/dyne/cjit/tree/main/assets) making them available to all running code. +4. To embed them creates a `tar.gz` of assets at build-time and decompresses them at run-time in a temporary dir. +5. It ships a non-exclusive, opinionated selection of libraries useful to quickly script advanced applications in C. + +The [CJIT's Frequently Asked Questions](https://dyne.org/docs/cjit/faq/) page may provide more information. + +## ๐Ÿ“‘ License CJIT is copyright (C) 2024 by the Dyne.org foundation @@ -57,6 +76,6 @@ TinyCC is copyright (C) 2001-2004 by Fabrice Bellard TinyCC is distributed under the GNU Lesser General Public License For more information on licensing please refer to the Reuse report and -license texts included in LICENSES/. +license texts included in [LICENSES](https://github.com/dyne/cjit/tree/main/LICENSES). [![software by Dyne.org](https://files.dyne.org/software_by_dyne.png)](http://www.dyne.org) diff --git a/REUSE.toml b/REUSE.toml index 87174f0..7413452 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -9,6 +9,18 @@ precedence = "aggregate" SPDX-FileCopyrightText = "2024 Dyne.org foundation" SPDX-License-Identifier = "GPL-3.0-or-later" +[[annotations]] +path = ["examples/tinyc.games/**", "examples/tetris/**"] +precedence = "aggregate" +SPDX-FileCopyrightText = "Copyright (c) 2016 Jer Wilson" +SPDX-License-Identifier = "MIT" + +[[annotations]] +path = "examples/tinyc.games/res/PixelatedEleganceRegular.ttf" +precedence = "aggregate" +SPDX-FileCopyrightText = " 2024 GGBotNet" +SPDX-License-Identifier = "MIT-0" + [[annotations]] path = [".*", ".github/**"] precedence = "aggregate" @@ -28,37 +40,37 @@ SPDX-FileCopyrightText = "2001-2004 Fabrice Bellard" SPDX-License-Identifier = "LGPL-2.1-or-later" [[annotations]] -path = "lib/contrib_headers/dmon.h" +path = "assets/misc/dmon.h" precedence = "aggregate" SPDX-FileCopyrightText = " 2019-2023 Sepehr Taghdisian" SPDX-License-Identifier = "BSD-2-Clause" [[annotations]] -path = "lib/contrib_headers/miniaudio.h" +path = "assets/misc/miniaudio.h" precedence = "aggregate" SPDX-FileCopyrightText = " 2023 David Reid" SPDX-License-Identifier = "MIT-0" [[annotations]] -path = "lib/contrib_headers/nuklear.h" +path = "assets/misc/nuklear.h" precedence = "aggregate" SPDX-FileCopyrightText = "2017 Micha Mettke" SPDX-License-Identifier = "MIT-0" [[annotations]] -path = "lib/stb/**" +path = "assets/stb/**" precedence = "aggregate" SPDX-FileCopyrightText = " 2009-2021 Sean Barrett" SPDX-License-Identifier = "MIT-0" [[annotations]] -path = "lib/win32ports/**" +path = "assets/win32ports/**" precedence = "aggregate" SPDX-FileCopyrightText = " 2019 win32ports" SPDX-License-Identifier = "MIT-0" [[annotations]] -path = "lib/contrib_headers/termbox2.h" +path = "assets/misc/termbox2.h" precedence = "aggregate" SPDX-FileCopyrightText = " 2010-2020 nsf, 2015-2024 Adam Saponara" SPDX-License-Identifier = "MIT-0" diff --git a/lib/contrib_headers/dmon.h b/assets/misc/dmon.h similarity index 100% rename from lib/contrib_headers/dmon.h rename to assets/misc/dmon.h diff --git a/lib/contrib_headers/miniaudio.h b/assets/misc/miniaudio.h similarity index 100% rename from lib/contrib_headers/miniaudio.h rename to assets/misc/miniaudio.h diff --git a/lib/contrib_headers/nuklear.h b/assets/misc/nuklear.h similarity index 100% rename from lib/contrib_headers/nuklear.h rename to assets/misc/nuklear.h diff --git a/lib/contrib_headers/termbox2.h b/assets/misc/termbox2.h similarity index 100% rename from lib/contrib_headers/termbox2.h rename to assets/misc/termbox2.h diff --git a/lib/stb/stb_c_lexer.h b/assets/stb/stb_c_lexer.h similarity index 100% rename from lib/stb/stb_c_lexer.h rename to assets/stb/stb_c_lexer.h diff --git a/lib/stb/stb_connected_components.h b/assets/stb/stb_connected_components.h similarity index 100% rename from lib/stb/stb_connected_components.h rename to assets/stb/stb_connected_components.h diff --git a/lib/stb/stb_divide.h b/assets/stb/stb_divide.h similarity index 100% rename from lib/stb/stb_divide.h rename to assets/stb/stb_divide.h diff --git a/lib/stb/stb_ds.h b/assets/stb/stb_ds.h similarity index 100% rename from lib/stb/stb_ds.h rename to assets/stb/stb_ds.h diff --git a/lib/stb/stb_dxt.h b/assets/stb/stb_dxt.h similarity index 100% rename from lib/stb/stb_dxt.h rename to assets/stb/stb_dxt.h diff --git a/lib/stb/stb_easy_font.h b/assets/stb/stb_easy_font.h similarity index 100% rename from lib/stb/stb_easy_font.h rename to assets/stb/stb_easy_font.h diff --git a/lib/stb/stb_herringbone_wang_tile.h b/assets/stb/stb_herringbone_wang_tile.h similarity index 100% rename from lib/stb/stb_herringbone_wang_tile.h rename to assets/stb/stb_herringbone_wang_tile.h diff --git a/lib/stb/stb_hexwave.h b/assets/stb/stb_hexwave.h similarity index 100% rename from lib/stb/stb_hexwave.h rename to assets/stb/stb_hexwave.h diff --git a/lib/stb/stb_image.h b/assets/stb/stb_image.h similarity index 100% rename from lib/stb/stb_image.h rename to assets/stb/stb_image.h diff --git a/lib/stb/stb_image_resize2.h b/assets/stb/stb_image_resize2.h similarity index 100% rename from lib/stb/stb_image_resize2.h rename to assets/stb/stb_image_resize2.h diff --git a/lib/stb/stb_image_write.h b/assets/stb/stb_image_write.h similarity index 100% rename from lib/stb/stb_image_write.h rename to assets/stb/stb_image_write.h diff --git a/lib/stb/stb_include.h b/assets/stb/stb_include.h similarity index 100% rename from lib/stb/stb_include.h rename to assets/stb/stb_include.h diff --git a/lib/stb/stb_leakcheck.h b/assets/stb/stb_leakcheck.h similarity index 100% rename from lib/stb/stb_leakcheck.h rename to assets/stb/stb_leakcheck.h diff --git a/lib/stb/stb_perlin.h b/assets/stb/stb_perlin.h similarity index 100% rename from lib/stb/stb_perlin.h rename to assets/stb/stb_perlin.h diff --git a/lib/stb/stb_rect_pack.h b/assets/stb/stb_rect_pack.h similarity index 100% rename from lib/stb/stb_rect_pack.h rename to assets/stb/stb_rect_pack.h diff --git a/lib/stb/stb_sprintf.h b/assets/stb/stb_sprintf.h similarity index 100% rename from lib/stb/stb_sprintf.h rename to assets/stb/stb_sprintf.h diff --git a/lib/stb/stb_textedit.h b/assets/stb/stb_textedit.h similarity index 100% rename from lib/stb/stb_textedit.h rename to assets/stb/stb_textedit.h diff --git a/lib/stb/stb_tilemap_editor.h b/assets/stb/stb_tilemap_editor.h similarity index 100% rename from lib/stb/stb_tilemap_editor.h rename to assets/stb/stb_tilemap_editor.h diff --git a/lib/stb/stb_truetype.h b/assets/stb/stb_truetype.h similarity index 100% rename from lib/stb/stb_truetype.h rename to assets/stb/stb_truetype.h diff --git a/lib/stb/stb_voxel_render.h b/assets/stb/stb_voxel_render.h similarity index 100% rename from lib/stb/stb_voxel_render.h rename to assets/stb/stb_voxel_render.h diff --git a/lib/win32ports/dirent.h b/assets/win32ports/dirent.h similarity index 100% rename from lib/win32ports/dirent.h rename to assets/win32ports/dirent.h diff --git a/lib/win32ports/strings.h b/assets/win32ports/strings.h similarity index 100% rename from lib/win32ports/strings.h rename to assets/win32ports/strings.h diff --git a/lib/win32ports/sys/time.h b/assets/win32ports/sys/time.h similarity index 100% rename from lib/win32ports/sys/time.h rename to assets/win32ports/sys/time.h diff --git a/lib/win32ports/sys/wait.h b/assets/win32ports/sys/wait.h similarity index 100% rename from lib/win32ports/sys/wait.h rename to assets/win32ports/sys/wait.h diff --git a/lib/win32ports/unistd.h b/assets/win32ports/unistd.h similarity index 100% rename from lib/win32ports/unistd.h rename to assets/win32ports/unistd.h diff --git a/build/embed-path.sh b/build/embed-path.sh index 5d4b91a..ef5568a 100644 --- a/build/embed-path.sh +++ b/build/embed-path.sh @@ -32,7 +32,8 @@ rm -f ${name}.tar.gz prevpwd=`pwd` cd ${parent} [ "$pathname" != "$name" ] && cp -ra "$pathname" "$name" -tar --format ustar -cvzf ${prevpwd}/${name}.tar.gz "$name" +>&2 echo "Embed ${prevpwd}/${name}.tar.gz" +tar --format ustar -czf ${prevpwd}/${name}.tar.gz "$name" [ "$pathname" != "$name" ] && rm -rf "$name" cd - @@ -57,18 +58,19 @@ fi varname=`echo $name | sed 's/\./_/g'` # generate embeddings in source for extract_embeddings(char *tmpdir) +echo >> src/embedded.h echo "extern char *${varname};" >> src/embedded.h echo "extern unsigned int ${varname}_len;" >> src/embedded.h +echo >> src/embedded.h cat <> src/embedded.c + +// vv ${name} vv snprintf(incpath,511,"%s/%s",CJIT->tmpdir,"${name}"); -if(fresh) { -res = muntargz_to_path(CJIT->tmpdir,(char*)&${varname},${varname}_len); -} -if(res!=0) { // muntar returns 0 on success -_err("Error extracting %s",incpath); -return(false); -} +if(fresh) res = muntargz_to_path(CJIT->tmpdir,(char*)&${varname},${varname}_len); +if(res!=0) { _err("Error extracting %s",incpath); return(false); } tcc_add_include_path(CJIT->TCC, incpath); +// ^^ ${name} ^^ + EOF exit 0 diff --git a/build/linux.mk b/build/linux.mk index fc161f9..0b5f0d6 100644 --- a/build/linux.mk +++ b/build/linux.mk @@ -10,7 +10,7 @@ SOURCES += \ src/kilo.o \ src/embed_libtcc1.a.o \ src/embed_include.o \ - src/embed_contrib_headers.o \ + src/embed_misc.o \ src/embed_stb.o all: embed cjit @@ -20,8 +20,8 @@ embed: lib/tinycc/libtcc1.a bash build/init-embeddings.sh bash build/embed-path.sh lib/tinycc/libtcc1.a bash build/embed-path.sh lib/tinycc/include - bash build/embed-path.sh lib/contrib_headers - bash build/embed-path.sh lib/stb + bash build/embed-path.sh assets/misc + bash build/embed-path.sh assets/stb @echo >> src/embedded.c @echo "return(true);" >> src/embedded.c @echo "}" >> src/embedded.c @@ -34,11 +34,14 @@ tinycc_config += --with-selinux endif ifdef ASAN - cflags := -Og -ggdb -DDEBUG=1 -fno-omit-frame-pointer -fsanitize=address + ASAN_FLAGS := -fsanitize=address -fsanitize=leak + ASAN_FLAGS += -fsanitize=float-divide-by-zero + ASAN_FLAGS += -fsanitize=float-cast-overflow + cflags := -g -DDEBUG=1 -Wall -fno-omit-frame-pointer + cflags += ${ASAN_FLAGS} -DMEM_DEBUG cflags += ${cflags_includes} ${cflags_gnu} -DKILO_SUPPORTED cflags += -DCJIT_BUILD_LINUX - ldflags := -fsanitize=address -static-libasan -# tinycc_config += --extra-ldflags="${ldflags}" + ldflags := ${ASAN_FLAGS} -static-libasan endif ifdef GDB diff --git a/build/musl.mk b/build/musl.mk index b07fc5a..146dc91 100644 --- a/build/musl.mk +++ b/build/musl.mk @@ -14,7 +14,7 @@ SOURCES += \ src/kilo.o \ src/embed_libtcc1.a.o \ src/embed_include.o \ - src/embed_contrib_headers.o \ + src/embed_misc.o \ src/embed_libc.so.o \ src/musl-symbols.o \ src/embed_stb.o @@ -31,9 +31,9 @@ embed: lib/tinycc/libtcc1.a bash build/init-embeddings.sh bash build/embed-path.sh lib/tinycc/libtcc1.a bash build/embed-path.sh lib/tinycc/include - bash build/embed-path.sh lib/contrib_headers + bash build/embed-path.sh assets/misc bash build/embed-path.sh /lib/x86_64-linux-musl/libc.so - bash build/embed-path.sh lib/stb + bash build/embed-path.sh assets/stb @echo >> src/embedded.c @echo "return(true);" >> src/embedded.c @echo "}" >> src/embedded.c diff --git a/build/osx.mk b/build/osx.mk index 4f43830..e481cf9 100644 --- a/build/osx.mk +++ b/build/osx.mk @@ -7,7 +7,7 @@ SOURCES += \ src/kilo.o \ src/embed_libtcc1.a.o \ src/embed_include.o \ - src/embed_contrib_headers.o \ + src/embed_misc.o \ src/embed_stb.o all: embed cjit.command @@ -17,8 +17,8 @@ embed: lib/tinycc/libtcc1.a bash build/init-embeddings.sh bash build/embed-path.sh lib/tinycc/libtcc1.a bash build/embed-path.sh lib/tinycc/include - bash build/embed-path.sh lib/contrib_headers - bash build/embed-path.sh lib/stb + bash build/embed-path.sh assets/misc + bash build/embed-path.sh assets/stb @echo >> src/embedded.c @echo "return(true);" >> src/embedded.c @echo "}" >> src/embedded.c diff --git a/build/pack-examples.sh b/build/pack-examples.sh new file mode 100644 index 0000000..ad726b6 --- /dev/null +++ b/build/pack-examples.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -e +set -x + +# this script is designed to run on GNU/Linux +# it will generate a cjit-demo archive usable on all platforms + +rm -rf cjit-demo +mkdir -p cjit-demo +cp -ra examples cjit-demo/ + +mkdir -p cjit-demo/SDL2 + +# SDL2 +ver=2.30.10 +url="https://github.com/libsdl-org/SDL2/releases/download/release-${ver}" +[ -r SDL2-${ver}-win32-x64.zip ] || + wget ${url}/SDL2-${ver}-win32-x64.zip +[ -r cjit-demo/SDL2.dll ] || + unzip -q -d cjit-demo SDL2-${ver}-win32-x64.zip SDL2.dll +[ -r SDL2-${ver}.zip ] || + wget ${url}/SDL2-${ver}.zip +[ -r cjit-demo/SDL2/SDL.h ] || { + unzip -q -d /tmp SDL2-${ver}.zip SDL2-${ver}/include/* + mv /tmp/SDL2-${ver}/include cjit-demo/SDL2 + rm -rf /tmp/SDL2-${ver} +} + +# SDL2_image +ver=2.8.3 +url="https://github.com/libsdl-org/SDL_image/releases/download/release-${ver}" +[ -r SDL2_image-${ver}-win32-x64.zip ] || + wget ${url}/SDL2_image-${ver}-win32-x64.zip +[ -r cjit-demo/SDL2_image.dll ] || + unzip -q -d cjit-demo SDL2_image-${ver}-win32-x64.zip SDL2_image.dll +[ -r SDL2_image-${ver}.zip ] || + wget ${url}/SDL2_image-${ver}.zip +[ -r cjit-demo/SDL2/SDL_image.h ] || { + unzip -q -d /tmp SDL2_image-${ver}.zip SDL2_image-${ver}/include/SDL_image.h + mv /tmp/SDL2_image-${ver}/include/SDL_image.h cjit-demo/SDL2/ + rm -rf /tmp/SDL2_image-${ver} +} + +# SDL2_ttf +ver=2.22.0 +url="https://github.com/libsdl-org/SDL_ttf/releases/download/release-${ver}" +[ -r SDL2_ttf-${ver}-win32-x64.zip ] || + wget ${url}/SDL2_ttf-${ver}-win32-x64.zip +[ -r cjit-demo/SDL2_ttf.dll ] || + unzip -q -d cjit-demo SDL2_ttf-${ver}-win32-x64.zip SDL2_ttf.dll +[ -r SDL2_ttf-${ver}.zip ] || + wget ${url}/SDL2_ttf-${ver}.zip +[ -r cjit-demo/SDL2/SDL_ttf.h ] || { + unzip -q -d /tmp SDL2_ttf-${ver}.zip SDL2_ttf-${ver}/SDL_ttf.h + mv /tmp/SDL2_ttf-${ver}/SDL_ttf.h cjit-demo/SDL2/ + rm -rf /tmp/SDL2_ttf-${ver} +} + + +[ -r ${glew} ] || { + wget "https://github.com/nigels-com/glew/releases/download/glew-2.2.0/glew-2.2.0-win32.zip" +} +[ -r cjit-demo/glew32.dll ] || { + unzip -q -d /tmp glew-2.2.0-win32.zip + mv /tmp/glew-2.2.0/bin/Release/x64/glew32.dll cjit-demo/ + mv /tmp/glew-2.2.0/include/GL cjit-demo/ + rm -rf /tmp/glew-2.2.0 +} + +tar --format ustar -czf cjit-demo.tar.gz cjit-demo +rm -rf cjit-demo diff --git a/build/win-native.mk b/build/win-native.mk index f409d6e..efcce31 100755 --- a/build/win-native.mk +++ b/build/win-native.mk @@ -25,7 +25,7 @@ SOURCES += src/win-compat.o \ src/embed_include.o \ src/embed_tinycc_win32.o \ src/embed_win32ports.o \ - src/embed_contrib_headers.o \ + src/embed_misc.o \ src/embed_stb.o all: embed cjit.exe @@ -36,9 +36,9 @@ embed: lib/tinycc/libtcc1.a bash build/embed-path.sh lib/tinycc/libtcc1.a bash build/embed-path.sh lib/tinycc/include bash build/embed-path.sh lib/tinycc/win32/include tinycc_win32 - bash build/embed-path.sh lib/win32ports - bash build/embed-path.sh lib/contrib_headers - bash build/embed-path.sh lib/stb + bash build/embed-path.sh assets/win32ports + bash build/embed-path.sh assets/misc + bash build/embed-path.sh assets/stb @echo >> src/embedded.c @echo "return(true);" >> src/embedded.c @echo "}" >> src/embedded.c diff --git a/build/win-wsl.mk b/build/win-wsl.mk index 22bf2ef..3f4452d 100644 --- a/build/win-wsl.mk +++ b/build/win-wsl.mk @@ -23,7 +23,7 @@ SOURCES += src/win-compat.o \ src/embed_include.o \ src/embed_tinycc_win32.o \ src/embed_win32ports.o \ - src/embed_contrib_headers.o \ + src/embed_misc.o \ src/embed_stb.o all: deps embed cjit.exe @@ -34,9 +34,9 @@ embed: lib/tinycc/libtcc1.a bash build/embed-path.sh lib/tinycc/libtcc1.a bash build/embed-path.sh lib/tinycc/include bash build/embed-path.sh lib/tinycc/win32/include tinycc_win32 - bash build/embed-path.sh lib/win32ports - bash build/embed-path.sh lib/contrib_headers - bash build/embed-path.sh lib/stb + bash build/embed-path.sh assets/win32ports + bash build/embed-path.sh assets/misc + bash build/embed-path.sh assets/stb @echo >> src/embedded.c @echo "return(true);" >> src/embedded.c @echo "}" >> src/embedded.c diff --git a/examples/flappy.c b/examples/flappy.c new file mode 100644 index 0000000..70b4c10 --- /dev/null +++ b/examples/flappy.c @@ -0,0 +1,211 @@ +// Flappy -- http://tinyc.games -- (c) 2020 Jer Wilson +// +// Flappy is an extremely small implementation of the Flappy Bird game. + +#include +#include +#include + +#include +#include +#include + +#define W 480 +#define H 600 +#define GROUND 80 +#define PIPE_W 86 +#define PHYS_W (W + PIPE_W + 80) +#define GAP 220 +#define GRACE 4 +#define RANDOM_PIPE_HEIGHT (rand() % (H - GROUND - GAP - 120) + 60) +#define PLYR_X 80 +#define PLYR_SZ 60 + +enum gamestates {READY, ALIVE, GAMEOVER} gamestate = READY; + +float player_y = (H - GROUND)/2; +float player_vel; +int pipe_x[2] = {W, W}; +float pipe_y[2]; +int score; +int best; +int idle_time = 30; +float frame = 0; + +SDL_Event event; +SDL_Renderer *renderer; +SDL_Surface *surf; +SDL_Texture *pillar; +SDL_Texture *background; +SDL_Texture *bird[4]; +TTF_Font *font; + +void setup(); +void new_game(); +void update_stuff(); +void update_pipe(int i); +void draw_stuff(); +void text(char *fstr, int value, int height); + +//the entry point and main game loop +int main() +{ + setup(); + + for(;;) + { + while(SDL_PollEvent(&event)) switch(event.type) + { + case SDL_QUIT: + exit(0); + case SDL_KEYDOWN: + case SDL_MOUSEBUTTONDOWN: + if(gamestate == ALIVE) + { + player_vel = -11.7f; + frame += 1.0f; + } + else if(idle_time > 30) + { + new_game(); + } + } + + update_stuff(); + draw_stuff(); + SDL_Delay(1000 / 60); + idle_time++; + } +} + +//initial setup to get the window and rendering going +void setup() +{ + srand(time(NULL)); + + SDL_Init(SDL_INIT_VIDEO); + SDL_Window *win = SDL_CreateWindow("Flappy", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, W, H, SDL_WINDOW_SHOWN); + renderer = SDL_CreateRenderer(win, -1, SDL_RENDERER_PRESENTVSYNC); + if(!renderer) exit(fprintf(stderr, "Could not create SDL renderer\n")); + IMG_Init(IMG_INIT_PNG); + pillar = IMG_LoadTexture(renderer,"examples/tinyc.games/flappy/pillar.png"); + SDL_SetColorKey(surf, 1, 0xffff00); + background = IMG_LoadTexture(renderer,"examples/tinyc.games/flappy/background.png"); + + for(int i = 0; i < 4; i++) + { + char file[80]; + sprintf(file, "examples/tinyc.games/flappy/bird-%d.png", i); + // surf = SDL_LoadBMP(file); + // SDL_SetColorKey(surf, 1, 0xffff00); + // bird[i] = SDL_CreateTextureFromSurface(renderer, surf); + bird[i] = IMG_LoadTexture(renderer,file); + } + + TTF_Init(); + font = TTF_OpenFont("examples/tinyc.games/PixelatedEleganceRegular.ttf", 42); +} + +//start a new game +void new_game() +{ + gamestate = ALIVE; + player_y = (H - GROUND)/2; + player_vel = -11.7f; + score = 0; + pipe_x[0] = PHYS_W + PHYS_W/2 - PIPE_W; + pipe_x[1] = PHYS_W - PIPE_W; + pipe_y[0] = RANDOM_PIPE_HEIGHT; + pipe_y[1] = RANDOM_PIPE_HEIGHT; +} + +//when we hit something +void game_over() +{ + gamestate = GAMEOVER; + idle_time = 0; + if(best < score) best = score; +} + +//update everything that needs to update on its own, without input +void update_stuff() +{ + if(gamestate != ALIVE) return; + + player_y += player_vel; + player_vel += 0.61; // gravity + + if(player_vel > 10.0f) + frame = 0; + else + frame -= (player_vel - 10.0f) * 0.03f; //fancy animation + + if(player_y > H - GROUND - PLYR_SZ) + game_over(); + + for(int i = 0; i < 2; i++) + update_pipe(i); +} + +//update one pipe for one frame +void update_pipe(int i) +{ + if(PLYR_X + PLYR_SZ >= pipe_x[i] + GRACE && PLYR_X <= pipe_x[i] + PIPE_W - GRACE && + (player_y <= pipe_y[i] - GRACE || player_y + PLYR_SZ >= pipe_y[i] + GAP + GRACE)) + game_over(); // player hit pipe + + // move pipes and increment score if we just passed one + pipe_x[i] -= 5; + if(pipe_x[i] + PIPE_W < PLYR_X && pipe_x[i] + PIPE_W > PLYR_X - 5) + score++; + + // respawn pipe once far enough off screen + if(pipe_x[i] <= -PIPE_W) + { + pipe_x[i] = PHYS_W - PIPE_W; + pipe_y[i] = RANDOM_PIPE_HEIGHT; + } +} + +//draw everything in the game on the screen +void draw_stuff() +{ + SDL_Rect dest = {0, 0, W, H}; + SDL_RenderCopy(renderer, background, NULL, &dest); + + //draw pipes + for(int i = 0; i < 2; i++) + { + int lower = pipe_y[i] + GAP; + SDL_RenderCopy(renderer, pillar, NULL, &(SDL_Rect){pipe_x[i], pipe_y[i] - H, PIPE_W, H}); + SDL_Rect src = {0, 0, 86, H - lower - GROUND}; + SDL_RenderCopy(renderer, pillar, &src, &(SDL_Rect){pipe_x[i], lower, PIPE_W, src.h}); + } + + //draw player + SDL_RenderCopy(renderer, bird[(int)frame % 4], NULL, + &(SDL_Rect){PLYR_X, player_y, PLYR_SZ, PLYR_SZ}); + + if(gamestate != READY) text("%d", score, 10); + if(gamestate == READY) text("Press any key", 0, 150); + if(gamestate == GAMEOVER) text("High score: %d", best, 150); + + SDL_RenderPresent(renderer); +} + +void text(char *fstr, int value, int height) +{ + if(!font) return; + int w, h; + char msg[80]; + snprintf(msg, 80, fstr, value); + TTF_SizeText(font, msg, &w, &h); + SDL_Surface *msgsurf = TTF_RenderText_Blended(font, msg, (SDL_Color){255, 255, 255}); + SDL_Texture *msgtex = SDL_CreateTextureFromSurface(renderer, msgsurf); + SDL_Rect fromrec = {0, 0, msgsurf->w, msgsurf->h}; + SDL_Rect torec = {(W - w)/2, height, msgsurf->w, msgsurf->h}; + SDL_RenderCopy(renderer, msgtex, &fromrec, &torec); + SDL_DestroyTexture(msgtex); + SDL_FreeSurface(msgsurf); +} diff --git a/examples/tetris/graphics.c b/examples/tetris/graphics.c new file mode 100644 index 0000000..8ef84c6 --- /dev/null +++ b/examples/tetris/graphics.c @@ -0,0 +1,354 @@ +#include "tet.h" +#include "utils.c" +#include "font.c" + +#define VBUFLEN 40000 + +unsigned main_prog_id; +GLuint main_vao; +GLuint main_vbo; +float vbuf[VBUFLEN]; +int vbuf_n; +float color_r, color_g, color_b; + +// render a line of text optionally with a %d value in it +void text(char *fstr, int value) +{ + if (!fstr) return; + char str[100]; + snprintf(str, 99, fstr, value); + font_begin(win_x, win_y); + font_add_text(str, text_x, text_y, 3 * bs / 4); + font_end(1, 1, 1); + text_y += bs * 125 / 100 + (fstr[strlen(fstr) - 1] == ' ' ? bs : 0); +} + +void draw_setup() +{ + fprintf(stderr, "GLSL version on this system is %s\n", (char *)glGetString(GL_SHADING_LANGUAGE_VERSION)); + unsigned int vertex = file2shader(GL_VERTEX_SHADER, "examples/tetris/shaders/main.vert"); + unsigned int fragment = file2shader(GL_FRAGMENT_SHADER, "examples/tetris/shaders/main.frag"); + main_prog_id = glCreateProgram(); + glAttachShader(main_prog_id, vertex); + glAttachShader(main_prog_id, fragment); + glLinkProgram(main_prog_id); + check_program_errors(main_prog_id, "main"); + glDeleteShader(vertex); + glDeleteShader(fragment); + glGenVertexArrays(1, &main_vao); + glGenBuffers(1, &main_vbo); +} + +void vertex(float x, float y, float r, float g, float b) +{ + if (vbuf_n >= VBUFLEN - 5) return; + vbuf[vbuf_n++] = x; + vbuf[vbuf_n++] = y; + vbuf[vbuf_n++] = r; + vbuf[vbuf_n++] = g; + vbuf[vbuf_n++] = b; +} + +void rect(float x, float y, float w, float h) +{ + vertex(x , y , color_r, color_g, color_b); + vertex(x + w, y , color_r, color_g, color_b); + vertex(x , y + h, color_r, color_g, color_b); + vertex(x , y + h, color_r, color_g, color_b); + vertex(x + w, y , color_r, color_g, color_b); + vertex(x + w, y + h, color_r, color_g, color_b); +} + +void draw_start() +{ + glViewport(0, 0, win_x, win_y); + glClear(GL_COLOR_BUFFER_BIT); +} + +void draw_end() +{ + glUseProgram(main_prog_id); + + float near = -1.f; + float far = 1.f; + float x = 1.f / (win_x / 2.f); + float y = -1.f / (win_y / 2.f); + float z = -1.f / ((far - near) / 2.f); + float tz = -(far + near) / (far - near); + float ortho[] = { + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + -1, 1, tz, 1, + }; + glUniformMatrix4fv(glGetUniformLocation(main_prog_id, "proj"), 1, GL_FALSE, ortho); + + glBindVertexArray(main_vao); + glBindBuffer(GL_ARRAY_BUFFER, main_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof vbuf, vbuf, GL_STATIC_DRAW); + + // show GL where the position data is + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), 0); + glEnableVertexAttribArray(0); + + // show GL where the color data is + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void *)(2 * sizeof(float))); + glEnableVertexAttribArray(1); + + glDrawArrays(GL_TRIANGLES, 0, vbuf_n / 5); + if (vbuf_n > VBUFLEN * 3 / 4) + fprintf(stderr, "vbuf fullness (%d/%d)\n", vbuf_n, VBUFLEN); + vbuf_n = 0; +} + +// set the current draw color to the color assoc. with a shape +void set_color_from_shape(int shape, int shade) +{ + color_r = MAX((colors[shape] >> 16 & 0xFF) + shade, 0) / 255.f; + color_g = MAX((colors[shape] >> 8 & 0xFF) + shade, 0) / 255.f; + color_b = MAX((colors[shape] >> 0 & 0xFF) + shade, 0) / 255.f; +} + +void set_color(int r, int g, int b) +{ + color_r = r / 255.f; + color_g = g / 255.f; + color_b = b / 255.f; +} + +void draw_menu() +{ + if (state != MAIN_MENU && state != NUMBER_MENU) return; + + menu_pos = MAX(menu_pos, 0); + menu_pos = MIN(menu_pos, state == NUMBER_MENU ? 3 : 2); + p = play; // just grab first player :) + + set_color(0, 0, 0); + rect(p->held.x, + p->held.y + p->box_w + bs2 + line_height * (menu_pos + 1), + p->board_w, + line_height); + draw_end(); + + text_x = p->held.x; + text_y = p->held.y + p->box_w + bs2; + if (state == MAIN_MENU) + { + text("Main Menu" , 0); + text("Endless" , 0); + text("Garbage Race" , 0); + text("Quit" , 0); + } + else if (state == NUMBER_MENU) + { + text("How many players?", 0); + text("1" , 0); + text("2" , 0); + text("3" , 0); + text("4" , 0); + } +} + +void draw_particles() +{ + set_color(254, 254, 254); + for (int i = 0; i < NPARTS; i++) + { + if (parts[i].r <= 0.5f) + continue; + rect(parts[i].x, parts[i].y, parts[i].r, parts[i].r); + } +} + +// draw a single mino (square) of a shape +void draw_mino(int x, int y, int shape, int outline, int part) +{ + if (!part) return; + int bw = MAX(1, outline ? bs / 10 : bs / 6); + set_color_from_shape(shape, -50); + rect(x, y, bs, bs); + set_color_from_shape(shape, outline ? -255 : 0); + rect( // horizontal band + x + (part & 8 ? 0 : bw), + y + bw, + bs - (part & 8 ? 0 : bw) - (part & 2 ? 0 : bw), + bs - bw - bw); + rect( // vertical band + x + (part & 32 ? 0 : bw), + y + (part & 1 ? 0 : bw), + bs - (part & 32 ? 0 : bw) - (part & 16 ? 0 : bw), + bs - (part & 1 ? 0 : bw) - (part & 4 ? 0 : bw)); +} + +#define CENTER 1 +#define OUTLINE 2 + +void draw_shape(int x, int y, int color, int rot, int flags) +{ + for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) + draw_mino( + x + bs * i + ((flags & CENTER) ? bs2 + bs2 * center[2 * color ] : 0), + y + bs * j + ((flags & CENTER) ? bs + bs2 * center[2 * color + 1] : 0), + color, + flags & OUTLINE, + is_solid_part(color, rot, i, j) + ); +} + +// draw everything in the game on the screen for current player +void draw_player() +{ + if (state == MAIN_MENU || state == NUMBER_MENU) return; + + int x = p->board_x + bs * p->shake_x; + int y = p->board_y + bs * p->shake_y; + + // draw background, black boxes + set_color(16, 26, 24); + rect(p->held.x, p->held.y, p->box_w, p->box_w); + rect(x, y, p->board_w, bs * VHEIGHT); + + // find ghost piece position + int ghost_y = p->it.y; + while (ghost_y < BHEIGHT && !collide(p->it.x, ghost_y + 1, p->it.rot)) + ghost_y++; + + // draw shadow + if (p->it.color) + { + struct shadow shadow = shadows[p->it.rot][p->it.color]; + int top = MAX(0, p->it.y + shadow.y - 5); + set_color(8, 13, 12); + rect(x + bs * (p->it.x + shadow.x), + y + bs * top, + bs * shadow.w, + MAX(0, bs * (ghost_y - top + shadow.y - 5))); + } + + // draw hard drop beam + float loss = .1f * (tick - p->beam_tick); + if (loss < 1.f && p->beam.color) + { + struct shadow shadow = shadows[p->beam.rot][p->beam.color]; + int rw = bs * shadow.w; + int rh = bs * (p->beam.y + shadow.y - 5); + int lossw = (1.f - ((1.f - loss) * (1.f - loss))) * rw; + int lossh = loss < .5f ? 0.f : (1.f - ((1.f - loss) * (1.f - loss))) * rh; + set_color(66, 74, 86); + rect(x + bs * (p->beam.x + shadow.x) + lossw / 2, + y + lossh, + rw - lossw, + rh - lossh); + } + + // draw pieces on board + for (int i = 0; i < BWIDTH; i++) for (int j = 0; j < BHEIGHT; j++) + draw_mino(x + bs * i, y + bs * (j-5) - p->row[j].offset, + p->row[j].col[i].color, 0, p->row[j].col[i].part); + + // draw queued garbage + p->top_garb = y + bs * VHEIGHT; + int garb_height = 0; + for (int i = 0; i < GARB_LVLS; i++) + for (int j = 0; j < p->garbage[i]; j++) + { + draw_mino(x - 3 * bs2, (p->top_garb -= bs), (3 - i) + 9, 0, '@'); + garb_height++; + } + if (p->flash > 8) + { + int flash_sq = p->flash * p->flash; + set_color(flash_sq, flash_sq, flash_sq); + rect(x - 3 * bs2 - bs4, p->top_garb - bs4, bs + bs4 + bs4, garb_height * bs + bs2); + } + p->top_garb = y + bs * VHEIGHT; + for (int i = 0; i < GARB_LVLS; i++) + for (int j = 0; j < p->garbage[i]; j++) + draw_mino(x - 3 * bs2, (p->top_garb -= bs), (3 - i) + 9, 0, '@'); + + // draw falling piece & ghost + draw_shape(x + bs * p->it.x, y + bs * (ghost_y - 5), p->it.color, p->it.rot, OUTLINE); + draw_shape(x + bs * p->it.x, y + bs * (p->it.y - 5), p->it.color, p->it.rot, 0); + + // draw next pieces + for (int n = 0; n < 5; n++) + draw_shape(p->preview_x, p->preview_y + 3 * bs * n, p->next[n], 0, CENTER); + + // draw held piece + draw_shape(p->held.x, p->held.y, p->held.color, 0, CENTER); + + draw_end(); + + // draw scores etc + text_x = p->held.x; + text_y = p->held.y + p->box_w + bs2; + text("%d pts ", p->score); + text("%d lines ", p->lines); + + int secs = p->ticks / 120 % 60; + int mins = p->ticks / 120 / 60 % 60; + char minsec[80]; + sprintf(minsec, "%d:%02d.%02d ", mins, secs, p->ticks % 120 * 1000 / 1200); + text(minsec, 0); + text(p->dev_name, 0); + if (p->combo > 1) text("%d combo ", p->combo); + if (p->tspin == TSPIN_FULL) + text("T-SPIN", 0); + else if (p->tspin == TSPIN_MINI) + text("T-SPIN MINI", 0); + + if (p->reward) + { + text_x = p->reward_x - bs; + text_y = p->reward_y--; + text("%d", p->reward); + } + + text_x = x + bs2; + text_y = y + bs2 * 19; + if (p->countdown_time > 0) + text(countdown_msg[p->countdown_time / CTDN_TICKS], 0); + + if (state == ASSIGN) + text(p >= play + assign_me ? "Press button to join" : p->dev_name, 0); + + if (state == GAMEOVER) text("Game over", 0); +} + +void reflow() +{ + float strength = 0.0005f * (1 + rand() % 10); + for (int n = 0; n < NFLOWS; n++) + { + flows[n].x = rand() % win_x; + flows[n].y = rand() % win_y; + flows[n].r = rand() % 100 + 100; + flows[n].vx = (rand() % 10 - 5) * strength; + flows[n].vy = (rand() % 10 - 5) * strength; + } +} + +// recalculate sizes and positions on resize +void resize(int x, int y) +{ + win_x = x; + win_y = y; + bs = MIN(win_x / (nplay * 22), win_y / 24); + bs2 = bs / 2; + bs4 = bs / 4; + line_height = bs * 125 / 100; + int n = 0; + for (p = play; p < play + nplay; p++, n++) + { + p->board_x = (x / (nplay * 2)) * (2 * n + 1) - bs2 * BWIDTH; + p->board_y = (y / 2) - bs2 * VHEIGHT; + p->board_w = bs * 10; + p->box_w = bs * 5; + p->held.x = p->board_x - p->box_w - bs2; + p->held.y = p->board_y; + p->preview_x = p->board_x + p->board_w + bs2; + p->preview_y = p->board_y; + } + reflow(); +} diff --git a/examples/tetris/input.c b/examples/tetris/input.c new file mode 100644 index 0000000..e443fae --- /dev/null +++ b/examples/tetris/input.c @@ -0,0 +1,249 @@ +#include "tet.h" +#include + +#define WASD -1 +#define ARROW_KEYS -2 + +void resize(int, int); + +void down() +{ + p->down = 1; + p->move_cooldown = 0; +} + +void left() +{ + p->left = 1; + p->move_cooldown = 0; +} + +void right() +{ + p->right = 1; + p->move_cooldown = 0; +} + +// spin the falling piece left or right, if possible +void spin(int dir) +{ + int new_rot = (p->it.rot + dir) % 4; + int k = new_rot * 20 + (dir == 1 ? 0 : 10) + (p->it.color == 4 ? 80 : 0); + + for (int i = 0; i < 5; i++) + { + int kx = kicks[k++]; + int ky = kicks[k++]; + if (!collide(p->it.x + kx, p->it.y + ky, new_rot)) + { + p->it.rot = new_rot; + p->it.x += kx; + p->it.y += ky; + if (p->grounded && p->grounded_moves < 15) + { + p->idle_time = 0; + p->grounded_moves++; + } + audio_tone(TRIANGLE, C5, B5, 1, 1, 1, 1); + return; + } + } + audio_tone(SQUARE, C5, B5, 1, 1, 1, 1); +} + +// move the falling piece as far down as it will go +void hard() +{ + for (; !collide(p->it.x, p->it.y + 1, p->it.rot); p->it.y++) + p->score++; + p->idle_time = 100; + p->beam = p->it; + p->beam_tick = tick; + p->shake_y += .25f; + audio_tone(TRIANGLE, A1, E3, 5, 5, 5, 90); +} + +// hold a piece for later +void hold() +{ + if (p->hold_uses++) return; + SWAP(p->held.color, p->it.color); + reset_fall(); +} + +void joy_setup() +{ + for (int i = 0; i < SDL_NumJoysticks(); i++) + { + if (!SDL_IsGameController(i)) + { + printf("Controller not supported: %s", SDL_JoystickNameForIndex(i)); + printf(" - Google SDL_GAMECONTROLLERCONFIG to fix this\n"); + continue; + } + SDL_GameController *cont = SDL_GameControllerOpen(i); + printf("Controller added: %s %p\n", SDL_GameControllerNameForIndex(i), (void*)cont); + } + SDL_GameControllerEventState(SDL_ENABLE); +} + +// set current player to match an input device +void set_player_from_device(int device) +{ + for (int i = 0; i < NPLAY; i++) + { + if (play[i].device < 0) + p = play + i; // default to any keyboard + + if (play[i].device == device) + { + p = play + i; + return; + } + } +} + +// figure out which "device" from key pressed, i.e. WASD or Arrow keys +int device_from_key() +{ + switch(event.key.keysym.sym) { + case SDLK_w: case SDLK_a: case SDLK_s: case SDLK_d: case SDLK_z: case SDLK_x: + case SDLK_TAB: case SDLK_CAPSLOCK: case SDLK_LSHIFT: + return WASD; + default: + return ARROW_KEYS; + } +} + +int menu_input(int key_or_button) +{ + switch (key_or_button) + { + case SDLK_s: case SDLK_DOWN: + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: menu_pos++; break; + + case SDLK_w: case SDLK_UP: + case SDL_CONTROLLER_BUTTON_DPAD_UP: menu_pos--; break; + + case SDLK_RETURN: case SDLK_z: + case SDL_CONTROLLER_BUTTON_A: + if (state == MAIN_MENU) + { + if (menu_pos == 0) garbage_race = 0; + if (menu_pos == 1) garbage_race = 1; + if (menu_pos == 2) exit(0); + state = NUMBER_MENU; + menu_pos = 0; + } + else + { + nplay = menu_pos + 1; + resize(win_x, win_y); + state = ASSIGN; + assign_me = 0; + } + break; + } + return 0; +} + +int assign(int device) +{ + for (int i = 0; i < assign_me; i++) + if (play[i].device == device) + return 0; + + play[assign_me].device = device; + sprintf(play[assign_me].dev_name, "%.10s", + device == WASD ? "WASD keys" : + device == ARROW_KEYS ? "Arrow keys" : + SDL_GameControllerName(SDL_GameControllerFromInstanceID(device))); + + if (++assign_me == nplay) + { + state = PLAY; + assign_me = 0; + seed = rand(); + for (p = play; p < play + nplay; p++) + new_game(); + } + return 0; +} + +// handle a key press from a player +int key_down() +{ + if (event.key.repeat) return 0; + if (state == MAIN_MENU || state == NUMBER_MENU) return menu_input(event.key.keysym.sym); + if (state == GAMEOVER) return (state = MAIN_MENU); + if (state == ASSIGN) return assign(device_from_key()); + + set_player_from_device(device_from_key()); + + if (!p->it.color || p->countdown_time >= CTDN_TICKS) return 0; + + switch (event.key.keysym.sym) + { + case SDLK_a: case SDLK_LEFT: left(); break; + case SDLK_s: case SDLK_DOWN: down(); break; + case SDLK_d: case SDLK_RIGHT: right(); break; + + case SDLK_w: case SDLK_UP: hard(); break; + case SDLK_z: case SDLK_CAPSLOCK: case SDLK_COMMA: spin(3); break; + case SDLK_x: case SDLK_LSHIFT: case SDLK_PERIOD: spin(1); break; + case SDLK_TAB: case SDLK_SLASH: hold(); break; + + case SDLK_l: + p->level++; + p->lines += 10; + break; + } + + return 0; +} + +void key_up() +{ + if (state == ASSIGN) return; + set_player_from_device(device_from_key()); + + switch (event.key.keysym.sym) + { + case SDLK_a: case SDLK_LEFT: p->left = 0; break; + case SDLK_d: case SDLK_RIGHT: p->right = 0; break; + case SDLK_s: case SDLK_DOWN: p->down = 0; break; + } +} + +int joy_down() +{ + if (state == ASSIGN) return assign(event.cbutton.which); + if (state == GAMEOVER) return (state = MAIN_MENU); + if (state == MAIN_MENU || state == NUMBER_MENU) return menu_input(event.cbutton.button); + set_player_from_device(event.cbutton.which); + + if (p->it.color) switch(event.cbutton.button) + { + case SDL_CONTROLLER_BUTTON_A: spin(3); break; + case SDL_CONTROLLER_BUTTON_B: spin(1); break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: hard(); break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: down(); break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: left(); break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: right(); break; + case SDL_CONTROLLER_BUTTON_LEFTSHOULDER: hold(); break; + } + return 0; +} + +void joy_up() +{ + if (state == ASSIGN) return; + set_player_from_device(event.cbutton.which); + + switch(event.cbutton.button) + { + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: p->down = 0; break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: p->left = 0; break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: p->right = 0; break; + } +} diff --git a/examples/tetris/main.c b/examples/tetris/main.c new file mode 100644 index 0000000..73b45a0 --- /dev/null +++ b/examples/tetris/main.c @@ -0,0 +1,678 @@ +// Tet -- http://tinyc.games -- (c) 2023 Jer Wilson +// +// Tet is tiny implementation of a fully-featured Tetris clone. + +#pragma comment(lib, "SDL2") + +#ifdef _WIN32 +#pragma comment(lib, "opengl32") +#elif LINUX +#pragma comment(lib, "OpenGL") +#else +#pragma comment(lib, "OpenGL") +#endif + +#include +#include +#include +#include +#include + +#include "tet.h" + +#include +#include + +// common tinyc.games +#include "utils.c" +#include "audio.c" +#include "font.c" + +// tetris +#include "graphics.c" +#include "input.c" +#include "timer.c" + +int get_interval() +{ + static int then = 0; + int now = SDL_GetTicks(); + int diff = now - then; + then = now; + return diff; +} + +// the entry point and main game loop +int main(int argc, char **argv) +{ + float frame_times[10] = {0.f}; + int frame_time_idx = 0; + setup(); + float accum_msec = 0.f; + for (;;) + { + accum_msec += (float)get_interval(); + + if (accum_msec < 7.3333f) + { + int sleep_for = 8 - (int)accum_msec; + TIMECALL(SDL_Delay, (sleep_for)); + //fprintf(stderr, "Slept for %dms\n", sleep_for); + } + + accum_msec += (float)get_interval(); + + for (; accum_msec > 8.3333f; accum_msec -= 8.3333f) + { + TIMECALL(do_events, ()); + TIMER(update_player); + for (p = play; p < play + nplay; p++) update_player(); + accum_msec += (float)get_interval(); + TIMECALL(update_particles, ()); + // MAX_LOOPS? + } + + TIMECALL(draw_start, ()); + TIMECALL(draw_menu, ()); + TIMER(draw_player); + for (p = play; p < play + nplay; p++) draw_player(); + TIMECALL(draw_particles, ()); + TIMECALL(draw_end, ()); + TIMECALL(SDL_GL_SwapWindow, (win)); + + tick++; + + unsigned long long now = SDL_GetPerformanceCounter(); + static unsigned long long then; + float diff = (float)(now - then) / (float)SDL_GetPerformanceFrequency() * 1000.f; + frame_times[frame_time_idx++] = diff; + frame_time_idx %= 10; + then = now; + + if (SHOW_FPS && frame_time_idx == 0) + { + float sum = 0.f; + char timings_buf[8000]; + timer_print(timings_buf, 8000, true); + fprintf(stderr, "%s", timings_buf); + for (int i = 0; i < 10; i++) + sum += frame_times[i]; + fprintf(stderr, "avg frame time %.2fms = %.1f fps\n", + sum / 10.f, 10000.f / sum); + } + } +} + +void do_events() +{ + static int joy_tick = 0; + + while (SDL_PollEvent(&event)) switch (event.type) + { + case SDL_QUIT: exit(0); + case SDL_KEYDOWN: key_down(); break; + case SDL_KEYUP: key_up(); break; + case SDL_CONTROLLERBUTTONDOWN: joy_down(); break; + case SDL_CONTROLLERBUTTONUP: joy_up(); break; + case SDL_JOYDEVICEADDED: + case SDL_JOYDEVICEREMOVED: joy_tick = tick; break; + case SDL_WINDOWEVENT: + if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) + resize(event.window.data1, event.window.data2); + break; + } + + if (joy_tick == tick - 1) joy_setup(); +} + +#ifndef __APPLE__ +void GLAPIENTRY +MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, + GLsizei length, const GLchar* message, const void* userParam) +{ + if (type != GL_DEBUG_TYPE_ERROR) return; // too much yelling + fprintf( stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n", + ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), + type, severity, message ); + exit(-7); +} +#endif + +// initial setup to get the window and rendering going +void setup() +{ + srand(time(NULL)); + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO); + + win = SDL_CreateWindow("Tet", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + win_x, win_y, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); + if (!win) exit(fprintf(stderr, "%s\n", SDL_GetError())); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + ctx = SDL_GL_CreateContext(win); + if (!ctx) exit(fprintf(stderr, "Could not create GL context\n")); + + SDL_GL_SetSwapInterval(0); // vsync? + glClearColor(0.18f, 0.18f, 0.18f, 1.f); + + #ifndef __APPLE__ + glewExperimental = GL_TRUE; + glewInit(); + glEnable(GL_DEBUG_OUTPUT); + glDebugMessageCallback(MessageCallback, 0); + #endif + + resize(win_x, win_y); + audio_init(); + font_init(); + draw_setup(); +} + +unsigned garb_rand() +{ + return (p->seed1 = (1103515245 * p->seed1 + 12345) % 2147483648); +} + +unsigned bag_rand() +{ + return (p->seed2 = (1103515245 * p->seed2 + 13456) % 2147483648); +} + +void age_garbage() +{ + p->garbage[0] += p->garbage[1]; + for (int i = 1; i < GARB_LVLS; i++) + p->garbage[i] = p->garbage[i + 1]; // last loop looks like it goes oob, but there's room! + p->garbage_tick = tick; +} + +void receive_garbage() +{ + int gap = garb_rand() % 10; + int max_at_once = garbage_race ? 10 : 6; // no more than 6, 10 garbage at a time + for (int i = 0; p->garbage[0] && i < max_at_once; p->garbage[0]--, i++) + { + memmove(p->row, p->row + 1, (BHEIGHT - 1) * sizeof *p->row); + memset(&p->row[BHEIGHT - 1], 0, sizeof *p->row); + p->row[BHEIGHT - 1].special = 1; // special = garbage + p->row[BHEIGHT - 1].offset = (p->countdown_time ? 0 : -bs); + p->garbage_remaining++; + + for (int i = 0; i < 10; i++) + if (gap != i && (!garbage_race || garb_rand() % 20)) + { + p->row[BHEIGHT - 1].col[i] = (struct spot){9, '@'}; + p->row[BHEIGHT - 1].fullness++; + } + + if (garbage_race) gap = garb_rand() % 10; + audio_tone(SQUARE, A1, D1, 50, 5, 50, 200); + } +} + +void new_particle(int x, int y, int opponent, int garbage_bits) +{ + parts[npart++] = (struct particle){ + p->board_x + x * bs, + p->board_y + (y + VHEIGHT - BHEIGHT) * bs + bs2, + bs * 0.8f, + (rand() % 10 - 5) * 0.02f, + (rand() % 30 + 30) * 0.02f, + opponent, + garbage_bits, + }; + if (npart >= NPARTS) npart = 0; +} + +void kill_lines() +{ + int opponent = -1; // target if we end up sending garbage + + p->lines += p->shiny_lines; + p->level = p->lines / 10; + p->reward = combo_bonus[MIN(MAX_COMBO, p->combo)] * rewards[p->shiny_lines]; + p->combo++; + if (p->tspin == TSPIN_FULL) + p->reward *= 2; + p->score += p->reward; + int sendable = p->reward / 400; // garbage to send + int garbage_bits = sendable * 12 / p->shiny_lines; + p->shiny_lines = 0; + + if (sendable && nplay > 1 && !garbage_race) + { + opponent = rand() % (nplay - 1); + if (play + opponent >= p) + opponent++; + } + + // clean up sliced pieces + for (int y = 0; y < BHEIGHT; y++) + { + p->row[y].offset = 0; + if (p->row[y].fullness == 10) + for (int x = 0; x < BWIDTH; x++) + new_particle(x, y, opponent, garbage_bits); + + if (y > 0 && p->row[y - 1].fullness == 10) + for (int x = 0; x < BWIDTH; x++) + p->row[y].col[x].part &= ~1; + + if (y < BHEIGHT - 1 && p->row[y + 1].fullness == 10) + for (int x = 0; x < BWIDTH; x++) + p->row[y].col[x].part &= ~4; + } + + for (int y = 0; y < BHEIGHT; y++) + { + if (p->row[y].fullness != 10) continue; + + if (p->row[y].special) p->garbage_remaining--; + + for (int j = y; j > 0; j--) + { + p->row[j] = p->row[j - 1]; + p->row[j].offset += bs; + } + + memset(&p->row[0], 0, sizeof p->row[0]); + } + + reflow(); + + if (garbage_race && p->garbage_remaining == 0) + { + age_garbage(); + receive_garbage(); + if (p->garbage_remaining == 0) + state = GAMEOVER; + } +} + +void new_game() +{ + memset(p->row, 0, sizeof p->row); + p->garbage_remaining = 0; + p->bag_idx = BAG_SZ; + if (p->best < p->score) p->best = p->score; + p->score = 0; + p->lines = 0; + p->level = 0; + p->held.color = 0; + p->hold_uses = 0; + p->countdown_time = 4 * CTDN_TICKS; + p->seed1 = seed; + p->seed2 = seed; + + if (garbage_race) + { + p->garbage[0] = 10; + p->garbage[1] = 5; + p->garbage[2] = 5; + p->garbage[3] = 5; + receive_garbage(); + } +} + +// set the current piece to the top, middle to start falling +void reset_fall() +{ + p->idle_time = 0; + p->grounded_moves = 0; + p->it.x = 3; + p->it.y = 3; + p->it.rot = 0; +} + +// pick a new next piece from the bag, and put the old one in play +void new_piece() +{ + if (p->bag_idx >= BAG_SZ) // need to make a new bag? + { + p->bag_idx = 0; + + for (int i = 0; i < BAG_SZ; i++) + p->bag[i] = i % 7 + 1; + + for (int i = 0; i < BAG_SZ; i++) + SWAP(p->bag[i], p->bag[bag_rand() % BAG_SZ]); + } + + p->it.color = p->next[0]; + memmove(p->next, p->next + 1, sizeof *(p->next) * 4); + p->next[4] = p->bag[p->bag_idx++]; + reset_fall(); +} + +//move the falling piece left, right, or down +void move(int dx, int dy, int gravity) +{ + if (!gravity) + p->move_cooldown = p->move_cooldown ? 8 : 18; + + int collision = collide(p->it.x + dx, p->it.y + dy, p->it.rot); + + if (collision == NONE) + { + p->it.x += dx; + p->it.y += dy; + + // reset idle time if you voluntarily move DOWN + if (dy) p->idle_time = 0; + + // remember last successful x movement time + if (dx) p->last_dx_tick = tick; + + // reset idle time if piece is grounded, limit grounded moves though + if (p->grounded && p->grounded_moves < 15) + { + p->idle_time = 0; + p->grounded_moves++; + } + + if (!gravity) + audio_tone(TRIANGLE, C3, F3, 1, 1, 1, 1); + } + else if (dy && gravity) + { + bake(); + receive_garbage(); + if (tick != p->beam_tick) + audio_tone(TRIANGLE, C4, F4, 10, 10, 10, 10); + } + else if (collision == WALL) + { + if (dx == -1 && tick - p->last_dx_tick < 16) + { + p->shake_x -= .25f; + audio_tone(TRIANGLE, C2, C2, 25, 5, 5, 25); + } + if (dx == 1 && tick - p->last_dx_tick < 16) + { + p->shake_x += .25f; + audio_tone(TRIANGLE, E2, C2, 15, 5, 5, 15); + } + } +} + +// update everything related to current player, while the game is running normally +void update_player() +{ + if (state != PLAY) return; + + if (p->countdown_time > 0) + { + int note = p->countdown_time > CTDN_TICKS ? A4 : A5; + if (p->countdown_time % CTDN_TICKS == 0) + audio_tone(TRIANGLE, note, note, 80, 50, 5, 20); + + if (--p->countdown_time > CTDN_TICKS) + return; + } + + p->ticks++; + + while (!p->it.color && !p->shine_time && !p->dead_time) + new_piece(); + + p->grounded = collide(p->it.x, p->it.y + 1, p->it.rot); + + if (p->move_cooldown) p->move_cooldown--; + + if (p->move_cooldown < 2) + { + if (p->left) move(-1, 0, 0); + if (p->right) move( 1, 0, 0); + if (p->down) move( 0, 1, 0); + } + + for (int y = 0; y < BHEIGHT; y++) + if (p->row[y].offset > 0) + p->row[y].offset = MAX(0, p->row[y].offset - bs4); + else + p->row[y].offset = MIN(0, p->row[y].offset + bs4); + + if (p->shine_time > 0 && --p->shine_time == 0) + kill_lines(); + + if (p->dead_time > 0) + { + p->dead_time--; + int x = p->dead_time % BWIDTH; + int y = BHEIGHT - 1 - p->dead_time / BWIDTH; + if (p->row[y].col[x].part) + { + new_particle(x, y, -1, 0); + p->row[y].col[x].part = 0; + } + if (p->dead_time == 0) + new_game(); + } + + if (++p->idle_time >= (p->grounded ? 100 : speeds[MIN(MAX_SPEED, p->level)])) + { + move(0, 1, 1); + p->idle_time = 0; + } + + if (tick - p->garbage_tick > (garbage_race ? 6000 : 400)) + age_garbage(); + + // update shaky offsets + if (p->shake_x < .01f && p->shake_x > -.01f) p->shake_x = .0f; + if (p->shake_y < .01f && p->shake_y > -.01f) p->shake_y = .0f; + if (p->shake_x) p-> shake_x *= .98f; + if (p->shake_y) p-> shake_y *= .98f; + + // decrease flash + p->flash = CLAMP(0, p->flash - 1, 18); +} + +void game_over() +{ + memset(p->bag, 0, sizeof p->bag); + memset(p->next, 0, sizeof p->next); + p->dead_time = BHEIGHT * BWIDTH; +} + +// check if a line has been completed and act accordingly +int increment_and_check_line(int y) +{ + if (++p->row[y].fullness != 10) + return 0; + + p->reward = 0; // set up hovering reward number + p->reward_x = p->board_x + bs * (p->it.x + 2); + p->reward_y = p->board_y + bs * (p->it.y - 4); + p->shine_time = 40; + p->shiny_lines++; + + for (int i = 0; i < BWIDTH; i++) + p->row[y].col[i].color = 8; + return 1; +} + +// bake the falling piece into the background/board +void bake() +{ + int completed_lines = 0; + int tsum = 0; // for detecting t-spins + int stuck = collide(p->it.x , p->it.y - 1, p->it.rot) + && collide(p->it.x - 1, p->it.y , p->it.rot) + && collide(p->it.x + 1, p->it.y , p->it.rot); + + for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) + { + int world_i = i + p->it.x; + int world_j = j + p->it.y; + int part = is_solid_part(p->it.color, p->it.rot, i, j); + + int tpart = is_tspin_part(p->it.color, p->it.rot, i, j); + if (world_i < 0 || world_i >= BWIDTH || world_j < 0 || world_j >= BHEIGHT || + p->row[world_j].col[world_i].color) + tsum += tpart; + + if (!part) continue; + + if (p->row[world_j].col[world_i].color) + game_over(); + + p->row[world_j].col[world_i].color = p->it.color; + p->row[world_j].col[world_i].part = part; + completed_lines += increment_and_check_line(world_j); + } + + p->it.color = 0; + p->hold_uses = 0; + p->tspin = TSPIN_NONE; + + if (stuck && tsum >= 21) + { + p->tspin = TSPIN_FULL; + audio_tone(SQUARE, F2, F2, 2, 2, 2, 2); + audio_tone(SQUARE, D4, D4, 2, 3, 1, 1); + audio_tone(SINE, C5, C5, 20, 5, 1, 1); + audio_tone(SINE, E5, E5, 80, 5, 1, 40); + } + else if (stuck && tsum == 12) + { + p->tspin = TSPIN_MINI; + audio_tone(SQUARE, F2, F2, 2, 2, 2, 2); + audio_tone(SQUARE, D4, D4, 2, 3, 1, 1); + } + else if (completed_lines) + { + for (int i = 0; i < completed_lines; i++) + audio_tone(SINE, G3, G5, 20, 50, 50, 200); + } + else + { + p->combo = 0; // break combo + } +} + +// check if a sub-part of the falling shape is solid at a particular rotation +int is_solid_part(int shape, int rot, int i, int j) +{ + int base = shape * 5 + rot * 5 * 8 * 4; + int part = shapes[base + j * 5 * 8 + i]; + return part < '@' ? 0 : part; +} + +int is_tspin_part(int shape, int rot, int i, int j) +{ + int base = shape * 5 + rot * 5 * 8 * 4; + int part = shapes[base + j * 5 * 8 + i]; + return part == ',' ? 1 : part == ';' ? 10 : 0; +} + +// check if the current piece would collide at a certain position and rotation +int collide(int x, int y, int rot) +{ + int ret = NONE; + for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) + if (is_solid_part(p->it.color, rot, i, j)) + { + if (i + x < 0 || i + x >= BWIDTH || j + y >= BHEIGHT) + return WALL; + if (p->row[j + y].col[i + x].color) + ret = NORMAL; + } + + return ret; +} + +int in_rect(int x, int y, int left, int top, int width, int height) +{ + return x >= left && x <= left + width && y >= top && y <= top + height; +} + +void update_particles() +{ + for (int i = 0; i < NPARTS; i++) + { + struct particle *q = parts + i; + if (q->r <= 0.1f) + continue; + q->x += q->vx; + q->y += q->vy; + + if (q->r < 2.f) + q->r *= 0.996f + (rand() % 800) * 0.001f; + else + q->r *= 0.992f + (rand() % 400) * 0.00001f; + + struct player *o = q->opponent < 0 ? NULL : + play + q->opponent; + + // get contribution from flow nodes + float flow_vx = 0.f; + float flow_vy = 0.f; + for (int n = 0; n < NFLOWS; n++) + { + float xdiff = flows[n].x - q->x; + float ydiff = flows[n].y - q->y; + float distsq = xdiff * xdiff + ydiff * ydiff; + if (distsq > flows[n].r * flows[n].r) + continue; + flow_vx += flows[n].vx; + flow_vy += flows[n].vy; + } + + // get contribution from homing in on opponent + float targ_x = 0.f; + float targ_y = 0.f; + float homing_vx = 0.f; + float homing_vy = 0.f; + float dist = 1000.f; + if (o) + { + targ_x = o->board_x - 3 * bs2; + targ_y = CLAMP(o->top_garb - bs, q->y, o->board_y + bs * VHEIGHT); + homing_vx = (targ_x - q->x); + homing_vy = (targ_y - q->y); + dist = sqrtf(homing_vx * homing_vx + homing_vy * homing_vy); + homing_vx /= dist; + homing_vy /= dist; + } + + float normal_r = q->r / bs; + if (o && q->r && normal_r < .65f + (i % 9) * .07f) // particle has an opponent target + { + if (dist < bs * 4 || normal_r < .5f) + { + q->vx *= .97f; + q->vy *= .97f; + } + float t = (dist < bs * 4) ? (dist / (bs * 6)) : + .75f; + q->vx += flow_vx * t + homing_vx * (1.f - t); + q->vy += flow_vy * t + homing_vy * (1.f - t); + } + else if (!o && normal_r > .8f) // particle still just falling softly + { + q->vy *= .82f; + } + else + { + q->vx += flow_vx; + q->vy += flow_vy; + } + + if (o && in_rect(q->x + q->r * .5f, q->y + q->r * .5f, targ_x, targ_y - bs2, bs, bs)) + { + q->r = 0.f; + o->flash += 50; + o->garbage_bits += q->bits; + if (o->garbage_bits >= 120) + { + audio_tone(SQUARE, G3, G4, 15, 15, 15, 100); + o->garbage[GARB_LVLS - 1]++; + o->garbage_tick = tick; + o->garbage_bits -= 120; + } + audio_tone(SQUARE, G4, C6, 5, 10, 5, 20); + } + } +} diff --git a/examples/tetris/shaders/main.frag b/examples/tetris/shaders/main.frag new file mode 100644 index 0000000..ee0dc2e --- /dev/null +++ b/examples/tetris/shaders/main.frag @@ -0,0 +1,9 @@ +#version 330 core +in vec3 color2; + +out vec4 color; + +void main() +{ + color = vec4(color2.xyz, 1.0); +} diff --git a/examples/tetris/shaders/main.vert b/examples/tetris/shaders/main.vert new file mode 100644 index 0000000..a017457 --- /dev/null +++ b/examples/tetris/shaders/main.vert @@ -0,0 +1,13 @@ +#version 330 core +layout (location = 0) in vec2 pos; +layout (location = 1) in vec3 color; + +out vec3 color2; + +uniform mat4 proj; + +void main() +{ + gl_Position = proj * vec4(pos.xy, 0.0, 1.0); + color2 = color; +} diff --git a/examples/tetris/tet.h b/examples/tetris/tet.h new file mode 100644 index 0000000..af2c877 --- /dev/null +++ b/examples/tetris/tet.h @@ -0,0 +1,191 @@ +#pragma once +#include + +#define GL3_PROTOTYPES 1 + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#define BWIDTH 10 // board width, height +#define BHEIGHT 25 +#define VHEIGHT 20 // visible height +#define BAG_SZ 7 // bag size +#define GARB_LVLS 4 // levels of queued garbage +#define NPLAY 4 +#define NPARTS 1000 +#define NFLOWS 20 +#define CTDN_TICKS 96 +#define SHOW_FPS 0 + +// collision test results +enum { NONE = 0, WALL, NORMAL }; + +enum { TSPIN_NONE = 0, TSPIN_FULL, TSPIN_MINI }; + +// Bits in each letter indicate which sides connect when drawing +// A 1000001 - up +// B 1000010 - right +// D 1000100 - down +// H 1001000 - left +// 1010000 - connect left corner where up or down connects - for square piece +// 1100000 - ... right ... + +char shapes[] = + ".... D... ..D. .Vl. .... BL.. .FH. ;D;. " + ".... CJH. BJI. .Si. BJJH .CH. BI.. BKH. " + ".... .... .... .... .... .... .... ,.,. " + ".... .... .... .... .... .... .... .... " + + ".... .FH. .D.. .Vl. ..D. ..D. .D.. ,D;. " + ".... .E.. .E.. .Si. ..E. .FI. .CL. .GH. " + ".... .A.. .CH. .... ..E. .A.. ..A. ,A;. " + ".... .... .... .... ..A. .... .... .... " + + ".... .... .... .Vl. .... .... .... ,.,. " + ".... BJL. FJH. .Si. .... BL.. .FH. BNH. " + ".... ..A. A... .... BJJH .CH. BI.. ;A;. " + ".... .... .... .... .... .... .... .... " + + ".... .D.. BL.. .Vl. .D.. .D.. D... ;D,. " + ".... .E.. .E.. .Si. .E.. FI.. CL.. BM.. " + ".... BI.. .A.. .... .E.. A... .A.. ;A,. " + ".... .... .... .... .A.. .... .... .... "; + +struct shadow { int x, w, y; } shadows[4][8] = { // pre-computed shadow positions for each piece + {{0,0,0}, {0,3,1}, {0,3,1}, {1,2,0}, {0,4,1}, {0,3,1}, {0,3,1}, {0,3,1}}, + {{0,0,0}, {1,2,0}, {1,2,2}, {1,2,0}, {2,1,0}, {1,2,1}, {1,2,1}, {1,2,1}}, + {{0,0,0}, {0,3,1}, {0,3,1}, {1,2,0}, {0,4,2}, {0,3,2}, {0,3,2}, {0,3,1}}, + {{0,0,0}, {0,2,2}, {0,2,0}, {1,2,0}, {1,1,0}, {0,2,1}, {0,2,1}, {0,2,1}}, +}; + +// helps center shapes in preview box +int center[] = {0,0, 1,1, 1,1, 0,1, 0,0, 1,1, 1,1, 1,1}; + +int colors[] = { + 0x000000, // unused + 0x1983c4, // J + 0xfa8333, // L + 0xffca39, // square + 0x1be7ff, // line + 0xff5a5f, // Z + 0x89c926, // S + 0x88488f, // T + 0xffffff, // shine color + 0x6f7866, // garbage colors + 0x9fa896, + 0xffca39, + 0xff5a5f, +}; + +int kicks[] = { // clockwise counterclockwise + 0,0, -1, 0, -1, 1, 0,-2, -1,-2, 0,0, 1, 0, 1, 1, 0,-2, 1,-2, // rot 0 + 0,0, -1, 0, -1,-1, 0, 2, -1, 2, 0,0, -1, 0, -1,-1, 0, 2, -1, 2, // rot 1 + 0,0, 1, 0, 1, 1, 0,-2, 1,-2, 0,0, -1, 0, -1, 1, 0,-2, -1,-2, // rot 2 + 0,0, 1, 0, 1,-1, 0, 2, 1, 2, 0,0, 1, 0, 1,-1, 0, 2, 1, 2, // rot 3 + // line-clockwise line-counterclockwise + 0,0, 2, 0, -1, 0, 2,-1, -1, 2, 0,0, 1, 0, -2, 0, 1, 2, -2,-1, // rot 0 + 0,0, -2, 0, 1, 0, -2, 1, 1,-2, 0,0, 1, 0, -2, 0, 1, 2, -2,-1, // rot 1 + 0,0, -1, 0, 2, 0, -1,-2, 2, 1, 0,0, -2, 0, 1, 0, -2, 1, 1,-2, // rot 2 + 0,0, 2, 0, -1, 0, 2,-1, -1, 2, 0,0, -1, 0, 2, 0, -1,-2, 2, 1, // rot 3 +}; + +float combo_bonus[] = { + 1.f, 1.5f, 2.f, 3.f, 4.f, 5.f, 6.f, 8.f, 10.f, 12.f, 15.f, 20.f, + 25.f, 30.f, 40.f, 50.f, 75.f, 100.f +}; +#define MAX_COMBO ((sizeof combo_bonus / sizeof *combo_bonus) - 1) + +int rewards[] = {0, 100, 250, 500, 1000}; // points for clearing 0,1,2,3,4 lines + +int speeds[] = {100, 80, 70, 60, 52, 46, 40, 35, 30, 26, 22, 18, 15, 12, 10, 8, 6, 5, 4, 3, 2}; +#define MAX_SPEED ((sizeof speeds / sizeof *speeds) - 1) + +char countdown_msg[][20] = {" Go!", " - 1 -", " - 2 -", " - 3 -"}; + +struct piece { int x, y, rot, color; }; +struct spot { int color, part; }; +struct row { + struct spot col[BWIDTH]; + int fullness; + int special; + int offset; +}; + +struct player { + struct row row[BHEIGHT]; // the board, excluding the falling piece + int left, right, down; // true when holding a direction + int move_cooldown; // cooldown before hold-to-repeat movement + struct piece it; // current falling piece - "it" + struct piece beam; // hard drop beam + struct piece held; // shape in the hold box + int beam_tick; // tick that beam was created + int hold_uses; // have we swapped with the hold already? + int bag[BAG_SZ]; // "bag" of upcoming pieces + int bag_idx; // last position used up in bag + int next[5]; // next pieces in preview (take from bag) + int grounded; // is piece on the ground? + int grounded_moves; // how many moves have we made on the ground? + int last_dx_tick; // tick of most recent left/right movement + int lines, score, best; // scoring + int combo; // clears in-a-row + int reward, reward_x, reward_y; // for hovering points indicator + int garbage[GARB_LVLS + 1]; // queued garbage, e.g. received from opponents + int garbage_tick; // keeps track of when to age garbage + int garbage_remaining; // how many lines of garbage remain to clear to win + int garbage_bits; // fractions of garbage attached to each particle + int top_garb; // highest position of garbage stack drawn + int level; // difficultly level (lines/10) + int countdown_time; // ready-set-go countdown + int idle_time; // how long the player has been idle in ticks + int shiny_lines; + int shine_time; // delay in ticks before clearing line(s) + int dead_time; // delay in ticks after game over + int board_x, board_y, board_w; // positions and sizes of things + int preview_x, preview_y; // position of preview + int box_w; // width of hold box / preview box + int ticks; // counts up while game is going + int seed1, seed2; // make garbage and bags fair + float shake_x, shake_y; // amount the board is offset by shaking + int flash; // flashing from receiving garbage + int tspin; + int device; // SDL's input device id + char dev_name[80]; // input device "name" +} play[NPLAY], *p; // one per player + +struct particle { float x, y, r, vx, vy; int opponent, bits; }; +struct particle parts[NPARTS]; +struct particle flows[NFLOWS]; + +enum state { MAIN_MENU = 0, NUMBER_MENU, ASSIGN, PLAY, GAMEOVER} state; +int win_x = 1000; // window size +int win_y = 750; +int bs, bs2, bs4; // individual block size, in half, in quarters +int tick; // counts up one per frame +int nplay = 1; // number of players +int assign_me; // who is getting an input device assigned? +int menu_pos; // current position in menu +int text_x, text_y; // position of text drawing +int line_height; // text line height +int garbage_race; +int npart; +int seed; + +SDL_GLContext ctx; +SDL_Event event; +SDL_Window *win; +SDL_Renderer *renderer; + +void do_events(); +void setup(); +void update_player(); +void move(int dx, int dy, int gravity); +void reset_fall(); +void bake(); +void new_game(); +int is_solid_part(int shape, int rot, int i, int j); +int is_tspin_part(int shape, int rot, int i, int j); +int collide(int x, int y, int rot); +void update_particles(); diff --git a/examples/tetris/timer.c b/examples/tetris/timer.c new file mode 100644 index 0000000..e1236c5 --- /dev/null +++ b/examples/tetris/timer.c @@ -0,0 +1,15 @@ +#define TIMER_NAMES \ + X(SDL_Delay), \ + X(do_events), \ + X(update_player), \ + X(update_particles), \ + X(draw_start), \ + X(draw_menu), \ + X(draw_player), \ + X(draw_particles), \ + X(draw_end), \ + X(SDL_GL_SwapWindow), + +#include + +#undef TIMER_NAMES diff --git a/examples/tinyc.games/PixelatedEleganceRegular.ttf b/examples/tinyc.games/PixelatedEleganceRegular.ttf new file mode 100755 index 0000000..9deeb16 Binary files /dev/null and b/examples/tinyc.games/PixelatedEleganceRegular.ttf differ diff --git a/examples/tinyc.games/audio.c b/examples/tinyc.games/audio.c new file mode 100644 index 0000000..d41dd18 --- /dev/null +++ b/examples/tinyc.games/audio.c @@ -0,0 +1,84 @@ +// tinyc.games audio - copyright 2023 Jer Wilson +// +// 1. Add SDL_INIT_AUDIO to your SDL_Init() +// 2. Call audioinit() before playing sound +// 3. Play tones with audio_tone() + +#include +#include +#include "audio.h" + +void audio_tone(int shape, int note_lo, int note_hi, + double attack, double decay, double sustain, double release) +{ + int note = note_lo + rand() % (note_hi - note_lo + 1); + if (note < A0 || note > C6) return; + + envs[rand() % NUM_ENVS] = (struct envelope){ + .shape = shape, + .start_freq = music_notes[note].frequency, + .volume = 1.0 + 0.08 * music_notes[note].wavelength, + .attack = (attack ) / 1000.f, + .decay = (attack + decay ) / 1000.f, + .sustain = (attack + decay + sustain ) / 1000.f, + .release = (attack + decay + sustain + release) / 1000.f, + }; +} + +static void mix_audio(void *unused, unsigned char *stream, int len) +{ + short *out = (short*)stream; + + for (int j = 0; j < len/2; j++) + { + int samp = 0; + + for (int n = 0; n < NUM_ENVS; n++) + { + struct envelope *e = envs + n; + if (e->pos >= e->release) + continue; + + double freq = e->start_freq; + double wl = 1.0 / freq; + double wl2 = wl / 2.0; + double frac = fmod(e->pos, wl); + double t = e->pos; + e->pos += 1.0 / 44100; + + double veloc = e->volume * 2200; + if (t <= e->attack ) veloc *= t / e->attack * 1.5; + else if (t <= e->decay ) veloc *= (e->decay - t) / (e->decay - e->attack) * .5 + 1.0; + else if (t <= e->sustain) ; + else if (t <= e->release) veloc *= (e->release - t) / (e->release - e->sustain); + + switch (e->shape) + { + case SINE: samp += veloc * sin(frac * freq * 6.28318531); break; + case SQUARE: samp += frac > wl2 ? veloc : -veloc; break; + case TRIANGLE: samp += (4 * freq * (frac > wl2 ? (wl - frac) : frac) - 1) * veloc; break; + } + } + + out[j] = samp > 32767 ? 32768 : + samp < -32767 ? -32767 : + samp; + } +} + +void audio_init() +{ + SDL_AudioDeviceID id; + SDL_AudioSpec actual, desired = { + .freq = 44100, + .format = AUDIO_S16LSB, + .channels = 1, + .samples = 512, + .callback = mix_audio, + }; + id = SDL_OpenAudioDevice(NULL, 0, &desired, &actual, SDL_AUDIO_ALLOW_ANY_CHANGE); + if (!id) + fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError()); + else + SDL_PauseAudioDevice(id, 0); +} diff --git a/examples/tinyc.games/audio.h b/examples/tinyc.games/audio.h new file mode 100644 index 0000000..5667a9b --- /dev/null +++ b/examples/tinyc.games/audio.h @@ -0,0 +1,100 @@ +#pragma once + +#define NUM_ENVS 20 + +enum shape {SINE, SQUARE, TRIANGLE}; + +struct envelope { + enum shape shape; + double start_freq; + double volume; + double attack, decay, sustain, release; // envelope timings + double pos; // current play position + double noiseval; + int noisectr; + int noisesign; +} envs[NUM_ENVS]; + +enum notes { + A0, As0, B0, C1, Cs1, D1, Ds1, E1, F1, Fs1, G1, Gs1, + A1, As1, B1, C2, Cs2, D2, Ds2, E2, F2, Fs2, G2, Gs2, + A2, As2, B2, C3, Cs3, D3, Ds3, E3, F3, Fs3, G3, Gs3, + A3, As3, B3, C4, Cs4, D4, Ds4, E4, F4, Fs4, G4, Gs4, + A4, As4, B4, C5, Cs5, D5, Ds5, E5, F5, Fs5, G5, Gs5, + A5, As5, B5, C6, +}; + +struct { + char name[3]; + int octave; + double frequency; + double wavelength; +} music_notes[] = { + {"A" , 0, 27.500, 12.374}, // Piano low + {"A#", 0, 29.135, 11.680}, + {"B" , 0, 30.868, 11.024}, + {"C" , 1, 32.703, 10.405}, + {"C#", 1, 34.648, 9.821}, + {"D" , 1, 36.708, 9.270}, + {"D#", 1, 38.891, 8.750}, + {"E" , 1, 41.203, 8.259}, + {"F" , 1, 43.654, 7.795}, + {"F#", 1, 46.249, 7.358}, + {"G" , 1, 48.999, 6.945}, + {"G#", 1, 51.913, 6.555}, + {"A" , 1, 55.000, 6.187}, + {"A#", 1, 58.270, 5.840}, + {"B" , 1, 61.735, 5.512}, + {"C" , 2, 65.406, 5.203}, + {"C#", 2, 69.296, 4.911}, + {"D" , 2, 73.416, 4.635}, + {"D#", 2, 77.782, 4.375}, + {"E" , 2, 82.407, 4.129}, + {"F" , 2, 87.307, 3.898}, + {"F#", 2, 92.499, 3.679}, + {"G" , 2, 97.999, 3.472}, + {"G#", 2, 103.826, 3.278}, + {"A" , 2, 110.000, 3.094}, + {"A#", 2, 116.541, 2.920}, + {"B" , 2, 123.471, 2.756}, + {"C" , 3, 130.813, 2.601}, + {"C#", 3, 138.591, 2.455}, + {"D" , 3, 146.832, 2.318}, + {"D#", 3, 155.563, 2.187}, + {"E" , 3, 164.814, 2.065}, + {"F" , 3, 174.614, 1.949}, + {"F#", 3, 184.997, 1.839}, + {"G" , 3, 195.998, 1.736}, + {"G#", 3, 207.652, 1.639}, + {"A" , 3, 220.000, 1.547}, + {"A#", 3, 233.082, 1.460}, + {"B" , 3, 246.942, 1.378}, + {"C" , 4, 261.626, 1.301}, // middle C + {"C#", 4, 277.183, 1.228}, + {"D" , 4, 293.665, 1.159}, + {"D#", 4, 311.127, 1.094}, + {"E" , 4, 329.628, 1.032}, + {"F" , 4, 349.228, 0.974}, + {"F#", 4, 369.994, 0.920}, + {"G" , 4, 391.995, 0.868}, + {"G#", 4, 415.305, 0.819}, + {"A" , 4, 440.000, 0.773}, // A 440 + {"A#", 4, 466.164, 0.730}, + {"B" , 4, 493.883, 0.689}, + {"C" , 5, 523.251, 0.650}, + {"C#", 5, 554.365, 0.614}, + {"D" , 5, 587.330, 0.579}, + {"D#", 5, 622.254, 0.547}, + {"E" , 5, 659.255, 0.516}, + {"F" , 5, 698.456, 0.487}, + {"F#", 5, 739.989, 0.460}, + {"G" , 5, 783.991, 0.434}, + {"G#", 5, 830.609, 0.410}, + {"A" , 5, 880.000, 0.387}, + {"A#", 5, 932.328, 0.365}, + {"B" , 5, 987.767, 0.345}, +}; + +void audio_init(); +void audio_tone(int shape, int note_lo, int note_hi, + double attack, double decay, double sustain, double release); diff --git a/examples/tinyc.games/flappy/background.png b/examples/tinyc.games/flappy/background.png new file mode 100644 index 0000000..af462ca Binary files /dev/null and b/examples/tinyc.games/flappy/background.png differ diff --git a/examples/tinyc.games/flappy/bird-0.png b/examples/tinyc.games/flappy/bird-0.png new file mode 100644 index 0000000..49f1617 Binary files /dev/null and b/examples/tinyc.games/flappy/bird-0.png differ diff --git a/examples/tinyc.games/flappy/bird-1.png b/examples/tinyc.games/flappy/bird-1.png new file mode 100644 index 0000000..0ca7eda Binary files /dev/null and b/examples/tinyc.games/flappy/bird-1.png differ diff --git a/examples/tinyc.games/flappy/bird-2.png b/examples/tinyc.games/flappy/bird-2.png new file mode 100644 index 0000000..6112033 Binary files /dev/null and b/examples/tinyc.games/flappy/bird-2.png differ diff --git a/examples/tinyc.games/flappy/bird-3.png b/examples/tinyc.games/flappy/bird-3.png new file mode 100644 index 0000000..27d5412 Binary files /dev/null and b/examples/tinyc.games/flappy/bird-3.png differ diff --git a/examples/tinyc.games/flappy/pillar.png b/examples/tinyc.games/flappy/pillar.png new file mode 100644 index 0000000..6dc8d49 Binary files /dev/null and b/examples/tinyc.games/flappy/pillar.png differ diff --git a/examples/tinyc.games/font.c b/examples/tinyc.games/font.c new file mode 100644 index 0000000..e73ed3e --- /dev/null +++ b/examples/tinyc.games/font.c @@ -0,0 +1,282 @@ +#pragma once + +#include + +#define FONT_CH_W 8 +#define FONT_CH_H 12 +#define FONT_PITCH 128 +#define FONT_LINES 128 +#define FONT_BUFLEN 16000 + +GLuint font_tex_id; +unsigned int font_prog_id; +GLuint font_vbo, font_vao; + +int font_screenw; +int font_screenh; + +float font_buf[FONT_BUFLEN + 100]; +float *font_buf_limit = font_buf + FONT_BUFLEN; +float *font_buf_p = font_buf; + +int font_spacing[256] = { 6,3,7,7,7,6,7,3,5,5,6,7,3,6,3,6, + 6,6,6,6,6,6,6,6,6,6,3,3,5,6,5,7, + 7,6,6,6,6,6,6,6,6,5,6,6,6,7,7,6, + 6,6,6,6,7,6,7,7,7,7,6,5,6,5,7,7, + 4,6,6,6,6,6,5,6,6,4,5,6,4,7,6,6, + 6,6,5,6,5,6,6,7,6,6,6,5,3,5,6,6 }; + +char font_kerning[(256 - ' ') * 256]; + +void font_init() +{ + char font_data[FONT_PITCH * FONT_LINES] = +"................................................................................................................................" +"................................................................................................................................" +"| |O |OO OO | | O | | |O | O |O | | | | | | O " +"| |O | O O | O O | OOO | | OO |O | O | O | | O | | | | O " +"| |O | |OOOOO |O O | |O | |O | O | O O | O | | | | O " +"| |O | | O O | OOO |O O | O | |O | O | O |OOOOO | |OOOO | | O " +"| |O | | O O | O O | O |O O O | |O | O | O O | O | | | | O " +"| | | |OOOOO | OOO | O |O O | | O | O | | O | | | | O " +"| |O | | O O | O |O O | OO O | | O |O | | |O | |O |O " +".................................................................................................O.......................O......" +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"| OO | O | OO |OOO |O |OOOO | OO |OOOO | OO | OO | | | | | | OOO " +"|O O | OO |O O | O |O O |O |O | O |O O |O O | | | O | |O |O O " +"|O OO | O | O | O |O O |OOO |O | O |O O |O O | | | O |OOOO | O | O " +"|OO O | O | O | OO |O O | O |OOO | O | OO | OOO |O |O |O | | O | O " +"|O O | O | O | O |OOOO | O |O O | O |O O | O | | | O |OOOO | O | O " +"|O O | O |O | O | O |O O |O O | O |O O | O | | | O | |O | " +"| OO | OOO |OOOO |OOO | O | OO | OO | O | OO | OO |O |O | | | | O " +".........................................................................................O......................................" +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"| OOO | OO |OOO | OO |OOO |OOOO |OOOO | OO |O O |OOO | O |O O |O |O O |O O | OO " +"|O O |O O |O O |O O |O O |O |O |O O |O O | O | O |O O |O |OO OO |OO O |O O " +"|O OOO |O O |O O |O |O O |O |O |O |O O | O | O |O O |O |O O O |O O O |O O " +"|O O O |OOOO |OOO |O |O O |OOO |OOO |O OO |OOOO | O | O |OO |O |O O O |O OO |O O " +"|O OOO |O O |O O |O |O O |O |O |O O |O O | O | O |O O |O |O O |O O |O O " +"|O |O O |O O |O O |O O |O |O |O O |O O | O |O O |O O |O |O O |O O |O O " +"| OOO |O O |OOO | OO |OOO |OOOO |O | OOO |O O |OOO | OO |O O |OOOO |O O |O O | OO " +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"|OOO | OO |OOO | OOO |OOOOO |O O |O O |O O |O O |O O |OOOO |OOO |O |OOO | O | " +"|O O |O O |O O |O | O |O O |O O |O O |O O |O O | O |O |O | O | O O | " +"|O O |O O |O O |O | O |O O |O O |O O | O O |O O | O |O | O | O |O O | " +"|OOO |O O |OOO | OO | O |O O |O O |O O O | O | OOO | O |O | O | O | | " +"|O |O O |O O | O | O |O O | O O |O O O | O O | O |O |O | O | O | | " +"|O |O O |O O | O | O |O O | O O |O O O |O O | O |O |O | O | O | | " +"|O | OO |O O |OOO | O | OO | O | O O |O O | O |OOOO |OOO | O |OOO | | " +"............O.......................................................................................O....................OOOOO.." +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"|O | |O | | O | | O | |O | O | O |O |OO | | | " +"| O | |O | | O | | O | |O | | |O | O | | | " +"| | OO |OOO | OOO | OOO | OO |OOO | OOO |OOO |OO | OO |O O | O |OOOO |OOO | OO " +"| | O |O O |O |O O |O O | O |O O |O O | O | O |O O | O |O O O |O O |O O " +"| | OOO |O O |O |O O |OOOO | O |O O |O O | O | O |OO | O |O O O |O O |O O " +"| |O O |O O |O |O O |O | O |O O |O O | O | O |O O | O |O O O |O O |O O " +"| | OOO |OOO | OOO | OOO | OOO | O | OOO |O O | O | O |O O | O |O O O |O O | OO " +"............................................................O......................O............................................" +"..........................................................OO.....................OO............................................." +"................................................................................................................................" +"................................................................................................................................" +"................................................................................................................................" +"| | | | | | | | | | | | O |O |O | O O | " +"| | | | | O | | | | | | | O |O | O |O O | " +"|OOO | OOO |O O | OOO |OOO |O O |O O |O O O |O O |O O |OOOO | O |O | O | | " +"|O O |O O |OO |O | O |O O |O O |O O O |O O |O O | O |O |O | O | | " +"|O O |O O |O | OO | O |O O |O O |O O O | OO |O O | OO | O | | O | | " +"|O O |O O |O | O | O |O O |O O |O O O |O O |O O |O | O |O | O | | " +"|OOO | OOO |O |OOO | OO | OOO |OO | OOOO |O O | OOO |OOOO | O |O |O | | " +".O..........O...............................................................O....................O.............................." +".O..........O.............................................................OO.....................O.............................." +"................................................................................................................................"; + + for (int i = 0; i < FONT_PITCH * FONT_LINES; i++) + font_data[i] = (font_data[i] == 'O') ? 0xFF : 0; + + // compute kerning + for (int a = 0; a < '~' - ' '; a++) for (int b = 0; b < '~' - ' '; b++) + { + int achar = (a + ' ') | ('A' ^ 'a'); + int bchar = (b + ' ') | ('A' ^ 'a'); + if (achar < 'a' || achar > 'z' || bchar < 'a' || bchar > 'z') + continue; + + int overlap; + int abase = (a % 16) * FONT_CH_W + (a / 16) * FONT_PITCH * FONT_CH_H + 1; + int bbase = (b % 16) * FONT_CH_W + (b / 16) * FONT_PITCH * FONT_CH_H + 1; + for (overlap = 1; overlap < 5; overlap++) + { + int boff = font_spacing[a] - overlap; + + for (int acol = 0; acol < FONT_CH_W; acol++) + { + int bcol = acol - boff; + if (bcol < 0) continue; // b is out of bounds + + for (int row = 1; row < FONT_CH_H - 1; row++) + { + int r0 = (row-1) * FONT_PITCH; + int r1 = (row+0) * FONT_PITCH; + int r2 = (row+1) * FONT_PITCH; + int a0_on = font_data[abase + acol + r0] ? 1 : 0; + int a1_on = font_data[abase + acol + r1] ? 1 : 0; + int a2_on = font_data[abase + acol + r2] ? 1 : 0; + int b_on = font_data[bbase + bcol + r1] ? 1 : 0; + if ((a0_on && b_on) || (a1_on && b_on) || (a2_on && b_on)) + goto collide; + } + } + } + + collide: + font_kerning[(a << 8) | b] = overlap - 3; + } + + glGenTextures(1, &font_tex_id); + glBindTexture(GL_TEXTURE_2D, font_tex_id); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, FONT_PITCH, FONT_LINES, 0, GL_RED, GL_UNSIGNED_BYTE, font_data); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + unsigned int vertex = file2shader(GL_VERTEX_SHADER, "examples/tinyc.games/shaders/font.vert"); + unsigned int fragment = file2shader(GL_FRAGMENT_SHADER, "examples/tinyc.games/shaders/font.frag"); + + font_prog_id = glCreateProgram(); + glAttachShader(font_prog_id, vertex); + glAttachShader(font_prog_id, fragment); + glLinkProgram(font_prog_id); + check_program_errors(font_prog_id, "font"); + glDeleteShader(vertex); + glDeleteShader(fragment); + + glGenVertexArrays(1, &font_vao); + glGenBuffers(1, &font_vbo); +} + +void font_begin(int w, int h) +{ + font_screenw = w; + font_screenh = h; + font_buf_p = font_buf; +} + +void font_add_text(char *s, int inx, int iny, float height) +{ + int x = inx; + int y = iny; + float scale = height / 8.f; + + if (!scale) + scale = roundf((font_screenw < font_screenh ? font_screenw : font_screenh) / 250.f); + + for (; *s && font_buf_p < font_buf_limit; s++) + { + if (*s == '\n') + { + x = inx; + y += FONT_CH_H * scale; + continue; + } + + int c = *s - ' '; + if (c < 0) c = 0; + float u = (c * FONT_CH_W) % FONT_PITCH; + float v = (c / (FONT_PITCH / FONT_CH_W)) * FONT_CH_H; + + if (c) // don't render spaces + { + *font_buf_p++ = x; + *font_buf_p++ = y; + *font_buf_p++ = u / FONT_PITCH; + *font_buf_p++ = v / FONT_LINES; + + *font_buf_p++ = x + font_spacing[c] * scale; + *font_buf_p++ = y; + *font_buf_p++ = (u + font_spacing[c]) / FONT_PITCH; + *font_buf_p++ = v / FONT_LINES; + + *font_buf_p++ = x; + *font_buf_p++ = y + FONT_CH_H * scale; + *font_buf_p++ = u / FONT_PITCH; + *font_buf_p++ = (v + FONT_CH_H) / FONT_LINES; + + *font_buf_p++ = x + font_spacing[c] * scale; + *font_buf_p++ = y; + *font_buf_p++ = (u + font_spacing[c]) / FONT_PITCH; + *font_buf_p++ = v / FONT_LINES; + + *font_buf_p++ = x; + *font_buf_p++ = y + FONT_CH_H * scale; + *font_buf_p++ = u / FONT_PITCH; + *font_buf_p++ = (v + FONT_CH_H) / FONT_LINES; + + *font_buf_p++ = x + font_spacing[c] * scale; + *font_buf_p++ = y + FONT_CH_H * scale; + *font_buf_p++ = (u + font_spacing[c]) / FONT_PITCH; + *font_buf_p++ = (v + FONT_CH_H) / FONT_LINES; + } + + int kern = font_spacing[c] - 1 - font_kerning[(c << 8) | (s[1] - ' ')]; + x += kern * scale; + } +} + +void font_end(float r, float g, float b) +{ + int n = (font_buf_p - font_buf); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glUseProgram(font_prog_id); + + float near = -100.f; + float far = 100.f; + float x = 1.f / (font_screenw / 2.f); + float y = -1.f / (font_screenh / 2.f); + float z = -1.f / ((far - near) / 2.f); + float tz = -(far + near) / (far - near); + float ortho[] = { + x, 0, 0, 0, + 0, y, 0, 0, + 0, 0, z, 0, + -1, 1, tz, 1, + }; + glUniformMatrix4fv(glGetUniformLocation(font_prog_id, "proj"), 1, GL_FALSE, ortho); + + glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, font_tex_id); + glUniform1i(glGetUniformLocation(font_prog_id, "tex"), 2); + + glBindVertexArray(font_vao); + glBindBuffer(GL_ARRAY_BUFFER, font_vbo); + glBufferData(GL_ARRAY_BUFFER, n * sizeof *font_buf, font_buf, GL_STATIC_DRAW); + + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0); + glEnableVertexAttribArray(0); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glUniform3f(glGetUniformLocation(font_prog_id, "incolor"), 0, 0, 0); + glDrawArrays(GL_TRIANGLES, 0, n); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glUniform3f(glGetUniformLocation(font_prog_id, "incolor"), r, g, b); + glDrawArrays(GL_TRIANGLES, 0, n); +} diff --git a/examples/tinyc.games/shaders/font.frag b/examples/tinyc.games/shaders/font.frag new file mode 100644 index 0000000..88f55e9 --- /dev/null +++ b/examples/tinyc.games/shaders/font.frag @@ -0,0 +1,11 @@ +#version 330 core +in vec2 uv; +out vec4 color; + +uniform vec3 incolor; +uniform sampler2D tex; + +void main() +{ + color = vec4(incolor, texture(tex, uv).r); +} diff --git a/examples/tinyc.games/shaders/font.vert b/examples/tinyc.games/shaders/font.vert new file mode 100644 index 0000000..33f5208 --- /dev/null +++ b/examples/tinyc.games/shaders/font.vert @@ -0,0 +1,11 @@ +#version 330 core +layout (location = 0) in vec4 pos; +out vec2 uv; + +uniform mat4 proj; + +void main() +{ + gl_Position = proj * vec4(pos.xy, 0.0, 1.0); + uv = pos.zw; +} diff --git a/examples/tinyc.games/timer.c b/examples/tinyc.games/timer.c new file mode 100644 index 0000000..babdd8a --- /dev/null +++ b/examples/tinyc.games/timer.c @@ -0,0 +1,71 @@ +#include +#include +#include + +// You must X-define TIMER_NAMES before including this file, e.g: +// +// #define TIMER_NAMES X(create_hmap), X(update_world), X(update_player), +// + +#ifndef TIMER_NAMES +#define TIMER_NAMES +#endif + +#define TIMER(name) { \ + unsigned long long now = SDL_GetPerformanceCounter(); \ + if (!timer_then) timer_then = now; \ + timer_times[ timer_curr_id ] += now - timer_then; \ + timer_curr_id = timer_ ## name; \ + timer_then = now; \ +} + +// FIXME: push id onto stack if re-entering +#define TIMECALL(f, args) { \ + unsigned long long now = SDL_GetPerformanceCounter(); \ + if (!timer_then) timer_then = now; \ + timer_times[ timer_curr_id ] += now - timer_then; \ + timer_then = now; \ + (f)args; \ + now = SDL_GetPerformanceCounter(); \ + timer_times[ timer_ ## f ] += now - timer_then; \ + timer_curr_id = timer_; \ + timer_then = now; \ +} + +enum timernames { + #define X(x) timer_ ## x + TIMER_NAMES + #undef X + timer_ +}; + +char timernamesprint[][80] = { + #define X(x) #x + TIMER_NAMES + #undef X + "uncounted" +}; + +int timer_curr_id = timer_; +unsigned long long timer_then = 0; +unsigned long long timer_times[timer_ + 1] = { 0 }; + +void timer_print(char *buf, size_t n, bool show_all) +{ + char *p = buf; + int i = 0; + unsigned long long sum = 0; + unsigned long long freq = SDL_GetPerformanceFrequency(); + + for (i = 0; i <= timer_; i++) + sum += timer_times[i]; + + for (i = 0; i <= timer_; i++) + { + float secs = (float)timer_times[i] / (float)freq; + float pct = 100.f * (float)timer_times[i] / sum; + if ((show_all && secs > 0.f) || pct >= 0.1f || secs >= 0.01f) + p += snprintf(p, n - (p-buf), + "%6.1f %2.0f%% %s\n", secs, pct, timernamesprint[i]); + } +} diff --git a/examples/tinyc.games/utils.c b/examples/tinyc.games/utils.c new file mode 100644 index 0000000..01a20f4 --- /dev/null +++ b/examples/tinyc.games/utils.c @@ -0,0 +1,67 @@ +#pragma once + +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#define SWAP(a,b) {int *x = &(a); int *y = &(b); int t = *x; *x = *y; *y = t;} +#define CLAMP(lo,x,hi) ((x) < (lo) ? (lo) : (x) > (hi) ? (hi) : (x)) + +int check_shader_errors(GLuint shader, char *name) +{ + GLint success; + GLchar log[1024]; + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (success) return 0; + glGetShaderInfoLog(shader, 1024, NULL, log); + fprintf(stderr, "ERROR in %s shader program: %s\n", name, log); + exit(1); + return 1; +} + +int check_program_errors(GLuint shader, char *name) +{ + GLint success; + GLchar log[1024]; + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if (success) return 0; + glGetProgramInfoLog(shader, 1024, NULL, log); + fprintf(stderr, "ERROR in %s shader: %s\n", name, log); + exit(1); + return 1; +} + +// please free() the returned string +char *file2str(char *filename) +{ + FILE *f; + + #if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&f, filename, "r")) + f = NULL; + #else + f = fopen(filename, "r"); + #endif + + if (!f) goto bad; + fseek(f, 0, SEEK_END); + size_t sz = ftell(f); + rewind(f); + char *buf = calloc(sz + 1, sizeof *buf); + if (fread(buf, 1, sz, f) != sz) goto bad; + fclose(f); + return buf; + + bad: + fprintf(stderr, "Failed to open/read %s\n", filename); + return NULL; +} + +unsigned int file2shader(unsigned int type, char *filename) +{ + char *code = file2str(filename); + unsigned int id = glCreateShader(type); + glShaderSource(id, 1, (const char *const *)&code, NULL); + glCompileShader(id); + check_shader_errors(id, filename); + free(code); + return id; +} diff --git a/src/cjit.c b/src/cjit.c index de06af7..8518e54 100644 --- a/src/cjit.c +++ b/src/cjit.c @@ -17,6 +17,8 @@ * */ +#include + #include #include #include @@ -25,16 +27,13 @@ #include #include -#if !defined(_WIN32) -#include -#include -#include -#include -#endif - #include -#include +#include +// from win-compat.c +extern void win_compat_usleep(unsigned int microseconds); +extern ssize_t win_compat_getline(char **lineptr, size_t *n, FILE *stream); +extern bool get_winsdkpath(char *dst, size_t destlen); void handle_error(void *n, const char *m) { (void)n; @@ -85,7 +84,8 @@ const char cli_help[] = " -e fun\t entry point function (default 'main')\n" " -p pid\t write pid of executed program to file\n" " --live\t run interactive editor for live coding\n" - " --tgen\t create the runtime temporary dir and exit\n"; + " --temp\t create the runtime temporary dir and exit\n" + " --utar\t extract all contents from a USTAR tar.gz\n"; bool free_cjit(CJITState *CJIT) { if(CJIT->tmpdir) free(CJIT->tmpdir); @@ -125,7 +125,8 @@ int main(int argc, char **argv) { static ko_longopt_t longopts[] = { { "help", ko_no_argument, 100 }, { "live", ko_no_argument, 301 }, - { "tgen", ko_no_argument, 401 }, + { "temp", ko_no_argument, 401 }, + { "utar", ko_required_argument, 501 }, { NULL, 0, 0 } }; ketopt_t opt = KETOPT_INIT; @@ -137,17 +138,7 @@ int main(int argc, char **argv) { _err("CJIT %s by Dyne.org",VERSION); // _err("Running version: %s\n",VERSION); // version is always shown -#if defined(CJIT_BUILD_WIN) - _err("Build: WINDOWS"); -#elif defined(CJIT_BUILD_MUSL) - _err("Build: MUSL"); -#elif defined(CJIT_BUILD_OSX) - _err("Build: OSX"); -#elif defined(CJIT_BUILD_LINUX) - _err("Build: LINUX"); -#else - _err("Build: UNKNOWN"); -#endif + _err("Build: %s",PLATFORM); #if defined(CJIT_DEBUG_ASAN) _err("Debug: ASAN"); #elif defined(CJIT_DEBUG_GDB) @@ -206,6 +197,15 @@ int main(int argc, char **argv) { fprintf(stdout,"%s\n",CJIT.tmpdir); free_cjit(&CJIT); exit(0); + } else if (c == 501) { + free_cjit(&CJIT); + unsigned int len = 0; + _err("Extract contents of: %s",opt.arg); + char *targz = file_load(opt.arg, &len); + if(!targz) exit(1); + if(!len) exit(1); + muntargz_to_path(".",targz,len); + exit(0); } else if (c == '?') _err("unknown opt: -%c\n", opt.opt? opt.opt : ':'); else if (c == ':') _err("missing arg: -%c\n", opt.opt? opt.opt : ':'); @@ -231,6 +231,10 @@ int main(int argc, char **argv) { CJIT.dmon = false; #endif + // When using SDL2 these defines are needed + tcc_define_symbol(TCC,"SDL_DISABLE_IMMINTRIN_H",NULL); + tcc_define_symbol(TCC,"SDL_MAIN_HANDLED",NULL); + ////////////////////////////////////// // initialize the tmpdir for execution // from here onwards use goto endgame @@ -243,9 +247,6 @@ int main(int argc, char **argv) { // where is libtcc1.a found tcc_add_library_path(TCC, CJIT.tmpdir); -#if defined(_WIN32) - tcc_add_library_path(TCC, "C:\\Windows\\System32"); -#endif // tcc_set_lib_path(TCC,tmpdir); // this overrides all? // set output in memory for just in time execution @@ -255,6 +256,34 @@ int main(int argc, char **argv) { tcc_add_libc_symbols(TCC); #endif + tcc_add_sysinclude_path(TCC, CJIT.tmpdir); + tcc_add_sysinclude_path(TCC, "."); + tcc_add_library_path(TCC, "."); +#if defined(_WIN32) + { + // windows system32 libraries + //tcc_add_library_path(TCC, "C:\\Windows\\System32") + // 64bit + tcc_add_library_path(TCC, "C:\\Windows\\SysWOW64"); + // tinycc win32 headers + char *tpath = malloc(strlen(CJIT.tmpdir)+32); + strcpy(tpath,CJIT.tmpdir); + strcat(tpath,"/tinycc_win32/winapi"); + tcc_add_sysinclude_path(TCC, tpath); + free(tpath); + // windows SDK headers + char *sdkpath = malloc(512); + if( get_winsdkpath(sdkpath,511) ) { + int pathend = strlen(sdkpath); + strcpy(&sdkpath[pathend],"\\um"); // um/GL + tcc_add_sysinclude_path(TCC, sdkpath); + strcpy(&sdkpath[pathend],"\\shared"); // winapifamili.h etc. + tcc_add_sysinclude_path(TCC, sdkpath); + } + free(sdkpath); + } +#endif + if (argc == 0 ) { _err("No input file: live mode!"); live_mode = true; @@ -352,11 +381,7 @@ int main(int argc, char **argv) { // number of args at the left hand of arg separator, or all of them int right_args = argc-left_args+1;//arg_separator? argc-arg_separator : 0; char **right_argv = &argv[left_args-1];//arg_separator?&argv[arg_separator]:0 -#if !defined(_WIN32) - res = cjit_exec_fork(TCC, &CJIT, entry, right_args, right_argv); -#else - res = cjit_exec_win(TCC, &CJIT, entry, right_args, right_argv); -#endif + res = cjit_exec(TCC, &CJIT, entry, right_args, right_argv); endgame: // free TCC diff --git a/src/cjit.h b/src/cjit.h index f5fd81c..8db4fcc 100644 --- a/src/cjit.h +++ b/src/cjit.h @@ -1,10 +1,30 @@ +/* CJIT https://dyne.org/cjit + * + * Copyright (C) 2024 Dyne.org foundation + * + * 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 3 of the License, 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, see . + * + */ + #ifndef _CJIT_H_ #define _CJIT_H_ +#include #include #include -// passed to cjit_exec_fork with CJIT execution parameters +// passed to cjit_exec with CJIT execution parameters struct CJITState { char *tmpdir; char *write_pid; // filename to write the pid of execution @@ -13,50 +33,31 @@ struct CJITState { }; typedef struct CJITState CJITState; -extern bool free_cjit(CJITState *CJIT); - - // from embedded.c - generated at build time extern bool extract_embeddings(CJITState *CJIT); +// implemented in repl.c +extern int cjit_exec(TCCState *TCC, CJITState *CJIT, + const char *ep, int argc, char **argv); + +extern bool free_cjit(CJITState *CJIT); + ///////////// // from file.c extern int detect_bom(const char *filename); extern long file_size(const char *filename); -extern char* file_load(const char *filename); +extern char* file_load(const char *filename, unsigned int *len); extern char *load_stdin(); extern char* dir_load(const char *path); extern bool write_to_file(const char *path, const char *filename, const char *buf, unsigned int len); - -#if defined(_WIN32) -bool win32_mkdtemp(CJITState *CJIT); -// from win-compat.c -extern void win_compat_usleep(unsigned int microseconds); -extern ssize_t win_compat_getline(char **lineptr, size_t *n, FILE *stream); -#else -bool posix_mkdtemp(CJITState *CJIT); -#endif // from io.c extern void _out(const char *fmt, ...); extern void _err(const char *fmt, ...); -// from repl.c -#if defined(_WIN32) -extern int cjit_exec_win(TCCState *TCC, CJITState *CJIT, - const char *ep, int argc, char **argv); -#else -extern int cjit_exec_fork(TCCState *TCC, CJITState *CJIT, - const char *ep, int argc, char **argv); -#endif + extern int cjit_cli_tty(TCCState *TCC); #ifdef KILO_SUPPORTED extern int cjit_cli_kilo(TCCState *TCC); #endif -// from embed-dmon.c -extern char *lib_dmon_dmon_extra_h; -extern unsigned int lib_dmon_dmon_extra_h_len; -extern char *lib_dmon_dmon_h; -extern unsigned int lib_dmon_dmon_h_len; -///////////// #endif diff --git a/src/exec-headers.c b/src/exec-headers.c deleted file mode 100644 index fc91ad4..0000000 --- a/src/exec-headers.c +++ /dev/null @@ -1,132 +0,0 @@ -/* CJIT https://dyne.org/cjit - * - * Copyright (C) 2024 Dyne.org foundation - * - * 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 3 of the License, 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, see . - * - */ - -#include - -// from tinycc internal lib -#include - -// from file.c -extern bool write_to_file(char *path, char *filename, char *buf, unsigned int len); - -bool gen_exec_headers(char *tmpdir) { - if(! write_to_file(tmpdir,"libtcc1.a",(char*)&libtcc1,libtcc1_len) ) - return(false); - -#if defined(LIBC_MUSL) - if(! write_to_file(tmpdir,"libc.so",(char*)&musl_libc,musl_libc_len) ) - return(false); -#endif - - if(!write_to_file(tmpdir,"float.h",(char*)&lib_tinycc_include_float_h,lib_tinycc_include_float_h_len)) return(false); - if(!write_to_file(tmpdir,"stdalign.h",(char*)&lib_tinycc_include_stdalign_h,lib_tinycc_include_stdalign_h_len)) return(false); - if(!write_to_file(tmpdir,"stdarg.h",(char*)&lib_tinycc_include_stdarg_h,lib_tinycc_include_stdarg_h_len)) return(false); - if(!write_to_file(tmpdir,"stdatomic.h",(char*)&lib_tinycc_include_stdatomic_h,lib_tinycc_include_stdatomic_h_len)) return(false); - if(!write_to_file(tmpdir,"stdbool.h",(char*)&lib_tinycc_include_stdbool_h,lib_tinycc_include_stdbool_h_len)) return(false); - if(!write_to_file(tmpdir,"stddef.h",(char*)&lib_tinycc_include_stddef_h,lib_tinycc_include_stddef_h_len)) return(false); - if(!write_to_file(tmpdir,"stdnoreturn.h",(char*)&lib_tinycc_include_stdnoreturn_h,lib_tinycc_include_stdnoreturn_h_len)) return(false); - if(!write_to_file(tmpdir,"tccdefs.h",(char*)&lib_tinycc_include_tccdefs_h,lib_tinycc_include_tccdefs_h_len)) return(false); - if(!write_to_file(tmpdir,"tgmath.h",(char*)&lib_tinycc_include_tgmath_h,lib_tinycc_include_tgmath_h_len)) return(false); - if(!write_to_file(tmpdir,"varargs.h",(char*)&lib_tinycc_include_varargs_h,lib_tinycc_include_varargs_h_len)) return(false); -#if defined(_WIN32) - if(!write_to_file(tmpdir,"assert.h",(char*)&lib_tinycc_win32_include_assert_h,lib_tinycc_win32_include_assert_h_len)) return(false); - if(!write_to_file(tmpdir,"conio.h",(char*)&lib_tinycc_win32_include_conio_h,lib_tinycc_win32_include_conio_h_len)) return(false); - if(!write_to_file(tmpdir,"ctype.h",(char*)&lib_tinycc_win32_include_ctype_h,lib_tinycc_win32_include_ctype_h_len)) return(false); - if(!write_to_file(tmpdir,"direct.h",(char*)&lib_tinycc_win32_include_direct_h,lib_tinycc_win32_include_direct_h_len)) return(false); - if(!write_to_file(tmpdir,"dirent.h",(char*)&lib_tinycc_win32_include_dirent_h,lib_tinycc_win32_include_dirent_h_len)) return(false); - if(!write_to_file(tmpdir,"dir.h",(char*)&lib_tinycc_win32_include_dir_h,lib_tinycc_win32_include_dir_h_len)) return(false); - if(!write_to_file(tmpdir,"dos.h",(char*)&lib_tinycc_win32_include_dos_h,lib_tinycc_win32_include_dos_h_len)) return(false); - if(!write_to_file(tmpdir,"errno.h",(char*)&lib_tinycc_win32_include_errno_h,lib_tinycc_win32_include_errno_h_len)) return(false); - if(!write_to_file(tmpdir,"excpt.h",(char*)&lib_tinycc_win32_include_excpt_h,lib_tinycc_win32_include_excpt_h_len)) return(false); - if(!write_to_file(tmpdir,"fcntl.h",(char*)&lib_tinycc_win32_include_fcntl_h,lib_tinycc_win32_include_fcntl_h_len)) return(false); - if(!write_to_file(tmpdir,"fenv.h",(char*)&lib_tinycc_win32_include_fenv_h,lib_tinycc_win32_include_fenv_h_len)) return(false); - if(!write_to_file(tmpdir,"inttypes.h",(char*)&lib_tinycc_win32_include_inttypes_h,lib_tinycc_win32_include_inttypes_h_len)) return(false); - if(!write_to_file(tmpdir,"io.h",(char*)&lib_tinycc_win32_include_io_h,lib_tinycc_win32_include_io_h_len)) return(false); - if(!write_to_file(tmpdir,"iso646.h",(char*)&lib_tinycc_win32_include_iso646_h,lib_tinycc_win32_include_iso646_h_len)) return(false); - if(!write_to_file(tmpdir,"limits.h",(char*)&lib_tinycc_win32_include_limits_h,lib_tinycc_win32_include_limits_h_len)) return(false); - if(!write_to_file(tmpdir,"locale.h",(char*)&lib_tinycc_win32_include_locale_h,lib_tinycc_win32_include_locale_h_len)) return(false); - if(!write_to_file(tmpdir,"malloc.h",(char*)&lib_tinycc_win32_include_malloc_h,lib_tinycc_win32_include_malloc_h_len)) return(false); - if(!write_to_file(tmpdir,"math.h",(char*)&lib_tinycc_win32_include_math_h,lib_tinycc_win32_include_math_h_len)) return(false); - if(!write_to_file(tmpdir,"mem.h",(char*)&lib_tinycc_win32_include_mem_h,lib_tinycc_win32_include_mem_h_len)) return(false); - if(!write_to_file(tmpdir,"memory.h",(char*)&lib_tinycc_win32_include_memory_h,lib_tinycc_win32_include_memory_h_len)) return(false); - if(!write_to_file(tmpdir,"_mingw.h",(char*)&lib_tinycc_win32_include__mingw_h,lib_tinycc_win32_include__mingw_h_len)) return(false); - if(!write_to_file(tmpdir,"process.h",(char*)&lib_tinycc_win32_include_process_h,lib_tinycc_win32_include_process_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\conio_s.h",(char*)&lib_tinycc_win32_include_sec_api_conio_s_h,lib_tinycc_win32_include_sec_api_conio_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\crtdbg_s.h",(char*)&lib_tinycc_win32_include_sec_api_crtdbg_s_h,lib_tinycc_win32_include_sec_api_crtdbg_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\io_s.h",(char*)&lib_tinycc_win32_include_sec_api_io_s_h,lib_tinycc_win32_include_sec_api_io_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\mbstring_s.h",(char*)&lib_tinycc_win32_include_sec_api_mbstring_s_h,lib_tinycc_win32_include_sec_api_mbstring_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\search_s.h",(char*)&lib_tinycc_win32_include_sec_api_search_s_h,lib_tinycc_win32_include_sec_api_search_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\stdio_s.h",(char*)&lib_tinycc_win32_include_sec_api_stdio_s_h,lib_tinycc_win32_include_sec_api_stdio_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\stdlib_s.h",(char*)&lib_tinycc_win32_include_sec_api_stdlib_s_h,lib_tinycc_win32_include_sec_api_stdlib_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\stralign_s.h",(char*)&lib_tinycc_win32_include_sec_api_stralign_s_h,lib_tinycc_win32_include_sec_api_stralign_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\string_s.h",(char*)&lib_tinycc_win32_include_sec_api_string_s_h,lib_tinycc_win32_include_sec_api_string_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\sys\\timeb_s.h",(char*)&lib_tinycc_win32_include_sec_api_sys_timeb_s_h,lib_tinycc_win32_include_sec_api_sys_timeb_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\tchar_s.h",(char*)&lib_tinycc_win32_include_sec_api_tchar_s_h,lib_tinycc_win32_include_sec_api_tchar_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\time_s.h",(char*)&lib_tinycc_win32_include_sec_api_time_s_h,lib_tinycc_win32_include_sec_api_time_s_h_len)) return(false); - if(!write_to_file(tmpdir,"sec_api\\wchar_s.h",(char*)&lib_tinycc_win32_include_sec_api_wchar_s_h,lib_tinycc_win32_include_sec_api_wchar_s_h_len)) return(false); - if(!write_to_file(tmpdir,"setjmp.h",(char*)&lib_tinycc_win32_include_setjmp_h,lib_tinycc_win32_include_setjmp_h_len)) return(false); - if(!write_to_file(tmpdir,"share.h",(char*)&lib_tinycc_win32_include_share_h,lib_tinycc_win32_include_share_h_len)) return(false); - if(!write_to_file(tmpdir,"signal.h",(char*)&lib_tinycc_win32_include_signal_h,lib_tinycc_win32_include_signal_h_len)) return(false); - if(!write_to_file(tmpdir,"stdint.h",(char*)&lib_tinycc_win32_include_stdint_h,lib_tinycc_win32_include_stdint_h_len)) return(false); - if(!write_to_file(tmpdir,"stdio.h",(char*)&lib_tinycc_win32_include_stdio_h,lib_tinycc_win32_include_stdio_h_len)) return(false); - if(!write_to_file(tmpdir,"stdlib.h",(char*)&lib_tinycc_win32_include_stdlib_h,lib_tinycc_win32_include_stdlib_h_len)) return(false); - if(!write_to_file(tmpdir,"string.h",(char*)&lib_tinycc_win32_include_string_h,lib_tinycc_win32_include_string_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\fcntl.h",(char*)&lib_tinycc_win32_include_sys_fcntl_h,lib_tinycc_win32_include_sys_fcntl_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\file.h",(char*)&lib_tinycc_win32_include_sys_file_h,lib_tinycc_win32_include_sys_file_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\locking.h",(char*)&lib_tinycc_win32_include_sys_locking_h,lib_tinycc_win32_include_sys_locking_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\stat.h",(char*)&lib_tinycc_win32_include_sys_stat_h,lib_tinycc_win32_include_sys_stat_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\timeb.h",(char*)&lib_tinycc_win32_include_sys_timeb_h,lib_tinycc_win32_include_sys_timeb_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\time.h",(char*)&lib_tinycc_win32_include_sys_time_h,lib_tinycc_win32_include_sys_time_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\types.h",(char*)&lib_tinycc_win32_include_sys_types_h,lib_tinycc_win32_include_sys_types_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\unistd.h",(char*)&lib_tinycc_win32_include_sys_unistd_h,lib_tinycc_win32_include_sys_unistd_h_len)) return(false); - if(!write_to_file(tmpdir,"sys\\utime.h",(char*)&lib_tinycc_win32_include_sys_utime_h,lib_tinycc_win32_include_sys_utime_h_len)) return(false); - if(!write_to_file(tmpdir,"tcc\\tcc_libm.h",(char*)&lib_tinycc_win32_include_tcc_tcc_libm_h,lib_tinycc_win32_include_tcc_tcc_libm_h_len)) return(false); - if(!write_to_file(tmpdir,"tchar.h",(char*)&lib_tinycc_win32_include_tchar_h,lib_tinycc_win32_include_tchar_h_len)) return(false); - if(!write_to_file(tmpdir,"time.h",(char*)&lib_tinycc_win32_include_time_h,lib_tinycc_win32_include_time_h_len)) return(false); - if(!write_to_file(tmpdir,"uchar.h",(char*)&lib_tinycc_win32_include_uchar_h,lib_tinycc_win32_include_uchar_h_len)) return(false); - if(!write_to_file(tmpdir,"vadefs.h",(char*)&lib_tinycc_win32_include_vadefs_h,lib_tinycc_win32_include_vadefs_h_len)) return(false); - if(!write_to_file(tmpdir,"values.h",(char*)&lib_tinycc_win32_include_values_h,lib_tinycc_win32_include_values_h_len)) return(false); - if(!write_to_file(tmpdir,"wchar.h",(char*)&lib_tinycc_win32_include_wchar_h,lib_tinycc_win32_include_wchar_h_len)) return(false); - if(!write_to_file(tmpdir,"wctype.h",(char*)&lib_tinycc_win32_include_wctype_h,lib_tinycc_win32_include_wctype_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\basetsd.h",(char*)&lib_tinycc_win32_include_winapi_basetsd_h,lib_tinycc_win32_include_winapi_basetsd_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\basetyps.h",(char*)&lib_tinycc_win32_include_winapi_basetyps_h,lib_tinycc_win32_include_winapi_basetyps_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\guiddef.h",(char*)&lib_tinycc_win32_include_winapi_guiddef_h,lib_tinycc_win32_include_winapi_guiddef_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\poppack.h",(char*)&lib_tinycc_win32_include_winapi_poppack_h,lib_tinycc_win32_include_winapi_poppack_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\pshpack1.h",(char*)&lib_tinycc_win32_include_winapi_pshpack1_h,lib_tinycc_win32_include_winapi_pshpack1_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\pshpack2.h",(char*)&lib_tinycc_win32_include_winapi_pshpack2_h,lib_tinycc_win32_include_winapi_pshpack2_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\pshpack4.h",(char*)&lib_tinycc_win32_include_winapi_pshpack4_h,lib_tinycc_win32_include_winapi_pshpack4_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\pshpack8.h",(char*)&lib_tinycc_win32_include_winapi_pshpack8_h,lib_tinycc_win32_include_winapi_pshpack8_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\qos.h",(char*)&lib_tinycc_win32_include_winapi_qos_h,lib_tinycc_win32_include_winapi_qos_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winbase.h",(char*)&lib_tinycc_win32_include_winapi_winbase_h,lib_tinycc_win32_include_winapi_winbase_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\wincon.h",(char*)&lib_tinycc_win32_include_winapi_wincon_h,lib_tinycc_win32_include_winapi_wincon_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\windef.h",(char*)&lib_tinycc_win32_include_winapi_windef_h,lib_tinycc_win32_include_winapi_windef_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\windows.h",(char*)&lib_tinycc_win32_include_winapi_windows_h,lib_tinycc_win32_include_winapi_windows_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winerror.h",(char*)&lib_tinycc_win32_include_winapi_winerror_h,lib_tinycc_win32_include_winapi_winerror_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\wingdi.h",(char*)&lib_tinycc_win32_include_winapi_wingdi_h,lib_tinycc_win32_include_winapi_wingdi_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winnls.h",(char*)&lib_tinycc_win32_include_winapi_winnls_h,lib_tinycc_win32_include_winapi_winnls_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winnt.h",(char*)&lib_tinycc_win32_include_winapi_winnt_h,lib_tinycc_win32_include_winapi_winnt_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winreg.h",(char*)&lib_tinycc_win32_include_winapi_winreg_h,lib_tinycc_win32_include_winapi_winreg_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winsock2.h",(char*)&lib_tinycc_win32_include_winapi_winsock2_h,lib_tinycc_win32_include_winapi_winsock2_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winuser.h",(char*)&lib_tinycc_win32_include_winapi_winuser_h,lib_tinycc_win32_include_winapi_winuser_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\winver.h",(char*)&lib_tinycc_win32_include_winapi_winver_h,lib_tinycc_win32_include_winapi_winver_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\ws2ipdef.h",(char*)&lib_tinycc_win32_include_winapi_ws2ipdef_h,lib_tinycc_win32_include_winapi_ws2ipdef_h_len)) return(false); - if(!write_to_file(tmpdir,"winapi\\ws2tcpip.h",(char*)&lib_tinycc_win32_include_winapi_ws2tcpip_h,lib_tinycc_win32_include_winapi_ws2tcpip_h_len)) return(false); -#endif - return(true); -} diff --git a/src/file.c b/src/file.c index c12a5c2..8b173ec 100644 --- a/src/file.c +++ b/src/file.c @@ -17,6 +17,8 @@ * */ +#include + #include #include #include @@ -28,26 +30,6 @@ #include #include // _GNU_SOURCE - -#if defined(_WIN32) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -#include -#include -#include -#pragma comment(lib, "rpcrt4.lib") -#pragma comment(lib, "shlwapi.lib") -#else -#include -#include -#include -#ifndef O_BINARY -# define O_BINARY 0 -#endif -#endif - -#include extern void _err(const char *fmt, ...); // generated in embedded.c @@ -140,7 +122,7 @@ long file_size(const char *filename) { return length; } -char* file_load(const char *filename) { +char* file_load(const char *filename, unsigned int *len) { long length = file_size(filename); if (length == -1) { return NULL; @@ -163,13 +145,14 @@ char* file_load(const char *filename) { fread(contents, 1, length, file); contents[length] = '\0'; // Null-terminate the string + *len = length; fclose(file); return contents; } char *load_stdin() { -#if defined(_WIN32) +#if defined(WINDOWS) return NULL; #else char *code = NULL; @@ -202,7 +185,7 @@ bool write_to_file(const char *path, const char *filename, const char *buf, unsi FILE *fd; size_t written; char fullpath[256]; -#if defined(_WIN32) +#if defined(WINDOWS) snprintf(fullpath,255,"%s\\%s",path,filename); #else snprintf(fullpath,255,"%s/%s",path,filename); @@ -226,7 +209,7 @@ bool write_to_file(const char *path, const char *filename, const char *buf, unsi static int rm_ftw(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb) { -#if !defined(_WIN32) +#if !defined(WINDOWS) if(remove(pathname) < 0) { _err("Error: remove path %s",pathname); _err("%s",strerror(errno)); @@ -258,7 +241,7 @@ bool rm_recursive(char *path) { return true; } -#if !defined(_WIN32) +#if !defined(WINDOWS) static char *full_content = NULL; @@ -268,11 +251,12 @@ static int file_load_ftw(const char *pathname, int type, struct FTW *ftwb) { FILE *fd; char *content = NULL; + unsigned int len; if (type == FTW_F) { size_t pathlen = strlen(pathname); if (pathname[pathlen-1] == 'c' && pathname[pathlen-2] == '.') { - content = file_load(pathname); + content = file_load(pathname, &len); if (content == NULL) { _err("Error: file_load %s",pathname); return -1; @@ -331,7 +315,7 @@ char *dir_load(const char *path) static bool dir_exists(CJITState *CJIT, const char *path) { CJIT->tmpdir = malloc(strlen(path)+1); strcpy(CJIT->tmpdir, path); -#if defined(_WIN32) +#if defined(WINDOWS) DWORD attributes = GetFileAttributes(path); if (attributes == INVALID_FILE_ATTRIBUTES) { // The path does not exist @@ -362,7 +346,7 @@ static bool dir_exists(CJITState *CJIT, const char *path) { bool cjit_mkdtemp(CJITState *CJIT) { bool res; -#if defined(_WIN32) +#if defined(WINDOWS) static char tempDir[MAX_PATH]; char tempPath[MAX_PATH]; char filename [64]; diff --git a/src/muntar.c b/src/muntar.c index e90d3b5..6d556be 100644 --- a/src/muntar.c +++ b/src/muntar.c @@ -157,13 +157,14 @@ static int mtar_rewind(mtar_t *tar) { /////////////////// -#if defined(_WIN32) +#if defined(_WIN32) || defined(WINDOWS) #include #define makedir(path) CreateDirectory(path, NULL) #else #include #define makedir(path) mkdir(path,0755) #endif + // used by extract_embeddings(char *tmpdir) int muntar_to_path(const char *path, const uint8_t *buf, const unsigned int len) { diff --git a/src/platforms.h b/src/platforms.h new file mode 100644 index 0000000..af83b04 --- /dev/null +++ b/src/platforms.h @@ -0,0 +1,78 @@ +// Platform detection +#ifndef __PLATFORMS_H__ +#define __PLATFORMS_H__ + +// deactivate warnings about re-definitions +#pragma GCC system_header + +#if defined(_WIN32) || defined(__COSMOPOLITAN__) +#define WINDOWS +#define PLATFORM "Windows" +#endif + +#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ + #define POSIX + #define PLATFORM "Posix" + #if defined(__unix__) + #define UNIX + #define PLATFORM "UNIX" + #endif + #if defined(__linux__) + #define LINUX + #define PLATFORM "GNU/Linux" + #endif + #if defined(__APPLE__) + #define APPLE + #define PLATFORM "Apple/OSX" + #endif + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define BSD + #define PLATFORM "BSD" + #endif + #if defined(__ANDROID__) + #define ANDROID + #define PLATFORM "Android" + #endif + #if defined(__EMSCRIPTEN__) + #define EMSCRIPTEN + #define PLATFORM "WASM" + #endif + #if defined(__BEOS__) || defined(__HAIKU__) + #define BEOS + #define PLATFORM "BEOS" + #endif + #if defined(__HAIKU__) + #define HAIKU + #define PLATFORM "Haiku" + #endif +#endif +#if !defined(PLATFORM) + #define PLATFORM "Unknown" +#endif + +#if defined(WINDOWS) +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +#include +#include +#include +#pragma comment(lib, "rpcrt4.lib") +#pragma comment(lib, "shlwapi.lib") +#else +#include +#include +#include +#ifndef O_BINARY +# define O_BINARY 0 +#endif +#endif + +#if defined(LINUX) || defined(POSIX) || defined(UNIX) || defined(BSD) || defined(ANDROID) || defined(EMSCRIPTEN) +#include +#include +#include +#include +#endif + +#endif diff --git a/src/repl.c b/src/repl.c index 2e5d9b7..8e2e859 100644 --- a/src/repl.c +++ b/src/repl.c @@ -25,7 +25,7 @@ #include -#if !defined(_WIN32) +#if !defined(WINDOWS) #include #include #include @@ -36,8 +36,8 @@ extern void _out(const char *fmt, ...); extern void _err(const char *fmt, ...); -#if defined(_WIN32) -int cjit_exec_win(TCCState *TCC, CJITState *CJIT, const char *ep, int argc, char **argv) { +int cjit_exec(TCCState *TCC, CJITState *CJIT, const char *ep, int argc, char **argv) { +#if defined(WINDOWS) int res = 1; int (*_ep)(int, char**); _ep = tcc_get_symbol(TCC, ep); @@ -59,10 +59,9 @@ int cjit_exec_win(TCCState *TCC, CJITState *CJIT, const char *ep, int argc, char // _err("Execution start\n---"); res = _ep(argc, argv); return(res); -} -#else // _WIN32 -int cjit_exec_fork(TCCState *TCC, CJITState *CJIT, const char *ep, int argc, char **argv) { +#else // we assume anything else but WINDOWS has fork() + pid_t pid; int res = 1; int (*_ep)(int, char**); @@ -110,8 +109,8 @@ int cjit_exec_fork(TCCState *TCC, CJITState *CJIT, const char *ep, int argc, cha } } return res; +#endif // cjit_exec with fork() } -#endif // _WIN32 #ifdef KILO_SUPPORTED @@ -356,9 +355,9 @@ int cjit_cli_tty(TCCState *TCC) { return 2; } strcpy(code, intro); -#if defined(_WIN32) +#if defined(WINDOWS) _err("Missing source code argument"); -#else // _WIN32 +#else // WINDOWS while (1) { printf("cjit> "); fflush(stdout); @@ -395,11 +394,8 @@ int cjit_cli_tty(TCCState *TCC) { _err("Running code\n"); _err("-----------------------------------\n"); #endif // VERBOSE_CLI -#if !defined(_WIN32) - res = cjit_exec_fork(TCC, NULL, "main", 0, NULL); -#else // _WIN32 - res = cjit_exec_win(TCC, NULL, "main", 0, NULL); -#endif // _WIN32 + + res = cjit_exec(TCC, NULL, "main", 0, NULL); free(code); code = NULL; break; @@ -414,6 +410,6 @@ int cjit_cli_tty(TCCState *TCC) { free(line); line = NULL; } -#endif // _WIN32 +#endif // WINDOWS return res; } diff --git a/src/win-compat.c b/src/win-compat.c index 7573f35..a386f25 100644 --- a/src/win-compat.c +++ b/src/win-compat.c @@ -1,10 +1,12 @@ #include #include #include +#include # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif #include +#include // Define the usleep function for Windows void win_compat_usleep(unsigned int microseconds) { @@ -53,3 +55,49 @@ ssize_t win_compat_getline(char **lineptr, size_t *n, FILE *stream) { (*lineptr)[pos] = '\0'; return pos; } + +bool get_winsdkpath(char *dst, size_t destlen) { + HKEY hKey; + LPDWORD len = (LPDWORD)destlen; + LONG lRes = RegOpenKeyEx + (HKEY_LOCAL_MACHINE, + "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", + 0, KEY_READ, &hKey); + if (lRes != ERROR_SUCCESS) { + fprintf(stderr,"Failed to open registry key. Error: %ld\n", lRes); + return false; + } + lRes = RegQueryValueEx(hKey, "KitsRoot10", + NULL, NULL, dst, &len); + RegCloseKey(hKey); + if (lRes != ERROR_SUCCESS) { + fprintf(stderr,"Failed to query registry value. Error: %ld\n",lRes); + RegCloseKey(hKey); + return false; + } + // Append the Include path + strcat(dst, "Include\\"); + // Find the correct SDK version directory + WIN32_FIND_DATA findFileData; + char *tmp = malloc(strlen(dst)+4); + strcpy(tmp,dst); strcat(tmp,"*"); + HANDLE hFind = FindFirstFile(tmp, &findFileData); + free(tmp); + if (hFind == INVALID_HANDLE_VALUE) { + printf("Failed to find any SDK version directories.\n"); + return false; + } + do { + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (_tcscmp(findFileData.cFileName, ".") != 0 + && _tcscmp(findFileData.cFileName, "..") != 0) { + // Append the first valid directory found + strcat(dst, findFileData.cFileName); + break; + } + } + } while (FindNextFile(hFind, &findFileData) != 0); + FindClose(hFind); + // fprintf(stderr,"Windows SDK Path: %s\n", dst); + return(true); +} diff --git a/test/bats_setup b/test/bats_setup index e35fce5..28eaf01 100644 --- a/test/bats_setup +++ b/test/bats_setup @@ -14,7 +14,7 @@ setup() { >&2 echo "CJIT is not built, cannot run test suite" exit 1 } - rm -rf `${CJIT} --tgen` + rm -rf `${CJIT} --temp` TCC="${R}/lib/tinycc/tcc" [ -r "$TCC" ] || TCC="${R}/lib/tinycc/tcc.exe" }