From fabf5201578ce390587c3bcafa31fb8156a354e9 Mon Sep 17 00:00:00 2001 From: Macoy Madson Date: Fri, 12 Mar 2021 20:21:19 -0800 Subject: [PATCH] Puzzles are now loaded from binary format * Created PuzzleIO.cake for translating from the Decompression.cake text format to a binary format which is easy to load on PC and Android (especially Android). It should be much faster to load as a side-effect of this change * Fix bug in Main.cake where libSDL was actually resolving to the system SDL. This happened because the executable was being relocated, but the rpath wasn't being updated to reflect that * Added hacky comptime-conds so Main could be included in PuzzleIO executable without conflicting --- .gitignore | 2 + Android/app/jni/src/Android.mk | 2 +- Build.sh | 3 + src/Main.cake | 116 +++++++++---------------- src/PuzzleIO.cake | 149 +++++++++++++++++++++++++++++++++ 5 files changed, 196 insertions(+), 76 deletions(-) create mode 100644 src/PuzzleIO.cake diff --git a/.gitignore b/.gitignore index c03633d..62bc46a 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ test/data/Models/* kitty-gridlock decompression-test +generate-puzzles *.kra-autosave.kra .stfolder @@ -72,6 +73,7 @@ decompression-test # Generated from database data/puzzles.txt +data/puzzles.bin diff --git a/Android/app/jni/src/Android.mk b/Android/app/jni/src/Android.mk index 78d8b13..84d85b9 100644 --- a/Android/app/jni/src/Android.mk +++ b/Android/app/jni/src/Android.mk @@ -12,7 +12,7 @@ LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(SDL_PATH)/include $(LOCAL_PATH)/../../../../ LOCAL_CFLAGS += -Wno-parentheses-equality # Add your application source files here... -LOCAL_SRC_FILES := Main.cake.cpp Math.cake.cpp SDL.cake.cpp +LOCAL_SRC_FILES := Main.cake.cpp Math.cake.cpp SDL.cake.cpp PuzzleIO.cake.cpp LOCAL_SHARED_LIBRARIES := SDL2 diff --git a/Build.sh b/Build.sh index a6b1a30..3aa005d 100755 --- a/Build.sh +++ b/Build.sh @@ -10,7 +10,10 @@ CAKELISP=./Dependencies/cakelisp/bin/cakelisp # After relocation, this will be able to be simply . KITTY_DIR=../.. +# These both happen by Main.cake as a pre-build step, but you can do it manually here # Uncomment to generate data/puzzles.txt # $CAKELISP --execute $KITTY_DIR/src/Decompression.cake || exit $? +# Uncomment to generate data/puzzles.bin +# $CAKELISP --execute $KITTY_DIR/src/PuzzleIO.cake || exit $? $CAKELISP --execute $KITTY_DIR/src/Main.cake || exit $? diff --git a/src/Main.cake b/src/Main.cake index 42fef75..bcce222 100644 --- a/src/Main.cake +++ b/src/Main.cake @@ -1,5 +1,7 @@ (comptime-define-symbol 'Unix) -(comptime-define-symbol 'Kitty-Main) +(comptime-cond ('No-Kitty-Main) + (true + (comptime-define-symbol 'Kitty-Main))) ;; Until GameLib is relocatable, we will build from it; All comptime paths in this file are relative ;; to Dependencies/gamelib! @@ -11,6 +13,7 @@ "../../src") (import "SDL.cake" "Math.cake" ;; GameLib + "PuzzleIO.cake" ;; kitty-gridlock ;; "Decompression.cake" ;; kitty-gridlock ;; Not used for runtime game &comptime-only "Macros.cake" "BuildTools.cake") ;; cakelisp/runtime @@ -290,69 +293,13 @@ (return true)) -(defstruct-local puzzle-data +(defstruct puzzle-data num-moves char num-states int board ([] 36 char)) -(var g-puzzle-list (* puzzle-data) null) -(var g-num-puzzles int 0) - -(defun-local read-puzzles (&return bool) - (var puzzles-file (* FILE) (fopen (in-data-dir "puzzles.txt") "r")) - (unless puzzles-file - (SDL_Log "Failed to load puzzles. Did the pre-build step generate-puzzles-list fail? You can - also run Decompression.cake directly (see Build.sh)\n") - (return true)) - - ;; 48 = max length of a single line in puzzles .txt - (var line-buffer ([] 48 char) (array 0)) - (unless (fgets line-buffer (array-size line-buffer) puzzles-file) - (SDL_Log "Puzzle file malformed\n") (return false)) - (var num-puzzles int (atoi line-buffer)) - (SDL_Log "Reading %d puzzles\n" num-puzzles) - (when g-puzzle-list (free (type-cast g-puzzle-list (* void)))) - (set g-num-puzzles num-puzzles) - (set g-puzzle-list (type-cast (calloc g-num-puzzles (sizeof (type puzzle-data))) (* puzzle-data))) - - ;; Read puzzles line by line - (var current-puzzle (* puzzle-data) g-puzzle-list) - (while (and (fgets line-buffer (array-size line-buffer) puzzles-file) - (< (- current-puzzle g-puzzle-list) g-num-puzzles)) - (var num-moves int 0) - (var num-states int 0) - (var pieces-read bool false) - (var buffer ([] 37 char) (array 0)) - (var write-char (* char) buffer) - (var current-char (* (const char)) line-buffer) - (while (deref current-char) - (cond - ;; Space field delimiter - ((= (deref current-char) (space-hack)) - (cond - ((not num-moves) - (set num-moves (atoi buffer))) - (true ;; Copy pieces - ;; TODO Magic number - (memcpy (path current-puzzle > board) buffer 36) - (set pieces-read true))) - - ;; Reset buffer after space - (memset buffer 0 (sizeof buffer)) - (set write-char buffer)) - ((= (deref current-char) '\n') - (break)) - (true - (set (deref write-char) (deref current-char)) - (incr write-char))) - (incr current-char)) - (set (path current-puzzle > num-moves) num-moves) - (set num-states (atoi buffer)) - (set (path current-puzzle > num-states) num-states) - (incr current-puzzle)) - - (fclose puzzles-file) - (return (> g-num-puzzles 0))) +(var-global g-puzzle-list (* puzzle-data) null) +(var-global g-num-puzzles int 0) (defun-local game-piece-grid-position-to-screen-position (piece (* (const board-piece)) &return vec2) (var piece-position vec2 (array (type-cast (vec-x (path piece > grid-position)) float) @@ -679,6 +626,7 @@ ;; Main ;; +(comptime-cond ('Kitty-Main (defun main (num-args int args ([] (* char)) &return int) (SDL_Log "Kitty Gridlock\n\n Created by Macoy Madson .\n @@ -744,7 +692,7 @@ Rush Hour database from Michael Fogleman.\n\n") (sdl-print-time-delta start-load-ticks "Loading screen displayed") - (unless (read-puzzles) (return 1)) + (unless (read-puzzles-binary (in-data-dir "puzzles.bin")) (return 1)) (sdl-print-time-delta start-load-ticks "Puzzles loaded") (var background-texture (* SDL_Texture) (sdl-texture-from-bmp (in-data-dir "Board.bmp") @@ -810,8 +758,8 @@ Rush Hour database from Michael Fogleman.\n\n") (srand (type-cast (SDL_GetPerformanceCounter) int)) - (game-board-load "IBBxooIooLDDJAALooJoKEEMFFKooMGGHHHM") - ;; (game-board-load-next-puzzle) + ;; (game-board-load "IBBxooIooLDDJAALooJoKEEMFFKooMGGHHHM") + (game-board-load-next-puzzle) ;; ;; Game loop @@ -1074,7 +1022,8 @@ Rush Hour database from Michael Fogleman.\n\n") (when exit-reason (SDL_Log "Exiting. Reason: %s\n" exit-reason)) - (when g-puzzle-list (free (type-cast g-puzzle-list (* void)))) + (when g-puzzle-list (free (type-cast g-puzzle-list (* void))) + (set g-puzzle-list null)) (var i int 0) (while (< i (array-size textures-to-destroy)) @@ -1086,7 +1035,7 @@ Rush Hour database from Michael Fogleman.\n\n") (sdl-shutdown window) (return 0)) - +)) ;; ;; Macros and generators ;; @@ -1176,20 +1125,31 @@ Rush Hour database from Michael Fogleman.\n\n") ;; (defun-comptime generate-puzzles-list (manager (& ModuleManager) module (* Module) &return bool) ;; Already built? - (when (fileExists "../../data/puzzles.txt") + (when (fileExists "../../data/puzzles.bin") (Log "generate-puzzles-list: Puzzles list already built\n") (return true)) - (Log "generate-puzzles-list: Building puzzles list from database\n") + (unless (fileExists "../../data/puzzles.txt") + (Log "generate-puzzles-list: Building puzzles list from database\n") + (run-process-sequential-or + ;; Note that we're still relative to Dependencies/gamelib + ("./Dependencies/cakelisp/bin/cakelisp" "--execute" "../../src/Decompression.cake") + (Log "generate-puzzles-list: Failed to run Decompression for puzzle database reading\n") + (return false)) + (unless (fileExists "../../data/puzzles.txt") + (Log "generate-puzzles-list: Successfully executed Decompression, but didn't find + data/puzzles.txt. Are paths incorrect?\n") + (return false))) + (run-process-sequential-or ;; Note that we're still relative to Dependencies/gamelib - ("./Dependencies/cakelisp/bin/cakelisp" "--execute" "../../src/Decompression.cake") - (Log "generate-puzzles-list: Failed to run Decompression for puzzle database reading\n") + ("./Dependencies/cakelisp/bin/cakelisp" "--execute" "../../src/PuzzleIO.cake") + (Log "generate-puzzles-list: Failed to run PuzzleIO for puzzle database reading\n") (return false)) - (unless (fileExists "../../data/puzzles.txt") - (Log "generate-puzzles-list: Successfully executed Decompression, but didn't find - data/puzzles.txt. Are paths incorrect?\n") + (unless (fileExists "../../data/puzzles.bin") + (Log "generate-puzzles-list: Successfully executed PuzzleIO, but didn't find + data/puzzles.bin. Are paths incorrect?\n") (return false)) (return true)) @@ -1218,13 +1178,19 @@ This tool requires rsync to be installed.\n") (return true)) ;; Order matters here, because we want to copy the generated puzzles list to android -(add-compile-time-hook-module pre-build generate-puzzles-list) -(add-compile-time-hook-module pre-build copy-src-files-to-android) +(comptime-cond + ('Kitty-Main + (add-compile-time-hook-module pre-build generate-puzzles-list) + (add-compile-time-hook-module pre-build copy-src-files-to-android))) (module-use-sdl-build-options) +(comptime-cond ('Kitty-Main ;; Note that this executable still pulls .so files from Dependencies -(set-cakelisp-option executable-output "../../kitty-gridlock") +(set-cakelisp-option executable-output "../../kitty-gridlock"))) + +; This ensures we still find SDL even after we've relocated the executable +(add-library-runtime-search-directory "Dependencies/gamelib/Dependencies/SDL/buildSDLBuild/lib") ;; Use build label to make it easier to find in cakelisp_cache (add-build-config-label "Kitty") diff --git a/src/PuzzleIO.cake b/src/PuzzleIO.cake new file mode 100644 index 0000000..4d53222 --- /dev/null +++ b/src/PuzzleIO.cake @@ -0,0 +1,149 @@ +(comptime-define-symbol 'Unix) +(comptime-define-symbol 'No-Kitty-Main) + +(define-constant DATA_DIR "data/") + +;; Until GameLib is relocatable, we will build from it; All comptime paths in this file are relative +;; to Dependencies/gamelib! +;; (add-cakelisp-search-directory "Dependencies/gamelib/src") +;; (set-cakelisp-option cakelisp-src-dir "Dependencies/gamelib/Dependencies/cakelisp/src") +;; (add-cakelisp-search-directory "Dependencies/gamelib/Dependencies/cakelisp/runtime") +(add-cakelisp-search-directory "src" "Dependencies/cakelisp/runtime" + ;; kitty-gridlock src + "../../src") + +(import "SDL.cake" ;; GameLib + "Main.cake" ;; kitty-gridlock + &comptime-only "Macros.cake" "BuildTools.cake") ;; cakelisp/runtime + +(c-import "" "" + "SDL.h" "SDL_rwops.h") + +(defun-local read-puzzles-text (&return bool) + (var puzzles-file (* FILE) (fopen (in-data-dir "puzzles.txt") "r")) + (unless puzzles-file + (SDL_Log "Failed to load puzzles. Did the pre-build step generate-puzzles-list fail? You can + also run Decompression.cake directly (see Build.sh)\n") + (return true)) + + ;; 48 = max length of a single line in puzzles .txt + (var line-buffer ([] 48 char) (array 0)) + (unless (fgets line-buffer (array-size line-buffer) puzzles-file) + (SDL_Log "Puzzle file malformed\n") (return false)) + (var num-puzzles int (atoi line-buffer)) + (SDL_Log "Reading %d puzzles\n" num-puzzles) + (when g-puzzle-list (free (type-cast g-puzzle-list (* void)))) + (set g-num-puzzles num-puzzles) + (set g-puzzle-list (type-cast (calloc g-num-puzzles (sizeof (type puzzle-data))) (* puzzle-data))) + + ;; Read puzzles line by line + (var current-puzzle (* puzzle-data) g-puzzle-list) + (while (and (fgets line-buffer (array-size line-buffer) puzzles-file) + (< (- current-puzzle g-puzzle-list) g-num-puzzles)) + (var num-moves int 0) + (var num-states int 0) + (var pieces-read bool false) + (var buffer ([] 37 char) (array 0)) + (var write-char (* char) buffer) + (var current-char (* (const char)) line-buffer) + (while (deref current-char) + (cond + ;; Space field delimiter + ((= (deref current-char) (space-hack)) + (cond + ((not num-moves) + (set num-moves (atoi buffer))) + (true ;; Copy pieces + ;; TODO Magic number + (memcpy (path current-puzzle > board) buffer 36) + (set pieces-read true))) + + ;; Reset buffer after space + (memset buffer 0 (sizeof buffer)) + (set write-char buffer)) + ((= (deref current-char) '\n') + (break)) + (true + (set (deref write-char) (deref current-char)) + (incr write-char))) + (incr current-char)) + (set (path current-puzzle > num-moves) num-moves) + (set num-states (atoi buffer)) + (set (path current-puzzle > num-states) num-states) + (incr current-puzzle)) + + (fclose puzzles-file) + (return (> g-num-puzzles 0))) + +(var g-puzzle-binary-version Uint32 1) + +(defun-local write-puzzles-binary (filename (* (const char)) &return bool) + (unless g-num-puzzles + (printf "No puzzles to write\n") + (return false)) + (var puzzles-file (* SDL_RWops) (SDL_RWFromFile filename "w")) + (unless puzzles-file + (printf "Failed to open puzzles output file\n") + (return false)) + + (SDL_WriteLE32 puzzles-file g-puzzle-binary-version) + (SDL_WriteLE32 puzzles-file g-num-puzzles) + + (unless (SDL_RWwrite puzzles-file g-puzzle-list (sizeof (type puzzle-data)) g-num-puzzles) + (printf "Failed to write puzzles\n") + (return false)) + + (SDL_RWclose puzzles-file) + (return true)) + +(defun read-puzzles-binary (filename (* (const char)) &return bool) + (when g-puzzle-list + (free (type-cast g-puzzle-list (* void))) + (set g-puzzle-list null)) + (var puzzles-file (* SDL_RWops) (SDL_RWFromFile filename "r")) + (unless puzzles-file + (printf "Failed to open puzzles output file\n") + (return false)) + + (var version Uint32 (SDL_ReadLE32 puzzles-file)) + (unless (= g-puzzle-binary-version version) + (printf "Version mismatch: expected %d, got %d\n" g-puzzle-binary-version version) + (return false)) + (set g-num-puzzles (SDL_ReadLE32 puzzles-file)) + (unless g-num-puzzles + (printf "No puzzles to read\n") + (return false)) + + (set g-puzzle-list (type-cast (calloc g-num-puzzles (sizeof (type puzzle-data))) (* puzzle-data))) + + (unless (SDL_RWread puzzles-file g-puzzle-list (sizeof (type puzzle-data)) g-num-puzzles) + (printf "Failed to read puzzles\n") + (return false)) + + (SDL_RWclose puzzles-file) + (return true)) + +(comptime-cond + ('Kitty-Main) + (true + (defun main (&return int) + (unless (read-puzzles-text) (return 1)) + (unless (write-puzzles-binary (in-data-dir "puzzles.bin")) (return 1)) + (unless (read-puzzles-binary (in-data-dir "puzzles.bin")) (return 1)) + (return 0)))) + +;; +;; Building +;; + +(module-use-sdl-build-options) + +(add-library-runtime-search-directory "Dependencies/gamelib/Dependencies/SDL/buildSDLBuild/lib") + +;; Note that this executable still pulls .so files from Dependencies +(comptime-cond + ('Kitty-Main) + (true + (set-cakelisp-option executable-output "../../generate-puzzles") + ;; Need a label so that our special version of Main.cake isn't used + (add-build-config-label "GeneratePuzzles")))