commit 624498de7da2394257bd306c74b49504daa692e8 Author: Gary Talent Date: Sat Dec 23 14:15:45 2023 -0600 Initial commit diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..4d5dbae --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,65 @@ +# Generated from CLion Inspection settings +--- +Checks: '-*, +cppcoreguidelines-interfaces-global-init, +cppcoreguidelines-narrowing-conversions, +cppcoreguidelines-pro-type-member-init, +-cppcoreguidelines-pro-type-static-cast-downcast, +cppcoreguidelines-slicing, +google-default-arguments, +google-runtime-operator, +hicpp-exception-baseclass, +hicpp-multiway-paths-covered, +mpi-buffer-deref, +mpi-type-mismatch, +openmp-use-default-none, +performance-faster-string-find, +performance-for-range-copy, +performance-implicit-conversion-in-loop, +performance-inefficient-algorithm, +performance-inefficient-string-concatenation, +performance-inefficient-vector-operation, +performance-move-const-arg, +performance-move-constructor-init, +performance-no-automatic-move, +performance-noexcept-move-constructor, +performance-trivially-destructible, +performance-type-promotion-in-math-fn, +performance-unnecessary-copy-initialization, +performance-unnecessary-value-param, +readability-avoid-const-params-in-decls, +readability-const-return-type, +readability-container-size-empty, +readability-convert-member-functions-to-static, +readability-delete-null-pointer, +readability-deleted-default, +readability-inconsistent-declaration-parameter-name, +readability-make-member-function-const, +readability-misleading-indentation, +readability-misplaced-array-index, +readability-non-const-parameter, +readability-redundant-control-flow, +readability-redundant-declaration, +readability-redundant-function-ptr-dereference, +readability-redundant-smartptr-get, +readability-redundant-string-cstr, +readability-redundant-string-init, +readability-simplify-subscript-expr, +readability-static-accessed-through-instance, +readability-static-definition-in-anonymous-namespace, +readability-string-compare, +readability-uniqueptr-delete-release, +readability-use-anyofallof, +cert-*, +misc-*, +-misc-include-cleaner +-misc-use-anonymous-namespace, +readability-duplicate-include, +-misc-non-private-member-variables-in-classes, +-misc-no-recursion, +bugprone-*, +clang-analyzer-*, +modernize-*, +portability-*, +-modernize-use-trailing-return-type, +-bugprone-easily-swappable-parameters' \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a68aa18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +.cache +.clangd +.current_build +.conanbuild +.idea +.mypy_cache +.stfolder +.stignore +scripts/__pycache__ +CMakeLists.txt.user +ROM.oxfs +Session.vim +build +compile_commands.json +dist +graph_info.json +imgui.ini +*.gba +*.sav +studio_state.json +tags diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..272afad --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,24 @@ +# This file is a template, and might need editing before it works on your project. +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/C++.gitlab-ci.yml + +# use the official gcc image, based on debian +# can use versions as well, like gcc:5.2 +# see https://hub.docker.com/_/gcc/ + +image: gcc + +build: + stage: build + variables: + OX_NODEBUG: 1 + before_script: + - apt update && apt -y install make cmake ninja-build pkg-config xorg-dev libgtk-3-dev python3 python3-mypy + script: + - make purge configure-release test install + - make purge configure-asan test install diff --git a/.liccor.yml b/.liccor.yml new file mode 100644 index 0000000..d6eb78d --- /dev/null +++ b/.liccor.yml @@ -0,0 +1,5 @@ +--- +source: +- src +copyright_notice: |- + Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c9f1ad4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.19) +set(CMAKE_POLICY_DEFAULT_CMP0110 NEW) # requires CMake 3.19 + +if(BUILDCORE_TARGET STREQUAL "gba") + project(jasper ASM CXX) +elseif(APPLE) + project(jasper C CXX OBJC OBJCXX) +else() + project(jasper C CXX) +endif() + +include(deps/nostalgia/deps/buildcore/base.cmake) + +set(NOSTALGIA_BUILD_PLAYER OFF) +set(NOSTALGIA_BUILD_STUDIO OFF) + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + +if(APPLE) + set(CMAKE_MACOSX_RPATH OFF) +else() + if(UNIX) + set(BUILD_SHARED_LIBS ON) + endif() + set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib") + set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +endif() + +include_directories( + deps/nostalgia/deps/ox/src +) +add_subdirectory(deps/nostalgia) +add_subdirectory(src) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d7dbd03 --- /dev/null +++ b/Makefile @@ -0,0 +1,42 @@ +BC_VAR_PROJECT_NAME=jasper +BC_VAR_PROJECT_NAME_CAP=Jasper +BUILDCORE_PATH=deps/nostalgia/deps/buildcore +include ${BUILDCORE_PATH}/base.mk + +ifeq ($(BC_VAR_OS),darwin) + PROJECT_PLAYER=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME}.app/Contents/MacOS/${BC_VAR_PROJECT_NAME} + PROJECT_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME}Studio.app/Contents/MacOS/${BC_VAR_PROJECT_NAME_CAP}Studio + MGBA=/Applications/mGBA.app/Contents/MacOS/mGBA +else + PROJECT_PLAYER=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME} + PROJECT_STUDIO=./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME_CAP}Studio + MGBA=mgba-qt +endif + +.PHONY: pkg-gba +pkg-gba: build + ${BC_CMD_ENVRUN} ${BC_PY3} ./deps/nostalgia/scripts/pkg-gba.py project ${BC_VAR_PROJECT_NAME} + +.PHONY: run +run: build + ${PROJECT_PLAYER} project +.PHONY: run-studio +run-studio: build + ${PROJECT_STUDIO} +.PHONY: gba-run +gba-run: pkg-gba + ${MGBA} ${BC_VAR_PROJECT_NAME}.gba +.PHONY: debug +debug: build + ${BC_CMD_HOST_DEBUGGER} ./build/${BC_VAR_CURRENT_BUILD}/bin/${BC_VAR_PROJECT_NAME} sample_project +.PHONY: debug-studio +debug-studio: build + ${BC_CMD_HOST_DEBUGGER} ${PROJECT_STUDIO} + +.PHONY: configure-gba +configure-gba: + ${BC_CMD_SETUP_BUILD} --toolchain=deps/gbabuildcore/cmake/modules/GBA.cmake --target=gba --current_build=0 --build_type=release --build_root=${BC_VAR_BUILD_PATH} + +.PHONY: configure-gba-debug +configure-gba-debug: + ${BC_CMD_SETUP_BUILD} --toolchain=deps/gbabuildcore/cmake/modules/GBA.cmake --target=gba --current_build=0 --build_type=debug --build_root=${BC_VAR_BUILD_PATH} diff --git a/README.md b/README.md new file mode 100644 index 0000000..fb03ffe --- /dev/null +++ b/README.md @@ -0,0 +1,42 @@ +# Jasper + +## Prerequisites + +* Install GCC, Clang, or Visual Studio with C++20 support +* Install [devkitPro](https://devkitpro.org/wiki/Getting_Started) to build for GBA +* Install Python 3 +* Install Ninja, Make, and CMake +* Consider also installing ccache for faster subsequent build times + +### Debian + +For Debian (and probably other Linux distros, but the package names will +probably differ), install the following additional packages: +* pkg-config +* xorg-dev +* libgtk-3-dev + +## Build + +Build options: release, debug, asan, gba, gba-debug + + make purge configure-{gba,release,debug} install + +## Run + +### Studio + + make run-studio + +### Native Platform + + make run + +### GBA + + make gba-run + +## Contributing + +Please read the [Developer Handbook](developer-handbook.md) for information on +coding standards. diff --git a/developer-handbook.md b/developer-handbook.md new file mode 100644 index 0000000..6b2eae4 --- /dev/null +++ b/developer-handbook.md @@ -0,0 +1,27 @@ +# Jasper Developer Handbook + +## About + +The purpose of the Developer Handbook is similar to that of the README. +The README should be viewed as a prerequisite to the Developer Handbook. +The README should provide information needed to build the project, which might +be used by an advanced user or a person trying to build and package the +project. +The Developer Handbook should focus on information needed by a developer +working on the project. + +## Project Structure + +### Overview + +All components have a platform indicator next to them: + + (PG) - PC, GBA + (-G) - GBA + (P-) - PC + +With this project being based on Nostalgia, the Nostalgia [developer +handbook](https://git.drinkingtea.net/drinkingtea/nostalgia/src/branch/master/developer-handbook.md) +should be considered a prerequisite for the Jasper developer handbook. + + diff --git a/jenkins/gba/Jenkinsfile b/jenkins/gba/Jenkinsfile new file mode 100644 index 0000000..49d5f69 --- /dev/null +++ b/jenkins/gba/Jenkinsfile @@ -0,0 +1,45 @@ +pipeline { + agent { + label 'gba' + } + stages { + stage('Environment') { + steps { + load 'jenkins/shared/env.gy' + sh 'make conan-config' + sh 'make conan' + } + } + stage('Build Tools Debug') { + steps { + sh 'make purge configure-debug' + sh 'make install' + } + } + stage('Build GBA Debug') { + steps { + sh 'make configure-gba-debug' + sh 'make' + sh 'make pkg-gba' + } + } + stage('Build Tools Release') { + steps { + sh 'make purge configure-release' + sh 'make install' + } + } + stage('Build GBA Release') { + steps { + sh 'make configure-gba' + sh 'make' + sh 'make pkg-gba' + } + } + } + post { + always { + archiveArtifacts artifacts: 'nostalgia.gba', fingerprint: true + } + } +} diff --git a/jenkins/linux/Jenkinsfile b/jenkins/linux/Jenkinsfile new file mode 100644 index 0000000..3aec5c0 --- /dev/null +++ b/jenkins/linux/Jenkinsfile @@ -0,0 +1,47 @@ +pipeline { + agent { + label 'linux-x86_64' + } + stages { + stage('Environment') { + steps { + load 'jenkins/shared/env.gy' + sh 'make conan-config' + sh 'make conan' + } + } + stage('Build Asan') { + steps { + sh 'make purge configure-asan' + sh 'make' + } + } + stage('Test Asan') { + steps { + sh 'make test' + } + } + stage('Build Debug') { + steps { + sh 'make purge configure-debug' + sh 'make' + } + } + stage('Test Debug') { + steps { + sh 'make test' + } + } + stage('Build Release') { + steps { + sh 'make purge configure-release' + sh 'make' + } + } + stage('Test Release') { + steps { + sh 'make test' + } + } + } +} diff --git a/jenkins/mac/Jenkinsfile b/jenkins/mac/Jenkinsfile new file mode 100644 index 0000000..86bb827 --- /dev/null +++ b/jenkins/mac/Jenkinsfile @@ -0,0 +1,47 @@ +pipeline { + agent { + label 'mac-x86_64' + } + stages { + stage('Environment') { + steps { + load 'jenkins/shared/env.gy' + sh 'make conan-config' + sh 'make conan' + } + } + stage('Build Asan') { + steps { + sh 'make purge configure-asan' + sh 'make' + } + } + stage('Test Asan') { + steps { + sh 'make test' + } + } + stage('Build Debug') { + steps { + sh 'make purge configure-debug' + sh 'make' + } + } + stage('Test Debug') { + steps { + sh 'make test' + } + } + stage('Build Release') { + steps { + sh 'make purge configure-release' + sh 'make' + } + } + stage('Test Release') { + steps { + sh 'make test' + } + } + } +} diff --git a/jenkins/shared/env.gy b/jenkins/shared/env.gy new file mode 100644 index 0000000..f5cde3b --- /dev/null +++ b/jenkins/shared/env.gy @@ -0,0 +1,2 @@ +env.OX_NODEBUG = 1 +env.BUILDCORE_SUPPRESS_CCACHE = 1 diff --git a/project/.jasper/type_descriptors/B.bool;0 b/project/.jasper/type_descriptors/B.bool;0 new file mode 100644 index 0000000..0f93c25 --- /dev/null +++ b/project/.jasper/type_descriptors/B.bool;0 @@ -0,0 +1,4 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "primitiveType" : 2, + "typeName" : "B.bool" +} diff --git a/project/.jasper/type_descriptors/B.int32;0 b/project/.jasper/type_descriptors/B.int32;0 new file mode 100644 index 0000000..5acc44f --- /dev/null +++ b/project/.jasper/type_descriptors/B.int32;0 @@ -0,0 +1,5 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "length" : 4, + "primitiveType" : 1, + "typeName" : "B.int32" +} diff --git a/project/.jasper/type_descriptors/B.int8;0 b/project/.jasper/type_descriptors/B.int8;0 new file mode 100644 index 0000000..a011811 --- /dev/null +++ b/project/.jasper/type_descriptors/B.int8;0 @@ -0,0 +1,5 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "length" : 1, + "primitiveType" : 1, + "typeName" : "B.int8" +} diff --git a/project/.jasper/type_descriptors/B.string;0 b/project/.jasper/type_descriptors/B.string;0 new file mode 100644 index 0000000..f658c64 --- /dev/null +++ b/project/.jasper/type_descriptors/B.string;0 @@ -0,0 +1,4 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "primitiveType" : 4, + "typeName" : "B.string" +} diff --git a/project/.jasper/type_descriptors/B.uint16;0 b/project/.jasper/type_descriptors/B.uint16;0 new file mode 100644 index 0000000..1330cf9 --- /dev/null +++ b/project/.jasper/type_descriptors/B.uint16;0 @@ -0,0 +1,4 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "length" : 2, + "typeName" : "B.uint16" +} diff --git a/project/.jasper/type_descriptors/B.uint64;0 b/project/.jasper/type_descriptors/B.uint64;0 new file mode 100644 index 0000000..458ed70 --- /dev/null +++ b/project/.jasper/type_descriptors/B.uint64;0 @@ -0,0 +1,4 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "length" : 8, + "typeName" : "B.uint64" +} diff --git a/project/.jasper/type_descriptors/B.uint8;0 b/project/.jasper/type_descriptors/B.uint8;0 new file mode 100644 index 0000000..44d41b3 --- /dev/null +++ b/project/.jasper/type_descriptors/B.uint8;0 @@ -0,0 +1,4 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "length" : 1, + "typeName" : "B.uint8" +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.jasper.core.Bootfile;1 b/project/.jasper/type_descriptors/net.drinkingtea.jasper.core.Bootfile;1 new file mode 100644 index 0000000..4af35be --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.jasper.core.Bootfile;1 @@ -0,0 +1,24 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "app", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "args", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.jasper.core.Bootfile", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.CompactTileSheet;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.CompactTileSheet;1 new file mode 100644 index 0000000..2f891fa --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.CompactTileSheet;1 @@ -0,0 +1,27 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "bpp", + "typeId" : "B.int8;0" + }, + { + "fieldName" : "defaultPalette", + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "pixels", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.CompactTileSheet", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.NostalgiaGraphic;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.NostalgiaGraphic;1 new file mode 100644 index 0000000..1427537 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.NostalgiaGraphic;1 @@ -0,0 +1,39 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "bpp", + "typeId" : "B.int8;0" + }, + { + "fieldName" : "rows", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "columns", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "defaultPalette", + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "pal", + "typeId" : "net.drinkingtea.nostalgia.core.Palette;1" + }, + { + "fieldName" : "pixels", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.NostalgiaGraphic", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.Palette;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.Palette;1 new file mode 100644 index 0000000..0336e78 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.Palette;1 @@ -0,0 +1,19 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "colors", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint16;0" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.Palette", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;1 new file mode 100644 index 0000000..949b965 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;1 @@ -0,0 +1,42 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "name", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "rows", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "columns", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "subsheets", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;1" + }, + { + "fieldName" : "pixels", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;3 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;3 new file mode 100644 index 0000000..01125bb --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet.SubSheet;3 @@ -0,0 +1,42 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "name", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "rows", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "columns", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "subsheets", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;3" + }, + { + "fieldName" : "pixels", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet", + "typeVersion" : 3 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;2 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;2 new file mode 100644 index 0000000..17be8f6 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;2 @@ -0,0 +1,20 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "bpp", + "typeId" : "B.int8;0" + }, + { + "fieldName" : "defaultPalette", + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "subsheet", + "typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;1" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.TileSheet", + "typeVersion" : 2 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;3 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;3 new file mode 100644 index 0000000..3bb3448 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.core.TileSheet;3 @@ -0,0 +1,24 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "bpp", + "typeId" : "B.int8;0" + }, + { + "fieldName" : "idIt", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "defaultPalette", + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "subsheet", + "typeId" : "net.drinkingtea.nostalgia.core.TileSheet.SubSheet;3" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.core.TileSheet", + "typeVersion" : 3 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.SceneDoc;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.SceneDoc;1 new file mode 100644 index 0000000..07dabd5 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.SceneDoc;1 @@ -0,0 +1,41 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "tilesheet", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "palettes", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "tiles", + "subscriptLevels" : 3, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.nostalgia.scene.TileDoc;1" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.scene.SceneDoc", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.SceneStatic;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.SceneStatic;1 new file mode 100644 index 0000000..8482e49 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.SceneStatic;1 @@ -0,0 +1,88 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "tilesheet", + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "palettes", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "columns", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint16;0" + }, + { + "fieldName" : "rows", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint16;0" + }, + { + "fieldName" : "tileMapIdx", + "subscriptLevels" : 2, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint16;0" + }, + { + "fieldName" : "tileType", + "subscriptLevels" : 2, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + }, + { + "fieldName" : "layerAttachments", + "subscriptLevels" : 2, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.scene.SceneStatic", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.TileDoc;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.TileDoc;1 new file mode 100644 index 0000000..3853479 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.scene.TileDoc;1 @@ -0,0 +1,33 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "subsheet_id", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "subsheet_path", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "type", + "typeId" : "B.uint8;0" + }, + { + "fieldName" : "layer_attachments", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "length" : 4, + "subscriptType" : 3 + } + ], + "typeId" : "B.uint8;0" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.scene.TileDoc", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.PrefabDoc;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.PrefabDoc;1 new file mode 100644 index 0000000..cd5bdff --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.PrefabDoc;1 @@ -0,0 +1,25 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "footprint", + "typeId" : "net.drinkingtea.ox.Size;1" + }, + { + "fieldName" : "visible_size", + "typeId" : "net.drinkingtea.ox.Size;1" + }, + { + "fieldName" : "tilesheet_path", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "subsheet_path", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.world.PrefabDoc", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.TileDoc;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.TileDoc;1 new file mode 100644 index 0000000..74dbf61 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.TileDoc;1 @@ -0,0 +1,33 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "subsheet_id", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "subsheet_path", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "type", + "typeId" : "B.uint8;0" + }, + { + "fieldName" : "layer_attachments", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "length" : 4, + "subscriptType" : 3 + } + ], + "typeId" : "B.uint8;0" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.world.TileDoc", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.WorldDoc;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.WorldDoc;1 new file mode 100644 index 0000000..67299d5 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.WorldDoc;1 @@ -0,0 +1,41 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "tilesheet", + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "palettes", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.ox.BasicString#8#;1" + }, + { + "fieldName" : "tiles", + "subscriptLevels" : 3, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.nostalgia.world.TileDoc;1" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.world.WorldDoc", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.WorldStatic;1 b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.WorldStatic;1 new file mode 100644 index 0000000..67e78f7 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.nostalgia.world.WorldStatic;1 @@ -0,0 +1,88 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "tilesheet", + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "palettes", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "net.drinkingtea.ox.FileAddress;1" + }, + { + "fieldName" : "columns", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint16;0" + }, + { + "fieldName" : "rows", + "subscriptLevels" : 1, + "subscriptStack" : + [ + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint16;0" + }, + { + "fieldName" : "tileMapIdx", + "subscriptLevels" : 2, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint16;0" + }, + { + "fieldName" : "tileType", + "subscriptLevels" : 2, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + }, + { + "fieldName" : "layerAttachments", + "subscriptLevels" : 2, + "subscriptStack" : + [ + { + "subscriptType" : 4 + }, + { + "subscriptType" : 4 + } + ], + "typeId" : "B.uint8;0" + } + ], + "preloadable" : true, + "primitiveType" : 5, + "typeName" : "net.drinkingtea.nostalgia.world.WorldStatic", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.ox.BasicString#8#;1 b/project/.jasper/type_descriptors/net.drinkingtea.ox.BasicString#8#;1 new file mode 100644 index 0000000..dc047dc --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.ox.BasicString#8#;1 @@ -0,0 +1,9 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "primitiveType" : 4, + "typeName" : "net.drinkingtea.ox.BasicString", + "typeParams" : + [ + "8" + ], + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.ox.FileAddress.Data;1 b/project/.jasper/type_descriptors/net.drinkingtea.ox.FileAddress.Data;1 new file mode 100644 index 0000000..cb981fa --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.ox.FileAddress.Data;1 @@ -0,0 +1,20 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "path", + "typeId" : "B.string" + }, + { + "fieldName" : "constPath", + "typeId" : "B.string" + }, + { + "fieldName" : "inode", + "typeId" : "B.uint64;0" + } + ], + "primitiveType" : 6, + "typeName" : "net.drinkingtea.ox.FileAddress.Data", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.ox.FileAddress;1 b/project/.jasper/type_descriptors/net.drinkingtea.ox.FileAddress;1 new file mode 100644 index 0000000..43361af --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.ox.FileAddress;1 @@ -0,0 +1,16 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "type", + "typeId" : "B.int8;0" + }, + { + "fieldName" : "data", + "typeId" : "net.drinkingtea.ox.FileAddress.Data" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.ox.FileAddress", + "typeVersion" : 1 +} diff --git a/project/.jasper/type_descriptors/net.drinkingtea.ox.Size;1 b/project/.jasper/type_descriptors/net.drinkingtea.ox.Size;1 new file mode 100644 index 0000000..d1cd279 --- /dev/null +++ b/project/.jasper/type_descriptors/net.drinkingtea.ox.Size;1 @@ -0,0 +1,16 @@ +O1;net.drinkingtea.ox.TypeDescriptor;1;{ + "fieldList" : + [ + { + "fieldName" : "width", + "typeId" : "B.int32;0" + }, + { + "fieldName" : "height", + "typeId" : "B.int32;0" + } + ], + "primitiveType" : 5, + "typeName" : "net.drinkingtea.ox.Size", + "typeVersion" : 1 +} diff --git a/project/Bootfile b/project/Bootfile new file mode 100644 index 0000000..5abaf32 --- /dev/null +++ b/project/Bootfile @@ -0,0 +1,4 @@ +O1;net.drinkingtea.jasper.core.Bootfile;1;{ + "app": "World", + "args": ["Worlds/Chester.jwld"] +} diff --git a/project/Palettes/Charset.npal b/project/Palettes/Charset.npal new file mode 100644 index 0000000..abb6c19 Binary files /dev/null and b/project/Palettes/Charset.npal differ diff --git a/project/Palettes/Chester.npal b/project/Palettes/Chester.npal new file mode 100644 index 0000000..9880191 --- /dev/null +++ b/project/Palettes/Chester.npal @@ -0,0 +1 @@ +K1;14fc3dd8-42ff-4bf9-81f1-a010cc5ac251;M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö diff --git a/project/Palettes/Dirt.npal b/project/Palettes/Dirt.npal new file mode 100644 index 0000000..ffb649e --- /dev/null +++ b/project/Palettes/Dirt.npal @@ -0,0 +1 @@ +K1;0f75977f-1c52-45f8-9793-52ea2dc200a0;M2;net.drinkingtea.nostalgia.core.Palette;1;ûÿ³Ö diff --git a/project/Palettes/Logo.npal b/project/Palettes/Logo.npal new file mode 100644 index 0000000..d648dba --- /dev/null +++ b/project/Palettes/Logo.npal @@ -0,0 +1 @@ +K1;c79f21e2-f74f-4ad9-90ed-32b0ef7da6ed;M2;net.drinkingtea.nostalgia.core.Palette;1;PÛ{³ÖQ„ \ No newline at end of file diff --git a/project/TileSheets/Charset.ng b/project/TileSheets/Charset.ng new file mode 100644 index 0000000..79da10e Binary files /dev/null and b/project/TileSheets/Charset.ng differ diff --git a/project/TileSheets/Chester.ng b/project/TileSheets/Chester.ng new file mode 100644 index 0000000..6f54952 Binary files /dev/null and b/project/TileSheets/Chester.ng differ diff --git a/project/TileSheets/Dirt.ng b/project/TileSheets/Dirt.ng new file mode 100644 index 0000000..54f1cd1 Binary files /dev/null and b/project/TileSheets/Dirt.ng differ diff --git a/project/TileSheets/Logo.ng b/project/TileSheets/Logo.ng new file mode 100644 index 0000000..161a3cb Binary files /dev/null and b/project/TileSheets/Logo.ng differ diff --git a/project/TileSheets/NS_Logo.ng b/project/TileSheets/NS_Logo.ng new file mode 100644 index 0000000..b3f677a Binary files /dev/null and b/project/TileSheets/NS_Logo.ng differ diff --git a/project/WorldObjects/Table.jwob b/project/WorldObjects/Table.jwob new file mode 100644 index 0000000..14c5186 Binary files /dev/null and b/project/WorldObjects/Table.jwob differ diff --git a/project/Worlds/Chester.jwld b/project/Worlds/Chester.jwld new file mode 100644 index 0000000..0c15327 Binary files /dev/null and b/project/Worlds/Chester.jwld differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..6011eca --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(jasper) diff --git a/src/jasper/CMakeLists.txt b/src/jasper/CMakeLists.txt new file mode 100644 index 0000000..1488b18 --- /dev/null +++ b/src/jasper/CMakeLists.txt @@ -0,0 +1,16 @@ + +#project packages + +if(APPLE) + set(JASPER_DIST_BIN JasperStudio.app/Contents/MacOS) + set(JASPER_DIST_LIB JasperStudio.app/Contents/Library) + set(JASPER_DIST_MODULE JasperStudio.app/Contents/Plugins) + set(JASPER_DIST_RESOURCES JasperStudio.app/Contents/Resources) + set(JASPER_DIST_MAC_APP_CONTENTS JasperStudio.app/Contents) +endif() + +add_subdirectory(modules) +add_subdirectory(player) +if(TURBINE_BUILD_TYPE STREQUAL "Native") + add_subdirectory(tools) +endif() diff --git a/src/jasper/modules/CMakeLists.txt b/src/jasper/modules/CMakeLists.txt new file mode 100644 index 0000000..8724df6 --- /dev/null +++ b/src/jasper/modules/CMakeLists.txt @@ -0,0 +1,42 @@ +# module dir list + +add_subdirectory(core) +add_subdirectory(world) + +# module libraries + +# Keel +add_library( + JasperKeelModules STATIC + keelmodules.cpp +) +target_link_libraries( + JasperKeelModules PUBLIC + Keel + NostalgiaKeelModules + JasperCore-Keel + JasperWorld-Keel +) + +# Studio +if(TURBINE_BUILD_TYPE STREQUAL "Native") + add_library( + JasperStudioModules STATIC + studiomodules.cpp + ) + target_link_libraries( + JasperStudioModules PUBLIC + StudioAppLib + NostalgiaStudioModules + JasperWorld-Studio + ) +endif() + + +add_library(JasperProfile INTERFACE) +target_compile_definitions( + JasperProfile INTERFACE + OLYMPIC_PROJECT_NAME="Jasper" + OLYMPIC_PROJECT_NAMESPACE=jasper + OLYMPIC_PROJECT_DATADIR=".jasper" +) diff --git a/src/jasper/modules/core/CMakeLists.txt b/src/jasper/modules/core/CMakeLists.txt new file mode 100644 index 0000000..0abe879 --- /dev/null +++ b/src/jasper/modules/core/CMakeLists.txt @@ -0,0 +1,8 @@ +add_subdirectory(src) + +install( + DIRECTORY + include/jasper + DESTINATION + include +) diff --git a/src/jasper/modules/core/include/jasper/core/animpage.hpp b/src/jasper/modules/core/include/jasper/core/animpage.hpp new file mode 100644 index 0000000..dc27109 --- /dev/null +++ b/src/jasper/modules/core/include/jasper/core/animpage.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include + +namespace jasper::core { + +struct AnimPage { + constexpr static auto TypeName = "net.drinkingtea.nostalgia.core.AnimPage"; + constexpr static auto TypeVersion = 1; + constexpr static auto Preloadable = true; + ox::String tilesheetPath; + ox::String subsheetPath; +}; + +oxModelBegin(AnimPage) + oxModelFieldRename(tilesheet_path, tilesheetPath) + oxModelFieldRename(subsheet_path, subsheetPath) +oxModelEnd() + + +} diff --git a/src/jasper/modules/core/include/jasper/core/bootfile.hpp b/src/jasper/modules/core/include/jasper/core/bootfile.hpp new file mode 100644 index 0000000..7e7e84e --- /dev/null +++ b/src/jasper/modules/core/include/jasper/core/bootfile.hpp @@ -0,0 +1,27 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include + +namespace jasper::core { + +struct Bootfile { + static constexpr auto TypeName = "net.drinkingtea.jasper.core.Bootfile"; + static constexpr auto TypeVersion = 1; + static constexpr auto Preloadable = true; + ox::String app; + ox::Vector args; +}; + +oxModelBegin(Bootfile) + oxModelField(app) + oxModelField(args) +oxModelEnd() + + +} diff --git a/src/jasper/modules/core/include/jasper/core/keelmodule.hpp b/src/jasper/modules/core/include/jasper/core/keelmodule.hpp new file mode 100644 index 0000000..9440dad --- /dev/null +++ b/src/jasper/modules/core/include/jasper/core/keelmodule.hpp @@ -0,0 +1,13 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +namespace jasper::core { + +keel::Module const*keelModule() noexcept; + +} diff --git a/src/jasper/modules/core/src/CMakeLists.txt b/src/jasper/modules/core/src/CMakeLists.txt new file mode 100644 index 0000000..f02bbb0 --- /dev/null +++ b/src/jasper/modules/core/src/CMakeLists.txt @@ -0,0 +1,22 @@ + +add_library(JasperCore INTERFACE) + +target_include_directories( + JasperCore INTERFACE + ../include +) + +target_link_libraries( + JasperCore INTERFACE + NostalgiaCore +) + +add_subdirectory(keel) + +install( + TARGETS + JasperCore + DESTINATION + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/src/jasper/modules/core/src/keel/CMakeLists.txt b/src/jasper/modules/core/src/keel/CMakeLists.txt new file mode 100644 index 0000000..b25972a --- /dev/null +++ b/src/jasper/modules/core/src/keel/CMakeLists.txt @@ -0,0 +1,16 @@ +add_library( + JasperCore-Keel + keelmodule.cpp +) + +target_link_libraries( + JasperCore-Keel PUBLIC + Keel + JasperCore +) +install( + TARGETS + JasperCore-Keel + LIBRARY DESTINATION + ${NOSTALGIA_DIST_MODULE} +) diff --git a/src/jasper/modules/core/src/keel/keelmodule.cpp b/src/jasper/modules/core/src/keel/keelmodule.cpp new file mode 100644 index 0000000..805c861 --- /dev/null +++ b/src/jasper/modules/core/src/keel/keelmodule.cpp @@ -0,0 +1,46 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +namespace jasper::core { + +class CoreModule: public keel::Module { + private: + + public: + [[nodiscard]] + ox::String id() const noexcept override { + return ox::String("net.drinkingtea.jasper.core"); + } + + [[nodiscard]] + ox::Vector types() const noexcept override { + return { + keel::generateTypeDesc, + }; + } + + [[nodiscard]] + ox::Vector converters() const noexcept override { + return { + }; + } + + [[nodiscard]] + ox::Vector packTransforms() const noexcept override { + return { + }; + } + +}; + +static const CoreModule mod; +keel::Module const*keelModule() noexcept { + return &mod; +} + +} diff --git a/src/jasper/modules/keelmodules.cpp b/src/jasper/modules/keelmodules.cpp new file mode 100644 index 0000000..b06e390 --- /dev/null +++ b/src/jasper/modules/keelmodules.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include +#include + +namespace nostalgia { +void registerKeelModules() noexcept; +} + +namespace jasper { + +static bool modulesRegistered = false; +void registerKeelModules() noexcept { + if (modulesRegistered) { + return; + } + modulesRegistered = true; + nostalgia::registerKeelModules(); + keel::registerModule(core::keelModule()); + keel::registerModule(world::keelModule()); +} + +} diff --git a/src/jasper/modules/studiomodules.cpp b/src/jasper/modules/studiomodules.cpp new file mode 100644 index 0000000..12865ca --- /dev/null +++ b/src/jasper/modules/studiomodules.cpp @@ -0,0 +1,25 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +namespace nostalgia { +void registerStudioModules() noexcept; +} + +namespace jasper { + +static bool modulesRegistered = false; +void registerStudioModules() noexcept { + if (modulesRegistered) { + return; + } + modulesRegistered = true; + nostalgia::registerStudioModules(); + studio::registerModule(world::studioModule()); +} + +} diff --git a/src/jasper/modules/world/CMakeLists.txt b/src/jasper/modules/world/CMakeLists.txt new file mode 100644 index 0000000..0abe879 --- /dev/null +++ b/src/jasper/modules/world/CMakeLists.txt @@ -0,0 +1,8 @@ +add_subdirectory(src) + +install( + DIRECTORY + include/jasper + DESTINATION + include +) diff --git a/src/jasper/modules/world/include/jasper/world/consts.hpp b/src/jasper/modules/world/include/jasper/world/consts.hpp new file mode 100644 index 0000000..ae922e1 --- /dev/null +++ b/src/jasper/modules/world/include/jasper/world/consts.hpp @@ -0,0 +1,14 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +namespace jasper::world { + +inline constexpr ox::StringLiteral FileExt_jwob("jwob"); +inline constexpr ox::StringLiteral FileExt_jwld("jwld"); + +} diff --git a/src/jasper/modules/world/include/jasper/world/keelmodule.hpp b/src/jasper/modules/world/include/jasper/world/keelmodule.hpp new file mode 100644 index 0000000..7fba482 --- /dev/null +++ b/src/jasper/modules/world/include/jasper/world/keelmodule.hpp @@ -0,0 +1,13 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +namespace jasper::world { + +keel::Module const*keelModule() noexcept; + +} diff --git a/src/jasper/modules/world/include/jasper/world/prefab.hpp b/src/jasper/modules/world/include/jasper/world/prefab.hpp new file mode 100644 index 0000000..78188cd --- /dev/null +++ b/src/jasper/modules/world/include/jasper/world/prefab.hpp @@ -0,0 +1,35 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include + +#include + +#include + +namespace jasper::world { + +struct PrefabDoc { + constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.PrefabDoc"; + constexpr static auto TypeVersion = 1; + constexpr static auto Preloadable = true; + + ox::Size footprint; + ox::Size visibleSz; + ox::String tilesheetPath; + ox::String subsheetPath; +}; + +oxModelBegin(PrefabDoc) + oxModelField(footprint) + oxModelFieldRename(visible_size, visibleSz) + oxModelFieldRename(tilesheet_path, tilesheetPath) + oxModelFieldRename(subsheet_path, subsheetPath) +oxModelEnd() + +} diff --git a/src/jasper/modules/world/include/jasper/world/studiomodule.hpp b/src/jasper/modules/world/include/jasper/world/studiomodule.hpp new file mode 100644 index 0000000..5720537 --- /dev/null +++ b/src/jasper/modules/world/include/jasper/world/studiomodule.hpp @@ -0,0 +1,13 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +namespace jasper::world { + +studio::Module const*studioModule() noexcept; + +} diff --git a/src/jasper/modules/world/include/jasper/world/world.hpp b/src/jasper/modules/world/include/jasper/world/world.hpp new file mode 100644 index 0000000..006750d --- /dev/null +++ b/src/jasper/modules/world/include/jasper/world/world.hpp @@ -0,0 +1,31 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include "consts.hpp" +#include "prefab.hpp" +#include "worldstatic.hpp" + +namespace jasper::world { + +namespace ncore = nostalgia::core; + +class World { + private: + WorldStatic const&m_worldStatic; + + public: + explicit World(WorldStatic const&worldStatic) noexcept; + + ox::Error setupDisplay(ncore::Context &ctx) const noexcept; + + private: + void setupLayer(ncore::Context&, ox::Vector const&layer, unsigned layerNo) const noexcept; + +}; + +} diff --git a/src/jasper/modules/world/include/jasper/world/worldstatic.hpp b/src/jasper/modules/world/include/jasper/world/worldstatic.hpp new file mode 100644 index 0000000..bb257b4 --- /dev/null +++ b/src/jasper/modules/world/include/jasper/world/worldstatic.hpp @@ -0,0 +1,230 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace jasper::world { + +namespace ncore = nostalgia::core; + +struct SpriteDoc { + + constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.SpriteDoc"; + constexpr static auto TypeVersion = 1; + constexpr static auto Preloadable = true; + + ox::String tilesheetPath; + ox::Vector subsheetId; + +}; + +struct TileDoc { + + constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.TileDoc"; + constexpr static auto TypeVersion = 1; + constexpr static auto Preloadable = true; + + ncore::SubSheetId subsheetId = -1; + ox::String subsheetPath; + uint8_t type = 0; + ox::Array layerAttachments; + + [[nodiscard]] + constexpr ox::Result getSubsheetId(ncore::TileSheet const&ts) const noexcept { + // prefer the already present ID + if (subsheetId > -1) { + return subsheetId; + } + return ts.getIdFor(subsheetPath); + } + + [[nodiscard]] + constexpr ox::Result getSubsheetPath( + ncore::TileSheet const&ts) const noexcept { + // prefer the already present path + if (!subsheetPath.len()) { + return ts.getNameFor(subsheetId); + } + return ox::StringView(subsheetPath); + } + +}; + +oxModelBegin(TileDoc) + oxModelFieldRename(subsheet_id, subsheetId) + oxModelFieldRename(subsheet_path, subsheetPath) + oxModelField(type) + oxModelFieldRename(layer_attachments, layerAttachments) +oxModelEnd() + +struct WorldDoc { + + using TileMapRow = ox::Vector; + using TileMapLayer = ox::Vector; + using TileMap = ox::Vector; + + constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.WorldDoc"; + constexpr static auto TypeVersion = 1; + constexpr static auto Preloadable = true; + + ox::String tilesheet; // path + ox::Vector palettes; // paths + TileMap tiles; + + [[nodiscard]] + constexpr ox::Size size(std::size_t layerIdx) const noexcept { + const auto &layer = this->tiles[layerIdx]; + const auto rowCnt = static_cast(layer.size()); + if (!rowCnt) { + return {}; + } + auto colCnt = layer[0].size(); + // find shortest row (they should all be the same, but you know this data + // could come from a file) + for (auto const&row : layer) { + colCnt = ox::min(colCnt, row.size()); + } + return {static_cast(colCnt), rowCnt}; + } +}; + +oxModelBegin(WorldDoc) + oxModelField(tilesheet) + oxModelField(palettes) + oxModelField(tiles) +oxModelEnd() + + +constexpr void setTopEdge(uint8_t &layerAttachments, unsigned val) noexcept { + const auto val8 = static_cast(val); + layerAttachments = (layerAttachments & 0b11111100) | val8; +} +constexpr void setBottomEdge(uint8_t &layerAttachments, unsigned val) noexcept { + const auto val8 = static_cast(val); + layerAttachments = (layerAttachments & 0b11110011) | static_cast(val8 << 2); +} +constexpr void setLeftEdge(uint8_t &layerAttachments, unsigned val) noexcept { + const auto val8 = static_cast(val); + layerAttachments = (layerAttachments & 0b11001111) | static_cast(val8 << 4); +} +constexpr void setRightEdge(uint8_t &layerAttachments, unsigned val) noexcept { + const auto val8 = static_cast(val); + layerAttachments = (layerAttachments & 0b00111111) | static_cast(val8 << 6); +} + +[[nodiscard]] +constexpr unsigned topEdge(uint8_t layerAttachments) noexcept { + return layerAttachments & 0b11; +} +[[nodiscard]] +constexpr unsigned bottomEdge(uint8_t layerAttachments) noexcept { + return (layerAttachments >> 2) & 0b11; +} +[[nodiscard]] +constexpr unsigned leftEdge(uint8_t layerAttachments) noexcept { + return (layerAttachments >> 4) & 0b11; +} +[[nodiscard]] +constexpr unsigned rightEdge(uint8_t layerAttachments) noexcept { + return (layerAttachments >> 6) & 0b11; +} + + +struct WorldStatic { + + constexpr static auto TypeName = "net.drinkingtea.nostalgia.world.WorldStatic"; + constexpr static auto TypeVersion = 1; + constexpr static auto Preloadable = true; + + struct Tile { + uint16_t &tileMapIdx; + uint8_t &tileType; + uint8_t &layerAttachments; + constexpr Tile(uint16_t &pTileMapIdx, uint8_t &pTileType, uint8_t &pLayerAttachments) noexcept: + tileMapIdx(pTileMapIdx), + tileType(pTileType), + layerAttachments(pLayerAttachments) { + } + }; + struct Layer { + uint16_t &columns; + uint16_t &rows; + ox::Vector &tileMapIdx; + ox::Vector &tileType; + ox::Vector &layerAttachments; + constexpr Layer( + uint16_t &pColumns, + uint16_t &pRows, + ox::Vector &pTileMapIdx, + ox::Vector &pTileType, + ox::Vector &pLayerAttachments) noexcept: + columns(pColumns), + rows(pRows), + tileMapIdx(pTileMapIdx), + tileType(pTileType), + layerAttachments(pLayerAttachments) { + } + [[nodiscard]] + constexpr Tile tile(std::size_t i) noexcept { + return {tileMapIdx[i], tileType[i], layerAttachments[i]}; + } + constexpr auto setDimensions(ox::Size dim) noexcept { + columns = static_cast(dim.width); + rows = static_cast(dim.height); + const auto tileCnt = static_cast(columns * rows); + tileMapIdx.resize(tileCnt); + tileType.resize(tileCnt); + layerAttachments.resize(tileCnt); + } + }; + + ox::FileAddress tilesheet; + ox::Vector palettes; + // tile layer data + ox::Vector columns; + ox::Vector rows; + ox::Vector> tileMapIdx; + ox::Vector> tileType; + ox::Vector> layerAttachments; + + [[nodiscard]] + constexpr Layer layer(std::size_t i) noexcept { + return { + columns[i], + rows[i], + tileMapIdx[i], + tileType[i], + layerAttachments[i], + }; + } + + constexpr auto setLayerCnt(std::size_t layerCnt) noexcept { + this->layerAttachments.resize(layerCnt); + this->columns.resize(layerCnt); + this->rows.resize(layerCnt); + this->tileMapIdx.resize(layerCnt); + this->tileType.resize(layerCnt); + } + +}; + +oxModelBegin(WorldStatic) + oxModelField(tilesheet) + oxModelField(palettes) + oxModelField(columns) + oxModelField(rows) + oxModelField(tileMapIdx) + oxModelField(tileType) + oxModelField(layerAttachments) +oxModelEnd() + +} diff --git a/src/jasper/modules/world/src/CMakeLists.txt b/src/jasper/modules/world/src/CMakeLists.txt new file mode 100644 index 0000000..73cd9e8 --- /dev/null +++ b/src/jasper/modules/world/src/CMakeLists.txt @@ -0,0 +1,30 @@ + +add_library( + JasperWorld + world.cpp + worldstatic.cpp +) + +target_include_directories( + JasperWorld PUBLIC + ../include +) + +target_link_libraries( + JasperWorld PUBLIC + NostalgiaCore + JasperCore +) + +add_subdirectory(keel) +if(NOSTALGIA_BUILD_STUDIO) + add_subdirectory(studio) +endif() + +install( + TARGETS + JasperWorld + DESTINATION + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/src/jasper/modules/world/src/keel/CMakeLists.txt b/src/jasper/modules/world/src/keel/CMakeLists.txt new file mode 100644 index 0000000..2aa42bd --- /dev/null +++ b/src/jasper/modules/world/src/keel/CMakeLists.txt @@ -0,0 +1,17 @@ +add_library( + JasperWorld-Keel + keelmodule.cpp + typeconv.cpp +) + +target_link_libraries( + JasperWorld-Keel PUBLIC + Keel + JasperWorld +) +install( + TARGETS + JasperWorld-Keel + LIBRARY DESTINATION + ${NOSTALGIA_DIST_MODULE} +) diff --git a/src/jasper/modules/world/src/keel/keelmodule.cpp b/src/jasper/modules/world/src/keel/keelmodule.cpp new file mode 100644 index 0000000..72ce3a0 --- /dev/null +++ b/src/jasper/modules/world/src/keel/keelmodule.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +#include "typeconv.hpp" + +namespace jasper::world { + +class WorldModule: public keel::Module { + private: + WorldDocToWorldStaticConverter m_worldDocToWorldStaticConverter; + + public: + [[nodiscard]] + ox::String id() const noexcept override { + return ox::String("net.drinkingtea.jasper.world"); + } + + [[nodiscard]] + ox::Vector types() const noexcept override { + return { + keel::generateTypeDesc, + keel::generateTypeDesc, + }; + } + + [[nodiscard]] + ox::Vector converters() const noexcept override { + return { + &m_worldDocToWorldStaticConverter, + }; + } + + [[nodiscard]] + ox::Vector packTransforms() const noexcept override { + return { + keel::transformRule, + }; + } + +}; + +static const WorldModule mod; +keel::Module const*keelModule() noexcept { + return &mod; +} + +} diff --git a/src/jasper/modules/world/src/keel/typeconv.cpp b/src/jasper/modules/world/src/keel/typeconv.cpp new file mode 100644 index 0000000..beeaa04 --- /dev/null +++ b/src/jasper/modules/world/src/keel/typeconv.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include + +#include "typeconv.hpp" + +namespace jasper::world { + +namespace ncore = nostalgia::core; + +[[nodiscard]] +constexpr unsigned adjustLayerAttachment(unsigned layer, unsigned attachment) noexcept { + if (attachment == 0) { + return layer; + } else { + return attachment - 1; + } +} + +constexpr void setLayerAttachments(unsigned layer, TileDoc const&srcTile, WorldStatic::Tile &dstTile) noexcept { + setTopEdge( + dstTile.layerAttachments, + adjustLayerAttachment(layer, srcTile.layerAttachments[0])); + setBottomEdge( + dstTile.layerAttachments, + adjustLayerAttachment(layer, srcTile.layerAttachments[1])); + setLeftEdge( + dstTile.layerAttachments, + adjustLayerAttachment(layer, srcTile.layerAttachments[2])); + setRightEdge( + dstTile.layerAttachments, + adjustLayerAttachment(layer, srcTile.layerAttachments[3])); +} + +ox::Error WorldDocToWorldStaticConverter::convert( + keel::Context &ctx, + WorldDoc &src, + WorldStatic &dst) const noexcept { + oxRequire(ts, keel::readObj(ctx, src.tilesheet)); + const auto layerCnt = src.tiles.size(); + dst.setLayerCnt(layerCnt); + dst.tilesheet = ox::FileAddress(src.tilesheet); + dst.palettes.reserve(src.palettes.size()); + for (const auto &pal : src.palettes) { + dst.palettes.emplace_back(pal); + } + for (auto layerIdx = 0u; const auto &layer : src.tiles) { + const auto layerDim = src.size(layerIdx); + auto dstLayer = dst.layer(layerIdx); + dstLayer.setDimensions(layerDim); + for (auto tileIdx = 0u; const auto &row : layer) { + for (const auto &srcTile : row) { + auto dstTile = dstLayer.tile(tileIdx); + dstTile.tileType = srcTile.type; + oxRequire(path, srcTile.getSubsheetPath(*ts)); + oxRequire(mapIdx, ts->getTileOffset(path)); + dstTile.tileMapIdx = static_cast(mapIdx); + setLayerAttachments(layerIdx, srcTile, dstTile); + ++tileIdx; + } + } + ++layerIdx; + } + return {}; +} + +} diff --git a/src/jasper/modules/world/src/keel/typeconv.hpp b/src/jasper/modules/world/src/keel/typeconv.hpp new file mode 100644 index 0000000..9049ed4 --- /dev/null +++ b/src/jasper/modules/world/src/keel/typeconv.hpp @@ -0,0 +1,17 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include + +namespace jasper::world { + +class WorldDocToWorldStaticConverter: public keel::Converter { + ox::Error convert(keel::Context&, WorldDoc &src, WorldStatic &dst) const noexcept final; +}; + +} diff --git a/src/jasper/modules/world/src/studio/CMakeLists.txt b/src/jasper/modules/world/src/studio/CMakeLists.txt new file mode 100644 index 0000000..5ab1a07 --- /dev/null +++ b/src/jasper/modules/world/src/studio/CMakeLists.txt @@ -0,0 +1,20 @@ +add_library( + JasperWorld-Studio + studiomodule.cpp +) + +target_link_libraries( + JasperWorld-Studio PUBLIC + JasperWorld + Studio +) + +install( + TARGETS + JasperWorld-Studio + LIBRARY DESTINATION + ${NOSTALGIA_DIST_MODULE} +) + +add_subdirectory(worldobjecteditor) +add_subdirectory(worldeditor) diff --git a/src/jasper/modules/world/src/studio/studiomodule.cpp b/src/jasper/modules/world/src/studio/studiomodule.cpp new file mode 100644 index 0000000..7c7be07 --- /dev/null +++ b/src/jasper/modules/world/src/studio/studiomodule.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +#include "worldobjecteditor/worldobjecteditor-imgui.hpp" +#include "worldeditor/worldeditor-imgui.hpp" + +namespace jasper::world { + +class StudioModule: public studio::Module { + public: + ox::Vector editors(turbine::Context &ctx) const noexcept override { + return { + studio::editorMaker(ctx, FileExt_jwob), + studio::editorMaker(ctx, FileExt_jwld), + }; + } + ox::Vector> itemMakers(turbine::Context&) const noexcept override { + ox::Vector> out; + out.emplace_back(ox::make>("World Object", "WorldObjects", FileExt_jwob, ox::ClawFormat::Organic)); + out.emplace_back(ox::make>("World", "Worlds", FileExt_jwld, ox::ClawFormat::Organic)); + return out; + } +}; + +static StudioModule const mod; +studio::Module const*studioModule() noexcept { + return &mod; +} + +} diff --git a/src/jasper/modules/world/src/studio/worldeditor/CMakeLists.txt b/src/jasper/modules/world/src/studio/worldeditor/CMakeLists.txt new file mode 100644 index 0000000..c9e714c --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldeditor/CMakeLists.txt @@ -0,0 +1,6 @@ +target_sources( + JasperWorld-Studio PRIVATE + worldeditor-imgui.cpp + worldeditor.cpp + worldeditorview.cpp +) \ No newline at end of file diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp new file mode 100644 index 0000000..31be91f --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +#include "worldeditor-imgui.hpp" + +namespace jasper::world { + +WorldEditorImGui::WorldEditorImGui(turbine::Context &ctx, ox::StringView path): + Editor(path), + m_ctx(ctx), + m_editor(m_ctx, path), + m_view(m_ctx, m_editor.world()) { + setRequiresConstantRefresh(false); +} + +void WorldEditorImGui::draw(turbine::Context&) noexcept { + auto const paneSize = ImGui::GetContentRegionAvail(); + m_view.draw(ox::Size{static_cast(paneSize.x), static_cast(paneSize.y)}); + auto &fb = m_view.framebuffer(); + auto const fbWidth = static_cast(fb.width); + auto const fbHeight = static_cast(fb.height); + auto const srcH = fbHeight / fbWidth; + auto const dstH = paneSize.y / paneSize.x; + float xScale{}, yScale{}; + if (dstH > srcH) { + // crop off width + xScale = srcH / dstH; + yScale = 1; + } else { + auto const srcW = fbWidth / fbHeight; + auto const dstW = (paneSize.x / paneSize.y); + xScale = 1; + yScale = srcW / dstW; + } + uintptr_t const buffId = fb.color.id; + ImGui::Image( + std::bit_cast(buffId), + paneSize, + ImVec2(0, 1), + ImVec2(xScale, 1 - yScale)); +} + +void WorldEditorImGui::onActivated() noexcept { + oxLogError(m_view.setupWorld()); +} + +ox::Error WorldEditorImGui::saveItem() noexcept { + const auto sctx = applicationData(m_ctx); + oxReturnError(sctx->project->writeObj(itemPath(), m_editor.world(), ox::ClawFormat::Organic)); + oxReturnError(keelCtx(m_ctx).assetManager.setAsset(itemPath(), m_editor.world())); + return {}; +} + +} diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp new file mode 100644 index 0000000..1d36208 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor-imgui.hpp @@ -0,0 +1,35 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include + +#include "worldeditor.hpp" +#include "worldeditorview.hpp" + +namespace jasper::world { + +class WorldEditorImGui: public studio::Editor { + + private: + turbine::Context &m_ctx; + WorldEditor m_editor; + WorldEditorView m_view; + + public: + WorldEditorImGui(turbine::Context &ctx, ox::StringView path); + + void draw(turbine::Context&) noexcept final; + + void onActivated() noexcept override; + + protected: + ox::Error saveItem() noexcept final; + +}; + +} diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor.cpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor.cpp new file mode 100644 index 0000000..e3d4616 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor.cpp @@ -0,0 +1,20 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include "worldeditor.hpp" + +namespace jasper::world { + +WorldEditor::WorldEditor(turbine::Context &ctx, ox::StringView path): + m_ctx(*applicationData(ctx)), + m_world(*readObj(keelCtx(ctx), path).unwrapThrow()) { +} + +ox::Error WorldEditor::saveItem() noexcept { + return m_ctx.project->writeObj(m_itemPath, m_world); +} + +} diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditor.hpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditor.hpp new file mode 100644 index 0000000..7cbe5a0 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditor.hpp @@ -0,0 +1,34 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include + +#include + +namespace jasper::world { + +class WorldEditor { + + private: + studio::StudioContext &m_ctx; + ox::String m_itemPath; + WorldStatic m_world; + + public: + WorldEditor(turbine::Context &ctx, ox::StringView path); + + [[nodiscard]] + WorldStatic const&world() const noexcept { + return m_world; + } + + protected: + ox::Error saveItem() noexcept; + +}; + +} diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.cpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.cpp new file mode 100644 index 0000000..4b8cfc5 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.cpp @@ -0,0 +1,36 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include "worldeditorview.hpp" + +namespace jasper::world { + +WorldEditorView::WorldEditorView(turbine::Context &tctx, WorldStatic const&worldStatic): + m_cctx(ncore::init(tctx, {.glInstallDrawer = false}).unwrapThrow()), + m_worldStatic(worldStatic), + m_world(m_worldStatic) { +} + +ox::Error WorldEditorView::setupWorld() noexcept { + glutils::resizeInitFrameBuffer(m_frameBuffer, ncore::gl::drawSize(m_scale)); + return m_world.setupDisplay(*m_cctx); +} + +void WorldEditorView::draw(ox::Size const&targetSz) noexcept { + auto const scaleSz = targetSz / ncore::gl::drawSize(1); + if (m_scaleSz != scaleSz) [[unlikely]] { + m_scale = ox::max(1, ox::max(scaleSz.width, scaleSz.height)); + glutils::resizeInitFrameBuffer(m_frameBuffer, ncore::gl::drawSize(m_scale)); + } + glutils::FrameBufferBind const frameBufferBind(m_frameBuffer); + ncore::gl::draw(*m_cctx, m_scale); +} + +glutils::FrameBuffer const&WorldEditorView::framebuffer() const noexcept { + return m_frameBuffer; +} + +} diff --git a/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.hpp b/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.hpp new file mode 100644 index 0000000..e9021da --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldeditor/worldeditorview.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace jasper::world { + +namespace ncore = nostalgia::core; + +class WorldEditorView { + + private: + ncore::ContextUPtr m_cctx; + WorldStatic const&m_worldStatic; + World m_world; + glutils::FrameBuffer m_frameBuffer; + int m_scale = 1; + ox::Size m_scaleSz = ncore::gl::drawSize(m_scale); + + public: + WorldEditorView(turbine::Context &ctx, WorldStatic const&worldStatic); + + ox::Error setupWorld() noexcept; + + void draw(ox::Size const&targetSz) noexcept; + + [[nodiscard]] + glutils::FrameBuffer const&framebuffer() const noexcept; + +}; + +} diff --git a/src/jasper/modules/world/src/studio/worldobjecteditor/CMakeLists.txt b/src/jasper/modules/world/src/studio/worldobjecteditor/CMakeLists.txt new file mode 100644 index 0000000..19ce204 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldobjecteditor/CMakeLists.txt @@ -0,0 +1,5 @@ +target_sources( + JasperWorld-Studio PRIVATE + worldobjecteditor.cpp + worldobjecteditor-imgui.cpp +) diff --git a/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor-imgui.cpp b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor-imgui.cpp new file mode 100644 index 0000000..afc9633 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor-imgui.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include "worldobjecteditor-imgui.hpp" + +namespace jasper::world { + +WorldObjectEditorImGui::WorldObjectEditorImGui(turbine::Context &ctx, ox::StringView path): + Editor(path), + m_sctx(*applicationData(ctx)), + m_editor(ctx, path) { +} + +void WorldObjectEditorImGui::draw(turbine::Context&) noexcept { + const auto paneSize = ImGui::GetContentRegionAvail(); + constexpr auto attrEditorWidth = 400.f; + ImGui::BeginChild("Preview", ImVec2(paneSize.x - attrEditorWidth, 0)); + ImGui::EndChild(); + ImGui::SameLine(); + ImGui::BeginChild("AttrEditor", ImVec2(attrEditorWidth, 0), true); + drawAttrEditor(); + drawTileSheetSelector(); + ImGui::EndChild(); +} + +void WorldObjectEditorImGui::onActivated() noexcept { +} + +void WorldObjectEditorImGui::drawAttrEditor() noexcept { + static constexpr auto boundDimension = [](int &v) noexcept { + v = ox::clamp(v, 1, 4); + }; + auto &doc = m_editor.doc(); + ImGui::InputInt("Footprint Width", &doc.footprint.width); + ImGui::InputInt("Footprint Height", &doc.footprint.height); + ImGui::InputInt("Visible Width", &doc.visibleSz.width); + ImGui::InputInt("Visible Height", &doc.visibleSz.height); + boundDimension(doc.footprint.width); + boundDimension(doc.footprint.height); + boundDimension(doc.visibleSz.width); + boundDimension(doc.visibleSz.height); +} + +void WorldObjectEditorImGui::drawTileSheetSelector() noexcept { + auto const&tilesheetList = m_sctx.project->fileList(ncore::FileExt_ng); + auto const first = m_selectedTilesheetIdx < tilesheetList.size() ? + tilesheetList[m_selectedTilesheetIdx].c_str() : ""; + if (ImGui::BeginCombo("Tile Sheet", first, 0)) { + for (auto i = 0u; i < tilesheetList.size(); ++i) { + auto const selected = (m_selectedTilesheetIdx == i); + if (ImGui::Selectable(tilesheetList[i].c_str(), selected) && + m_selectedTilesheetIdx != i) { + m_selectedTilesheetIdx = i; + //oxLogError(m_model.setPalette(tilesheetList[n])); + } + if (selected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } +} + +ox::Error WorldObjectEditorImGui::saveItem() noexcept { + return m_editor.saveItem(); +} + +} diff --git a/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor-imgui.hpp b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor-imgui.hpp new file mode 100644 index 0000000..45e15f3 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor-imgui.hpp @@ -0,0 +1,38 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include + +#include + +#include "worldobjecteditor.hpp" + +namespace jasper::world { + +class WorldObjectEditorImGui: public studio::Editor { + + private: + studio::StudioContext &m_sctx; + PrefabEditor m_editor; + uint_t m_selectedTilesheetIdx{}; + + public: + WorldObjectEditorImGui(turbine::Context &ctx, ox::StringView path); + + void draw(turbine::Context&) noexcept final; + + void onActivated() noexcept override; + + protected: + void drawAttrEditor() noexcept; + + void drawTileSheetSelector() noexcept; + + ox::Error saveItem() noexcept final; + +}; + +} diff --git a/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor.cpp b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor.cpp new file mode 100644 index 0000000..d242c29 --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor.cpp @@ -0,0 +1,29 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include "worldobjecteditor.hpp" + +namespace jasper::world { + +PrefabEditor::PrefabEditor(turbine::Context &ctx, ox::StringView path): + m_ctx(*applicationData(ctx)), + m_itemPath(path), + m_doc(*readObj(keelCtx(ctx), path).unwrapThrow()) { +} + +PrefabDoc const&PrefabEditor::doc() const noexcept { + return m_doc; +} + +PrefabDoc &PrefabEditor::doc() noexcept { + return m_doc; +} + +ox::Error PrefabEditor::saveItem() noexcept { + return m_ctx.project->writeObj(m_itemPath, m_doc, ox::ClawFormat::Organic); +} + +} diff --git a/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor.hpp b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor.hpp new file mode 100644 index 0000000..4b23bfc --- /dev/null +++ b/src/jasper/modules/world/src/studio/worldobjecteditor/worldobjecteditor.hpp @@ -0,0 +1,34 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#pragma once + +#include +#include + +#include + +namespace jasper::world { + +class PrefabEditor { + + private: + studio::StudioContext &m_ctx; + ox::String m_itemPath; + PrefabDoc m_doc; + + public: + PrefabEditor(turbine::Context &ctx, ox::StringView path); + + [[nodiscard]] + PrefabDoc const&doc() const noexcept; + + [[nodiscard]] + PrefabDoc &doc() noexcept; + + ox::Error saveItem() noexcept; + +}; + +} diff --git a/src/jasper/modules/world/src/world.cpp b/src/jasper/modules/world/src/world.cpp new file mode 100644 index 0000000..b1402f8 --- /dev/null +++ b/src/jasper/modules/world/src/world.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +#include + +namespace jasper::world { + +namespace ncore = nostalgia::core; + +World::World(WorldStatic const&worldStatic) noexcept: + m_worldStatic(worldStatic) { +} + +ox::Error World::setupDisplay(ncore::Context &ctx) const noexcept { + if (m_worldStatic.palettes.empty()) { + return OxError(1, "World has no palettes"); + } + auto const&palette = m_worldStatic.palettes[0]; + oxReturnError(ncore::loadBgTileSheet( + ctx, 0, m_worldStatic.tilesheet)); + oxReturnError(ncore::loadBgPalette(ctx, palette)); + // disable all backgrounds + ncore::setBgStatus(ctx, 0); + for (auto layerNo = 0u; auto const&layer : m_worldStatic.tileMapIdx) { + setupLayer(ctx, layer, layerNo); + ++layerNo; + } + return {}; +} + +void World::setupLayer( + ncore::Context &ctx, + ox::Vector const&layer, + unsigned layerNo) const noexcept { + ncore::setBgStatus(ctx, layerNo, true); + ncore::setBgCbb(ctx, layerNo, 0); + auto x = 0; + auto y = 0; + const auto width = m_worldStatic.rows[layerNo]; + for (auto const&tile : layer) { + const auto tile8 = static_cast(tile); + ncore::setBgTile(ctx, layerNo, x, y, tile8); + ncore::setBgTile(ctx, layerNo, x + 1, y, tile8 + 1); + ncore::setBgTile(ctx, layerNo, x, y + 1, tile8 + 2); + ncore::setBgTile(ctx, layerNo, x + 1, y + 1, tile8 + 3); + x += 2; + if (x >= width * 2) { + x = 0; + y += 2; + } + } +} + +} diff --git a/src/jasper/modules/world/src/worldstatic.cpp b/src/jasper/modules/world/src/worldstatic.cpp new file mode 100644 index 0000000..8183207 --- /dev/null +++ b/src/jasper/modules/world/src/worldstatic.cpp @@ -0,0 +1,11 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include + +namespace jasper::world { + + + +} diff --git a/src/jasper/player/CMakeLists.txt b/src/jasper/player/CMakeLists.txt new file mode 100644 index 0000000..23f9ffd --- /dev/null +++ b/src/jasper/player/CMakeLists.txt @@ -0,0 +1,34 @@ +add_executable( + jasper WIN32 MACOSX_BUNDLE + app.cpp +) + +if(${BUILDCORE_TARGET} STREQUAL "gba") + set(LOAD_KEEL_MODS FALSE) + set_target_properties(jasper + PROPERTIES + LINK_FLAGS ${LINKER_FLAGS} + COMPILER_FLAGS "-mthumb -mthumb-interwork" + ) + OBJCOPY_FILE(jasper) +else() + set(LOAD_KEEL_MODS TRUE) +endif() + +target_compile_definitions( + jasper PRIVATE + OLYMPIC_LOAD_KEEL_MODULES=$ +) + +# enable LTO +if(NOT WIN32) + set_property(TARGET jasper PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + +target_link_libraries( + jasper PUBLIC + JasperProfile + JasperKeelModules + OlympicApplib + OxLogConn +) diff --git a/src/jasper/player/app.cpp b/src/jasper/player/app.cpp new file mode 100644 index 0000000..57b31af --- /dev/null +++ b/src/jasper/player/app.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2016 - 2023 Gary Talent (gary@drinkingtea.net). All rights reserved. + */ + +#include +#include + +#include +#include + +#include + +namespace jasper { +ox::Error run(turbine::Context &ctx, ox::StringView, ox::SpanView) noexcept { + oxOut("Jasper Player\n"); + oxReturnError(turbine::run(ctx)); + oxOut("Exiting...\n"); + return {}; +} +} + +namespace olympic { + +ox::Error run( + [[maybe_unused]] ox::StringView project, + [[maybe_unused]] ox::StringView appName, + [[maybe_unused]] ox::StringView projectDataDir, + [[maybe_unused]] int argc, + [[maybe_unused]] char const**argv) noexcept { + auto const path = ox::StringView(argv[1]); + oxRequireM(fs, keel::loadRomFs(path)); + oxRequireM(tctx, turbine::init(std::move(fs), project)); + constexpr ox::FileAddress BootfileAddr = ox::StringLiteral("/Bootfile"); + oxRequire(bootfile, keel::readObj(keelCtx(*tctx), BootfileAddr)); + return jasper::run(*tctx, bootfile->app, bootfile->args); +} + +} diff --git a/src/jasper/tools/CMakeLists.txt b/src/jasper/tools/CMakeLists.txt new file mode 100644 index 0000000..306f5a9 --- /dev/null +++ b/src/jasper/tools/CMakeLists.txt @@ -0,0 +1,57 @@ +add_executable(JasperStudio WIN32 MACOSX_BUNDLE) + +target_link_libraries( + JasperStudio + OlympicApplib + JasperStudioModules + JasperKeelModules + JasperProfile +) + +install( + TARGETS + JasperStudio + RUNTIME DESTINATION + ${JASPER_DIST_BIN} + BUNDLE DESTINATION . +) + +install( + FILES + js.icns + DESTINATION + ${JASPER_DIST_RESOURCES}/icons +) + +if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32) + # enable LTO + set_property(TARGET JasperStudio PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + +if(APPLE) + set_target_properties(JasperStudio PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) +endif() + +# Pack ######################################################################## + +add_executable(jasper-pack) + +target_link_libraries( + jasper-pack + OlympicApplib + KeelPack-AppLib + JasperKeelModules + JasperProfile +) + +if(CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT WIN32) + # enable LTO + set_property(TARGET jasper-pack PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) +endif() + +install( + TARGETS + jasper-pack + RUNTIME DESTINATION + bin +) diff --git a/src/jasper/tools/Info.plist b/src/jasper/tools/Info.plist new file mode 100644 index 0000000..701c4c1 --- /dev/null +++ b/src/jasper/tools/Info.plist @@ -0,0 +1,35 @@ + + + + + CFBundleExecutable + JasperStudio + + CFBundleGetInfoString + Jasper Studio + + CFBundleIconFile + icons/js.icns + + CFBundleIdentifier + net.drinkingtea.jasper.studio + + CFBundlePackageType + APPL + + CFBundleVersion + d2023.12.0 + + LSMinimumSystemVersion + 12.0.0 + + + NSPrincipalClass + NSApplication + NSHighResolutionCapable + True + + NSHumanReadableCopyright + Copyright (c) 2016-2023 Gary Talent <gary@drinkingtea.net> + + diff --git a/src/jasper/tools/js.icns b/src/jasper/tools/js.icns new file mode 100644 index 0000000..1318d1a Binary files /dev/null and b/src/jasper/tools/js.icns differ diff --git a/src/jasper/tools/js_icon480.png b/src/jasper/tools/js_icon480.png new file mode 100644 index 0000000..1b087ea Binary files /dev/null and b/src/jasper/tools/js_icon480.png differ