diff --git a/.github/workflows/build_ds.yml b/.github/workflows/build_ds.yml index 24a4e1920..a4ac4e0a4 100644 --- a/.github/workflows/build_ds.yml +++ b/.github/workflows/build_ds.yml @@ -12,7 +12,7 @@ jobs: container: image: skylyrac/blocksds:dev-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Compile DS build id: compile run: | @@ -20,19 +20,21 @@ jobs: export BLOCKSDSEXT=/opt/blocksds/external make ds + # otherwise notify_failure doesn't work - name: Install curl when necessary if: ${{ always() && steps.compile.outcome == 'failure' }} - run: apt install curl + run: apt-get -y install curl - uses: ./.github/actions/notify_failure if: ${{ always() && steps.compile.outcome == 'failure' }} with: NOTIFY_MESSAGE: 'Failed to compile DS build' WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - + + - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'classicube.nds' - DEST_NAME: 'classicube.nds' \ No newline at end of file + DEST_NAME: 'classicube.nds' diff --git a/.github/workflows/build_n64.yml b/.github/workflows/build_n64.yml index e9d08f591..ca81cb6be 100644 --- a/.github/workflows/build_n64.yml +++ b/.github/workflows/build_n64.yml @@ -26,6 +26,11 @@ jobs: make n64 + # otherwise notify_failure doesn't work + - name: Install curl when necessary + if: ${{ always() && steps.compile.outcome == 'failure' }} + run: apt-get -y install curl + - uses: ./.github/actions/notify_failure if: ${{ always() && steps.compile.outcome == 'failure' }} with: @@ -37,4 +42,4 @@ jobs: if: ${{ always() && steps.compile.outcome == 'success' }} with: SOURCE_FILE: 'ClassiCube-n64.z64' - DEST_NAME: 'ClassiCube-n64.z64' \ No newline at end of file + DEST_NAME: 'ClassiCube-n64.z64' diff --git a/.github/workflows/build_ps3.yml b/.github/workflows/build_ps3.yml index 1e1e22772..673380c37 100644 --- a/.github/workflows/build_ps3.yml +++ b/.github/workflows/build_ps3.yml @@ -10,33 +10,38 @@ jobs: if: github.ref_name == github.event.repository.default_branch runs-on: ubuntu-latest container: - image: akusiroyo/ps3sdk:latest + image: ghcr.io/classicube/minimal-psl1ght:latest steps: - uses: actions/checkout@v4 - name: Compile PS3 build id: compile run: | - pacman -S make --noconfirm - export PSL1GHT=/usr/local/ps3dev export PS3DEV=/usr/local/ps3dev + export PSL1GHT=/usr/local/ps3dev + export PATH=$PATH:$PS3DEV/bin + export PATH=$PATH:$PS3DEV/ppu/bin make ps3 - + + # otherwise notify_failure doesn't work + - name: Install curl when necessary + if: ${{ always() && steps.compile.outcome == 'failure' }} + run: apt-get install -y curl - uses: ./.github/actions/notify_failure if: ${{ always() && steps.compile.outcome == 'failure' }} with: NOTIFY_MESSAGE: 'Failed to compile PS3 build' WEBHOOK_URL: '${{ secrets.WEBHOOK_URL }}' - - + + - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: - SOURCE_FILE: 'ClassiCube-ps3.pkg' - DEST_NAME: 'ClassiCube-ps3.pkg' + SOURCE_FILE: 'ClassiCube-PS3.self' + DEST_NAME: 'ClassiCube-PS3.self' - uses: ./.github/actions/upload_build if: ${{ always() && steps.compile.outcome == 'success' }} with: - SOURCE_FILE: 'ClassiCube-ps3.elf' - DEST_NAME: 'ClassiCube-ps3.elf' \ No newline at end of file + SOURCE_FILE: 'ClassiCube-PS3.pkg' + DEST_NAME: 'ClassiCube-PS3.pkg' \ No newline at end of file diff --git a/misc/ClassicalSharp/ClassicalSharp.zip b/misc/ClassicalSharp/ClassicalSharp.zip deleted file mode 100644 index 8353d19cf..000000000 Binary files a/misc/ClassicalSharp/ClassicalSharp.zip and /dev/null differ diff --git a/misc/ClassicalSharp/known_bugs.txt b/misc/ClassicalSharp/known_bugs.txt deleted file mode 100644 index a7990fc01..000000000 --- a/misc/ClassicalSharp/known_bugs.txt +++ /dev/null @@ -1,21 +0,0 @@ -* Blocks over 256 are not saved or loaded at all. -* Custom block information for blocks over 256 is not saved or loaded at all. -* /hold 0 prevents you deleting blocks until you change to another. -* Sometimes when holding air and your own model is a block model, you crash. -* Labels and buttons overlap in launcher with some fonts. (e.g. Lucida Console) -* terrain.png with width under 16 insta-crash the game -* catbox.moe texture packs/terrain.png links insta-crash the game -* Sometimes you randomly crash reading leveldatachunk packet on OSX -* Models with size of over 2 are not supported at all -* Direct3D9 backend uses an ill-formed vertex format that works by accident -* Alt text doesn't update its Y position if you click on chat -* Menu inputs (save, edit hotkey, water level, etc) are reset on window resize -* Chat input caret is reset on window resize -* Position in chat (if you scrolled up into history) is reset on window resize -* Two blank lines get shown in chat when you type /client cuboid -* Alt text is closed on window resize -* Changing server texture packs sometimes still retains textures from previous one -* Crashes at startup when another process has exclusively acquired Direct3D9 device -* Can't bind controls to mouse buttons -* Does not work at all on 64 bit macOS -* Making a gas block undeletable doesn't prevent placing blocks over it \ No newline at end of file diff --git a/misc/ClassicalSharp/readme.txt b/misc/ClassicalSharp/readme.txt deleted file mode 100644 index 507d0d061..000000000 --- a/misc/ClassicalSharp/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -Here lies ClassicalSharp, the original C# client. (works with Mono and .NET framework 2.0) -It has unfixed bugs and missing features. There's no reason to use it anymore. - -For licensing, please see license.txt inside ClassicalSharp.zip. -Absolutely no support or assistance will be provided for ClassicalSharp. \ No newline at end of file diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.desktop b/misc/linux/flatpak/net.classicube.flatpak.client.desktop new file mode 100644 index 000000000..11f416da3 --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Name=ClassiCube +Exec=ClassiCubeLauncher +Comment=Sandbox building-block game +Type=Application +Icon=net.classicube.flatpak.client +Categories=Game;ActionGame; +Terminal=false +MimeType=x-scheme-handler/mc; +StartupWMClass=net.classicube.flatpak.client \ No newline at end of file diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml b/misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml new file mode 100644 index 000000000..bcdbe339e --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml @@ -0,0 +1,145 @@ + + + net.classicube.flatpak.client + ClassiCube + Sandbox building-block game + + The ClassiCube Project + + CC0-1.0 + BSD-3-Clause + +

ClassiCube brings you back to the days of 2009 where one block game ruled them all, it includes such features as:

+ +
+ + + Classic mode features faithful classic gameplay + https://github.com/ClassiCube/ClassiCube/assets/6509348/eedee53f-f53e-456f-b51c-92c62079eee0 + + + Enhanced mode allows hacks like flying and noclipping, it also allows servers to provide many custom features + https://github.com/ClassiCube/ClassiCube/assets/6509348/b2fe0e2b-5d76-41ab-909f-048d0ad15f37 + + + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.6 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.5 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.4 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.3 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.2 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.1 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.3.0 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.9 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.8 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.7 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.6 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.5 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.4 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.3 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.2 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.1 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.2.0 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.9 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.8 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.7 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.6 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.5 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.4 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.3 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.2 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.1.1 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.10 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.0.9 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.0.8 + + + https://github.com/ClassiCube/ClassiCube/releases/tag/1.0.7 + + + https://www.classicube.net/ + https://github.com/ClassiCube/ClassiCube/issues + https://www.patreon.com/ClassiCube + https://github.com/ClassiCube/ClassiCube + + Game + AdventureGame + ActionGame + + + pointing + keyboard + + + moderate + intense + + net.classicube.flatpak.client.desktop + + ClassiCube + +
diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.svg b/misc/linux/flatpak/net.classicube.flatpak.client.svg new file mode 100644 index 000000000..44324156b --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.svg @@ -0,0 +1,44 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/misc/linux/flatpak/net.classicube.flatpak.client.yml b/misc/linux/flatpak/net.classicube.flatpak.client.yml new file mode 100644 index 000000000..760e8b9d2 --- /dev/null +++ b/misc/linux/flatpak/net.classicube.flatpak.client.yml @@ -0,0 +1,29 @@ +id: net.classicube.flatpak.client +runtime: org.freedesktop.Platform +runtime-version: '23.08' +sdk: org.freedesktop.Sdk +command: ClassiCubeLauncher +finish-args: + - --socket=x11 + - --device=dri + - --share=network + - --share=ipc + - --socket=pulseaudio +modules: + - name: ClassiCube + buildsystem: simple + build-commands: + - gcc -fno-math-errno src/*.c -o src/ClassiCube -O1 -DCC_BUILD_FLATPAK -DCC_BUILD_GLMODERN -rdynamic -lm -lpthread -lX11 -lXi -lGL -ldl + - install -Dm755 src/ClassiCube -t ${FLATPAK_DEST}/bin + - install -Dm755 ClassiCubeLauncher -t ${FLATPAK_DEST}/bin + - install -Dm644 misc/linux/flatpak/net.classicube.flatpak.client.svg ${FLATPAK_DEST}/share/icons/hicolor/scalable/apps/net.classicube.flatpak.client.svg + - install -Dm644 misc/linux/flatpak/net.classicube.flatpak.client.desktop ${FLATPAK_DEST}/share/applications/net.classicube.flatpak.client.desktop + - install -Dm644 misc/linux/flatpak/net.classicube.flatpak.client.metainfo.xml ${FLATPAK_DEST}/share/metainfo/net.classicube.flatpak.client.metainfo.xml + sources: + - type: dir + path: ../../../ + - type: script + dest-filename: ClassiCubeLauncher + commands: + - mkdir -p ${XDG_DATA_HOME}/ClassiCube + - cd ${XDG_DATA_HOME}/ClassiCube && exec /app/bin/ClassiCube "$@" diff --git a/misc/os2/Makefile b/misc/os2/Makefile index bed5227ce..967bd65ae 100644 --- a/misc/os2/Makefile +++ b/misc/os2/Makefile @@ -41,7 +41,7 @@ APP_AUTHOR := UnknownShadow200 # options for code generation #--------------------------------------------------------------------------------- CC := gcc -CFLAGS := -pipe -fno-math-errno -O3 -g -mtune=pentium4 -msse2 -march=i686 -idirafter /@unixroot/usr/include/os2tk45 -DOS2 +CFLAGS := -pipe -fno-math-errno -O0 -g -mtune=pentium4 -msse2 -march=i686 -idirafter /@unixroot/usr/include/os2tk45 -DOS2 LDFLAGS := -Zhigh-mem -Zomf -Zargs-wild -Zargs-resp -Zlinker DISABLE -Zlinker 1121 LIBS := -lcx -lmmpm2 -lpthread -lSDL2 @@ -53,6 +53,6 @@ $(BUILD_DIR): $(C_OBJECTS): $(BUILD_DIR)/%.o : $(SOURCE_DIR)/%.c $(CC) $(CFLAGS) -c $< -o $@ -$(BUILD_DIR)/$(TARGET).res: misc/os2/$(TARGET).rc +$(BUILD_DIR)/$(TARGET).res: misc/os2/$(TARGET).rc misc/os2/$(TARGET).ico wrc -r misc/os2/$(TARGET).rc -fo=$@ diff --git a/misc/ps1/classicube.zip b/misc/ps1/classicube.zip new file mode 100644 index 000000000..bfc089647 Binary files /dev/null and b/misc/ps1/classicube.zip differ diff --git a/misc/ps1/iso.xml b/misc/ps1/iso.xml index 96ea23a8c..220f72635 100644 --- a/misc/ps1/iso.xml +++ b/misc/ps1/iso.xml @@ -69,6 +69,9 @@ --> + + + diff --git a/misc/ps3/Makefile b/misc/ps3/Makefile index b37d83967..e28411b5a 100644 --- a/misc/ps3/Makefile +++ b/misc/ps3/Makefile @@ -35,7 +35,7 @@ ICON0 := ../misc/ps3/ICON0.png CFLAGS = -O2 -DPLAT_PS3 -Wall -mcpu=cell -fno-math-errno $(MACHDEP) $(INCLUDE) CXXFLAGS = $(CFLAGS) -LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map +LDFLAGS = $(MACHDEP) -Wl,-Map,$(notdir $@).map -fno-use-linker-plugin #--------------------------------------------------------------------------------- # any extra libraries we wish to link with the project @@ -141,4 +141,4 @@ $(OUTPUT).elf: $(OFILES) #--------------------------------------------------------------------------------- endif -#--------------------------------------------------------------------------------- \ No newline at end of file +#--------------------------------------------------------------------------------- diff --git a/readme.md b/readme.md index efc2bcb45..2e873b0d2 100644 --- a/readme.md +++ b/readme.md @@ -21,9 +21,9 @@ ClassiCube is not trying to replicate modern Minecraft versions. It will never s You can **download ClassiCube** [from here](https://www.classicube.net/download/) and the very latest builds [from here](https://www.classicube.net/nightlies/). -![classic](https://github.com/ClassiCube/actions-testing-cc/assets/7892772/a233cb4c-296a-4d08-87fc-49874c230d4f) +![classic](https://github.com/ClassiCube/ClassiCube/assets/6509348/eedee53f-f53e-456f-b51c-92c62079eee0) -![enhanced](https://github.com/ClassiCube/actions-testing-cc/assets/7892772/61a064bd-cfaa-4a91-bedf-a16c3dd7e8a2) +![enhanced](https://github.com/ClassiCube/ClassiCube/assets/6509348/b2fe0e2b-5d76-41ab-909f-048d0ad15f37) # We need your help @@ -117,13 +117,17 @@ I am assuming you used the installer from https://osdn.net/projects/mingw/ 4. Enter `gcc -fno-math-errno *.c -o ClassiCube.exe -mwindows -lwinmm -limagehlp` ##### Using TCC (Tiny C Compiler) -I am assuming you used `tcc-0.9.27-win64-bin.zip` from https://bellard.org/tcc/ -1. Extract the .zip file +Setting up TCC: +1. Download and extract `tcc-0.9.27-win64-bin.zip` from https://bellard.org/tcc/ 2. In TCC's `lib/kernel32.def`, add missing `RtlCaptureContext` at line 554 (In between `RtlAddFunctionTable` and `RtlDeleteFunctionTable`) -3. Copy `winapi` folder and `_mingw_dxhelper.h` from `winapi-full-for-0.9.27.zip` into TCC's `include` folder -4. Navigate to the directory with ClassiCube's source code -5. In `ExtMath.c`, change `fabsf` to `fabs` and `sqrtf` to `sqrtf` -6. Enter `tcc.exe -o ClassiCube.exe *.c -lwinmm -limagehlp -lgdi32 -luser32 -lcomdlg32 -lshell32` +3. Download `winapi-full-for-0.9.27.zip` from https://bellard.org/tcc/ +4. Copy `winapi` folder and `_mingw_dxhelper.h` from `winapi-full-for-0.9.27.zip` into TCC's `include` folder + +Compiling with TCC: +1. Navigate to the directory with ClassiCube's source code +2. In `ExtMath.c`, change `fabsf` to `fabs` and `sqrtf` to `sqrt` +3. Enter `tcc.exe -o ClassiCube.exe *.c -lwinmm -limagehlp -lgdi32 -luser32 -lcomdlg32 -lshell32`
+(Note: You may need to specify the full path to `tcc.exe` instead of just `tcc.exe`) ## Compiling - Linux diff --git a/src/Animations.c b/src/Animations.c index 9c469dd64..29196f6ff 100644 --- a/src/Animations.c +++ b/src/Animations.c @@ -12,7 +12,12 @@ #include "Options.h" #include "Logger.h" -#define LIQUID_ANIM_MAX 64 +#ifdef CC_BUILD_LOWMEM + #define LIQUID_ANIM_MAX 16 +#else + #define LIQUID_ANIM_MAX 64 +#endif + #define WATER_TEX_LOC 14 #define LAVA_TEX_LOC 30 static void Animations_Update(int loc, struct Bitmap* bmp, int stride); @@ -292,20 +297,20 @@ static void Animations_Clear(void) { } static void Animations_Validate(void) { - struct AnimationData data; + struct AnimationData* data; int maxX, maxY, tileX, tileY; int i, j; anims_validated = true; for (i = 0; i < anims_count; i++) { - data = anims_list[i]; + data = &anims_list[i]; - maxX = data.frameX + data.frameSize * data.statesCount; - maxY = data.frameY + data.frameSize; - tileX = Atlas2D_TileX(data.texLoc); - tileY = Atlas2D_TileY(data.texLoc); + maxX = data->frameX + data->frameSize * data->statesCount; + maxY = data->frameY + data->frameSize; + tileX = Atlas2D_TileX(data->texLoc); + tileY = Atlas2D_TileY(data->texLoc); - if (data.frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) { + if (data->frameSize > Atlas2D.TileSize || tileY >= Atlas2D.RowsCount) { Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY); } else if (maxX > anims_bmp.width || maxY > anims_bmp.height) { Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY); @@ -313,8 +318,8 @@ static void Animations_Validate(void) { /* if user has water/lava animations in their default.zip, disable built-in */ /* However, 'usewateranim' and 'uselavaanim' files should always disable use */ /* of custom water/lava animations, even when they exist in animations.png */ - if (data.texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false; - if (data.texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false; + if (data->texLoc == LAVA_TEX_LOC && !alwaysLavaAnim) useLavaAnim = false; + if (data->texLoc == WATER_TEX_LOC && !alwaysWaterAnim) useWaterAnim = false; continue; } diff --git a/src/Audio.c b/src/Audio.c index 407492f46..435f37579 100644 --- a/src/Audio.c +++ b/src/Audio.c @@ -12,11 +12,15 @@ #include "Stream.h" #include "Utils.h" #include "Options.h" +#include "Deflate.h" #ifdef CC_BUILD_ANDROID /* TODO: Refactor maybe to not rely on checking WinInfo.Handle != NULL */ #include "Window.h" #endif + int Audio_SoundsVolume, Audio_MusicVolume; +const cc_string Sounds_ZipPathMC = String_FromConst("audio/default.zip"); +const cc_string Sounds_ZipPathCC = String_FromConst("audio/classicube.zip"); static const cc_string audio_dir = String_FromConst("audio"); struct Sound { @@ -91,7 +95,12 @@ static cc_result Sound_ReadWaveData(struct Stream* stream, struct Sound* snd) { if ((res = Audio_AllocChunks(size, &snd->data, 1))) return res; snd->size = size; - return Stream_Read(stream, (cc_uint8*)snd->data, size); + res = Stream_Read(stream, (cc_uint8*)snd->data, size); + + #ifdef CC_BUILD_BIGENDIAN + Utils_SwapEndian16((cc_int16*)snd->data, size / 2); + #endif + return res; } /* Skip over unhandled data */ @@ -99,30 +108,18 @@ static cc_result Sound_ReadWaveData(struct Stream* stream, struct Sound* snd) { } } -static cc_result Sound_ReadWave(const cc_string* path, struct Sound* snd) { - struct Stream stream; - cc_result res; - - res = Stream_OpenFile(&stream, path); - if (res) return res; - res = Sound_ReadWaveData(&stream, snd); - - /* No point logging error for closing readonly file */ - (void)stream.Close(&stream); - return res; -} - -static struct SoundGroup* Soundboard_Find(struct Soundboard* board, const cc_string* name) { +static struct SoundGroup* Soundboard_FindGroup(struct Soundboard* board, const cc_string* name) { struct SoundGroup* groups = board->groups; int i; - for (i = 0; i < SOUND_COUNT; i++) { + for (i = 0; i < SOUND_COUNT; i++) + { if (String_CaselessEqualsConst(name, Sound_Names[i])) return &groups[i]; } return NULL; } -static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName, const cc_string* file) { +static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName, const cc_string* file, struct Stream* stream) { struct SoundGroup* group; struct Sound* snd; cc_string name = *file; @@ -139,7 +136,7 @@ static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName name = String_UNSAFE_SubstringAt(&name, boardName->length); name = String_UNSAFE_Substring(&name, 0, name.length - 1); - group = Soundboard_Find(board, &name); + group = Soundboard_FindGroup(board, &name); if (!group) { Chat_Add1("&cUnknown sound group '%s'", &name); return; } @@ -148,7 +145,7 @@ static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName } snd = &group->sounds[group->count]; - res = Sound_ReadWave(file, snd); + res = Sound_ReadWaveData(stream, snd); if (res) { Logger_SysWarn2(res, "decoding", file); @@ -219,12 +216,29 @@ static void Audio_PlayBlockSound(void* obj, IVec3 coords, BlockID old, BlockID n } } -static void Sounds_LoadFile(const cc_string* path, void* obj) { +static cc_bool SelectZipEntry(const cc_string* path) { return true; } +static cc_result ProcessZipEntry(const cc_string* path, struct Stream* stream, struct ZipEntry* source) { static const cc_string dig = String_FromConst("dig_"); static const cc_string step = String_FromConst("step_"); - Soundboard_Load(&digBoard, &dig, path); - Soundboard_Load(&stepBoard, &step, path); + Soundboard_Load(&digBoard, &dig, path, stream); + Soundboard_Load(&stepBoard, &step, path, stream); + return 0; +} + +static cc_result Sounds_ExtractZip(const cc_string* path) { + struct Stream stream; + cc_result res; + + res = Stream_OpenFile(&stream, path); + if (res) { Logger_SysWarn2(res, "opening", path); return res; } + + res = Zip_Extract(&stream, SelectZipEntry, ProcessZipEntry); + if (res) Logger_SysWarn2(res, "extracting", path); + + /* No point logging error for closing readonly file */ + (void)stream.Close(&stream); + return res; } /* TODO this is a pretty terrible solution */ @@ -269,7 +283,7 @@ static void InitWebSounds(void) { static cc_bool sounds_loaded; static void Sounds_Start(void) { - int i; + cc_result res; if (!AudioBackend_Init()) { AudioBackend_Free(); Audio_SoundsVolume = 0; @@ -281,7 +295,9 @@ static void Sounds_Start(void) { #ifdef CC_BUILD_WEBAUDIO InitWebSounds(); #else - Directory_Enum(&audio_dir, NULL, Sounds_LoadFile); + res = Sounds_ExtractZip(&Sounds_ZipPathMC); + if (res == ReturnCode_FileNotFound) + Sounds_ExtractZip(&Sounds_ZipPathCC); #endif } @@ -336,7 +352,7 @@ static cc_result Music_Buffer(cc_int16* data, int maxSamples, struct VorbisState static cc_result Music_PlayOgg(struct Stream* source) { struct OggState ogg; - struct VorbisState vorbis = { 0 }; + struct VorbisState vorbis; int channels, sampleRate, volume; int chunkSize, samplesPerSecond; @@ -345,6 +361,7 @@ static cc_result Music_PlayOgg(struct Stream* source) { cc_result res; Ogg_Init(&ogg, source); + Vorbis_Init(&vorbis); vorbis.source = &ogg; if ((res = Vorbis_DecodeHeaders(&vorbis))) goto cleanup; diff --git a/src/Audio.h b/src/Audio.h index 416a42b36..e054d1c2d 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -34,6 +34,8 @@ extern int Audio_SoundsVolume; /* Volume music is played at, from 0-100. */ /* NOTE: Use Audio_SetMusic, don't change this directly. */ extern int Audio_MusicVolume; +extern const cc_string Sounds_ZipPathMC; +extern const cc_string Sounds_ZipPathCC; void Audio_SetMusic(int volume); void Audio_SetSounds(int volume); diff --git a/src/AudioBackend.c b/src/AudioBackend.c index 63efc5c9f..5f750b9c1 100644 --- a/src/AudioBackend.c +++ b/src/AudioBackend.c @@ -1133,10 +1133,19 @@ void Audio_FreeChunks(void** chunks, int numChunks) { #include #include #include +#include + +struct AudioBuffer { + int available; + int size; + void* samples; +}; struct AudioContext { - int chanID, count; + int chanID, count, bufHead; + struct AudioBuffer bufs[AUDIO_MAX_BUFFERS]; int channels, sampleRate, volume; + cc_bool makeAvailable; }; cc_bool AudioBackend_Init(void) { @@ -1152,10 +1161,42 @@ void AudioBackend_Free(void) { ASND_End(); } +void MusicCallback(s32 voice) { + struct AudioContext* ctx = &music_ctx; + struct AudioBuffer* nextBuf = &ctx->bufs[(ctx->bufHead + 1) % ctx->count]; + + if (ASND_StatusVoice(voice) != SND_WORKING) return; + + if (ASND_AddVoice(voice, nextBuf->samples, nextBuf->size) == SND_OK) { + ctx->bufHead = (ctx->bufHead + 1) % ctx->count; + if (ctx->bufHead == 2) ctx->makeAvailable = true; + if (ctx->makeAvailable) { + int prev = ctx->bufHead - 2; + if (prev < 0) prev += 4; + ctx->bufs[prev].available = true; + } + } + + int inUse; + Audio_Poll(ctx, &inUse); + if (!inUse) { + // music has finished, stop the voice so this function isn't called anymore + ASND_StopVoice(ctx->chanID); + } +} + cc_result Audio_Init(struct AudioContext* ctx, int buffers) { - ctx->chanID = -1; - ctx->count = buffers; - ctx->volume = 255; + ctx->chanID = -1; + ctx->count = buffers; + ctx->volume = 255; + ctx->bufHead = 0; + ctx->makeAvailable = false; + + Mem_Set(ctx->bufs, 0, sizeof(ctx->bufs)); + for (int i = 0; i < buffers; i++) { + ctx->bufs[i].available = true; + } + return 0; } @@ -1170,6 +1211,7 @@ cc_result Audio_SetFormat(struct AudioContext* ctx, int channels, int sampleRate ctx->channels = channels; ctx->sampleRate = sampleRate; ctx->chanID = ASND_GetFirstUnusedVoice(); + return 0; } @@ -1179,34 +1221,51 @@ void Audio_SetVolume(struct AudioContext* ctx, int volume) { cc_result Audio_QueueChunk(struct AudioContext* ctx, void* chunk, cc_uint32 dataSize) { // Audio buffers must be aligned and padded to a multiple of 32 bytes - if (((uintptr_t)chunk & 0x20) != 0) { + if (((uintptr_t)chunk & 0x1F) != 0) { Platform_Log1("Audio_QueueData: tried to queue buffer with non-aligned audio buffer 0x%x\n", &chunk); } - if ((dataSize & 0x20) != 0) { - Platform_Log1("Audio_QueueData: unaligned audio data size 0x%x\n", &dataSize); - } - int format = (ctx->channels == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; - ASND_SetVoice(ctx->chanID, format, ctx->sampleRate, 0, chunk, dataSize, ctx->volume, ctx->volume, NULL); + struct AudioBuffer* buf; - return 0; + for (int i = 0; i < ctx->count; i++) + { + buf = &ctx->bufs[i]; + if (!buf->available) continue; + + buf->samples = chunk; + buf->size = dataSize; + buf->available = false; + + return 0; + } // tried to queue data without polling for free buffers first return ERR_INVALID_ARGUMENT; } cc_result Audio_Play(struct AudioContext* ctx) { + int format = (ctx->channels == 2) ? VOICE_STEREO_16BIT : VOICE_MONO_16BIT; + ASND_SetVoice(ctx->chanID, format, ctx->sampleRate, 0, ctx->bufs[0].samples, ctx->bufs[0].size, ctx->volume, ctx->volume, (ctx->count > 1) ? MusicCallback : NULL); + if (ctx->count == 1) ctx->bufs[0].available = true; + return 0; } cc_result Audio_Poll(struct AudioContext* ctx, int* inUse) { - int status = ASND_StatusVoice(ctx->chanID); - *inUse = (status <= 0) ? 0 : ctx->count; + struct AudioBuffer* buf; + int count = 0; + for (int i = 0; i < ctx->count; i++) { + buf = &ctx->bufs[i]; + if (!buf->available) count++; + } + + *inUse = count; return 0; } -static cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { + +cc_bool Audio_FastPlay(struct AudioContext* ctx, struct AudioData* data) { return true; } @@ -1216,7 +1275,7 @@ cc_bool Audio_DescribeError(cc_result res, cc_string* dst) { cc_result Audio_AllocChunks(cc_uint32 size, void** chunks, int numChunks) { size = (size + 0x1F) & ~0x1F; // round up to nearest multiple of 0x20 - void* dst = aligned_alloc(0x20, size * numChunks); + void* dst = memalign(0x20, size); if (!dst) return ERR_OUT_OF_MEMORY; for (int i = 0; i < numChunks; i++) { diff --git a/src/Bitmap.c b/src/Bitmap.c index 043423fae..6f319a426 100644 --- a/src/Bitmap.c +++ b/src/Bitmap.c @@ -645,9 +645,9 @@ static void Png_EncodeRow(const cc_uint8* cur, const cc_uint8* prior, cc_uint8* best[0] = bestFilter; } -static BitmapCol* DefaultGetRow(struct Bitmap* bmp, int y) { return Bitmap_GetRow(bmp, y); } +static BitmapCol* DefaultGetRow(struct Bitmap* bmp, int y, void* ctx) { return Bitmap_GetRow(bmp, y); } cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, - Png_RowGetter getRow, cc_bool alpha) { + Png_RowGetter getRow, cc_bool alpha, void* ctx) { cc_uint8 tmp[32]; /* TODO: This should be * 4 for alpha (should switch to mem_alloc though) */ cc_uint8 prevLine[PNG_MAX_DIMS * 3], curLine[PNG_MAX_DIMS * 3]; @@ -691,7 +691,7 @@ cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, Mem_Set(prevLine, 0, lineSize); for (y = 0; y < bmp->height; y++) { - BitmapCol* src = getRow(bmp, y); + BitmapCol* src = getRow(bmp, y, ctx); cc_uint8* prev = (y & 1) == 0 ? prevLine : curLine; cc_uint8* cur = (y & 1) == 0 ? curLine : prevLine; diff --git a/src/Bitmap.h b/src/Bitmap.h index ca7cc6d81..c8688cc09 100644 --- a/src/Bitmap.h +++ b/src/Bitmap.h @@ -89,7 +89,7 @@ CC_API void Bitmap_Scale(struct Bitmap* dst, struct Bitmap* src, /* Whether data starts with PNG format signature/identifier. */ cc_bool Png_Detect(const cc_uint8* data, cc_uint32 len); -typedef BitmapCol* (*Png_RowGetter)(struct Bitmap* bmp, int row); +typedef BitmapCol* (*Png_RowGetter)(struct Bitmap* bmp, int row, void* ctx); /* Decodes a bitmap in PNG format. Partially based off information from https://handmade.network/forums/wip/t/2363-implementing_a_basic_png_reader_the_handmade_way @@ -100,5 +100,5 @@ CC_API cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream); /* getRow is optional. Can be used to modify how rows are encoded. (e.g. flip image) */ /* if alpha is non-zero, RGBA channels are saved, otherwise only RGB channels are. */ cc_result Png_Encode(struct Bitmap* bmp, struct Stream* stream, - Png_RowGetter getRow, cc_bool alpha); + Png_RowGetter getRow, cc_bool alpha, void* ctx); #endif diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters index 86b40ac23..aef0be6f9 100644 --- a/src/ClassiCube.vcxproj.filters +++ b/src/ClassiCube.vcxproj.filters @@ -620,9 +620,6 @@ Source Files\Platform - - Source Files\Window - Source Files\Window @@ -713,6 +710,9 @@ Source Files\Graphics + + Source Files\Audio + diff --git a/src/Core.h b/src/Core.h index 43f881e2b..41ea903a9 100644 --- a/src/Core.h +++ b/src/Core.h @@ -6,74 +6,74 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 */ #if _MSC_VER -typedef signed __int8 cc_int8; -typedef signed __int16 cc_int16; -typedef signed __int32 cc_int32; -typedef signed __int64 cc_int64; - -typedef unsigned __int8 cc_uint8; -typedef unsigned __int16 cc_uint16; -typedef unsigned __int32 cc_uint32; -typedef unsigned __int64 cc_uint64; -#ifdef _WIN64 -typedef unsigned __int64 cc_uintptr; -#else -typedef unsigned __int32 cc_uintptr; -#endif - -#define CC_INLINE inline -#define CC_NOINLINE __declspec(noinline) -#ifndef CC_API -#define CC_API __declspec(dllexport, noinline) -#define CC_VAR __declspec(dllexport) -#endif - -#define CC_HAS_TYPES -#define CC_HAS_MISC + typedef signed __int8 cc_int8; + typedef signed __int16 cc_int16; + typedef signed __int32 cc_int32; + typedef signed __int64 cc_int64; + + typedef unsigned __int8 cc_uint8; + typedef unsigned __int16 cc_uint16; + typedef unsigned __int32 cc_uint32; + typedef unsigned __int64 cc_uint64; + #ifdef _WIN64 + typedef unsigned __int64 cc_uintptr; + #else + typedef unsigned __int32 cc_uintptr; + #endif + + #define CC_INLINE inline + #define CC_NOINLINE __declspec(noinline) + #ifndef CC_API + #define CC_API __declspec(dllexport, noinline) + #define CC_VAR __declspec(dllexport) + #endif + + #define CC_HAS_TYPES + #define CC_HAS_MISC #elif __GNUC__ -/* really old GCC/clang might not have these defined */ -#ifdef __INT8_TYPE__ -/* avoid including because it breaks defining UNICODE in Platform.c with MinGW */ -typedef __INT8_TYPE__ cc_int8; -typedef __INT16_TYPE__ cc_int16; -typedef __INT32_TYPE__ cc_int32; -typedef __INT64_TYPE__ cc_int64; - -#ifdef __UINT8_TYPE__ -typedef __UINT8_TYPE__ cc_uint8; -typedef __UINT16_TYPE__ cc_uint16; -typedef __UINT32_TYPE__ cc_uint32; -typedef __UINT64_TYPE__ cc_uint64; -typedef __UINTPTR_TYPE__ cc_uintptr; -#else -/* clang doesn't define the __UINT8_TYPE__ */ -typedef unsigned __INT8_TYPE__ cc_uint8; -typedef unsigned __INT16_TYPE__ cc_uint16; -typedef unsigned __INT32_TYPE__ cc_uint32; -typedef unsigned __INT64_TYPE__ cc_uint64; -typedef unsigned __INTPTR_TYPE__ cc_uintptr; -#endif -#define CC_HAS_TYPES -#endif - -#define CC_INLINE inline -#define CC_NOINLINE __attribute__((noinline)) -#ifndef CC_API -#ifdef _WIN32 -#define CC_API __attribute__((dllexport, noinline)) -#define CC_VAR __attribute__((dllexport)) -#else -#define CC_API __attribute__((visibility("default"), noinline)) -#define CC_VAR __attribute__((visibility("default"))) -#endif -#endif -#define CC_HAS_MISC -#ifdef __BIG_ENDIAN__ -#define CC_BIG_ENDIAN -#endif + /* really old GCC/clang might not have these defined */ + #ifdef __INT8_TYPE__ + /* avoid including because it breaks defining UNICODE in Platform.c with MinGW */ + typedef __INT8_TYPE__ cc_int8; + typedef __INT16_TYPE__ cc_int16; + typedef __INT32_TYPE__ cc_int32; + typedef __INT64_TYPE__ cc_int64; + + #ifdef __UINT8_TYPE__ + typedef __UINT8_TYPE__ cc_uint8; + typedef __UINT16_TYPE__ cc_uint16; + typedef __UINT32_TYPE__ cc_uint32; + typedef __UINT64_TYPE__ cc_uint64; + typedef __UINTPTR_TYPE__ cc_uintptr; + #else + /* clang doesn't define the __UINT8_TYPE__ */ + typedef unsigned __INT8_TYPE__ cc_uint8; + typedef unsigned __INT16_TYPE__ cc_uint16; + typedef unsigned __INT32_TYPE__ cc_uint32; + typedef unsigned __INT64_TYPE__ cc_uint64; + typedef unsigned __INTPTR_TYPE__ cc_uintptr; + #endif + #define CC_HAS_TYPES + #endif + + #define CC_INLINE inline + #define CC_NOINLINE __attribute__((noinline)) + #ifndef CC_API + #ifdef _WIN32 + #define CC_API __attribute__((dllexport, noinline)) + #define CC_VAR __attribute__((dllexport)) + #else + #define CC_API __attribute__((visibility("default"), noinline)) + #define CC_VAR __attribute__((visibility("default"))) + #endif + #endif + #define CC_HAS_MISC + #ifdef __BIG_ENDIAN__ + #define CC_BIG_ENDIAN + #endif #elif __MWERKS__ -/* TODO: Is there actual attribute support for CC_API etc somewhere? */ -#define CC_BIG_ENDIAN + /* TODO: Is there actual attribute support for CC_API etc somewhere? */ + #define CC_BIG_ENDIAN #endif /* Unrecognised compiler, so just go with some sensible default typdefs */ @@ -120,31 +120,28 @@ typedef cc_uint8 cc_bool; #define CC_BUILD_NETWORKING #define CC_BUILD_FREETYPE -#ifndef CC_BUILD_FLATPAK #define CC_BUILD_RESOURCES -#endif +#define CC_BUILD_PLUGINS /*#define CC_BUILD_GL11*/ #ifndef CC_BUILD_MANUAL #if defined NXDK /* XBox also defines _WIN32 */ #define CC_BUILD_XBOX + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_NOMUSIC #define CC_BUILD_NOSOUNDS #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined XENON /* libxenon also defines __linux__ (yes, really) */ #define CC_BUILD_XBOX360 + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_NOMUSIC #define CC_BUILD_NOSOUNDS #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined _WIN32 #define CC_BUILD_WIN #define CC_BUILD_D3D9 @@ -269,128 +266,120 @@ typedef cc_uint8 cc_bool; #define CC_BUILD_COOPTHREADED #undef CC_BUILD_FREETYPE #undef CC_BUILD_RESOURCES + #undef CC_BUILD_PLUGINS #elif defined __psp__ #define CC_BUILD_PSP + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined __3DS__ #define CC_BUILD_3DS + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE #define CC_BUILD_TOUCH #define CC_BUILD_DUALSCREEN - #undef CC_BUILD_FREETYPE #elif defined GEKKO #define CC_BUILD_GCWII + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined __vita__ #define CC_BUILD_PSVITA + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_TOUCH #elif defined _arch_dreamcast #define CC_BUILD_DREAMCAST + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #undef CC_BUILD_RESOURCES #elif defined PLAT_PS3 #define CC_BUILD_PS3 + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE #define CC_BUILD_BEARSSL - #undef CC_BUILD_FREETYPE #elif defined N64 #define CC_BIG_ENDIAN #define CC_BUILD_N64 - #define CC_BUILD_OPENAL - #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED + #define CC_BUILD_OPENAL #undef CC_BUILD_RESOURCES #undef CC_BUILD_NETWORKING #elif defined PLAT_PS2 #define CC_BUILD_PS2 + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE - #undef CC_BUILD_FREETYPE #elif defined PLAT_NDS #define CC_BUILD_NDS + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_NOMUSIC #define CC_BUILD_NOSOUNDS #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE #define CC_BUILD_TOUCH - #undef CC_BUILD_FREETYPE #undef CC_BUILD_RESOURCES #elif defined __WIIU__ #define CC_BUILD_WIIU + #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_OPENAL #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM - #define CC_BUILD_CONSOLE #define CC_BUILD_BEARSSL - #undef CC_BUILD_FREETYPE #elif defined __SWITCH__ #define CC_BUILD_SWITCH + #define CC_BUILD_CONSOLE #define CC_BUILD_HTTPCLIENT #define CC_BUILD_BEARSSL - #define CC_BUILD_CONSOLE #define CC_BUILD_TOUCH #define CC_BUILD_GL #define CC_BUILD_GLMODERN #define CC_BUILD_GLES #define CC_BUILD_EGL - #undef CC_BUILD_FREETYPE #elif defined PLAT_PS1 #define CC_BUILD_PS1 - #define CC_BUILD_HTTPCLIENT - #define CC_BUILD_COOPTHREADED - #define CC_BUILD_LOWMEM #define CC_BUILD_CONSOLE + #define CC_BUILD_LOWMEM + #define CC_BUILD_COOPTHREADED #define CC_BUILD_NOMUSIC #define CC_BUILD_NOSOUNDS - #undef CC_BUILD_FREETYPE #undef CC_BUILD_RESOURCES #undef CC_BUILD_NETWORKING #elif defined OS2 #define CC_BUILD_OS2 #define CC_BUILD_POSIX #define CC_BUILD_SOFTGPU - #define CC_BUILD_SDL + #define CC_BUILD_SDL2 #define CC_BUILD_CURL #define CC_BUILD_FREETYPE #endif #endif +#ifdef CC_BUILD_CONSOLE +#undef CC_BUILD_FREETYPE +#undef CC_BUILD_PLUGINS +#endif #ifndef CC_BUILD_LOWMEM #define EXTENDED_BLOCKS diff --git a/src/Entity.c b/src/Entity.c index ff8f1bdb8..c73062606 100644 --- a/src/Entity.c +++ b/src/Entity.c @@ -1025,6 +1025,7 @@ static const struct EntityVTABLE netPlayer_VTABLE = { void NetPlayer_Init(struct NetPlayer* p) { Mem_Set(p, 0, sizeof(struct NetPlayer)); Entity_Init(&p->Base); + p->Base.Flags |= ENTITY_FLAG_CLASSIC_ADJUST; p->Base.VTABLE = &netPlayer_VTABLE; } diff --git a/src/Entity.h b/src/Entity.h index 4c6ea5d2e..18fcbd220 100644 --- a/src/Entity.h +++ b/src/Entity.h @@ -87,6 +87,9 @@ struct EntityVTABLE { /* And therefore trying to access the ModelVB Field in entity struct instances created by the CEF plugin */ /* results in attempting to read or write data from potentially invalid memory */ #define ENTITY_FLAG_HAS_MODELVB 0x02 +/* Whether in classic mode, to slightly adjust this entity downwards when rendering it */ +/* to replicate the behaviour of the original vanilla classic client */ +#define ENTITY_FLAG_CLASSIC_ADJUST 0x04 /* Contains a model, along with position, velocity, and rotation. May also contain other fields and properties. */ struct Entity { diff --git a/src/EntityRenderers.c b/src/EntityRenderers.c index 4a35f8eb2..863ca2c46 100644 --- a/src/EntityRenderers.c +++ b/src/EntityRenderers.c @@ -232,9 +232,9 @@ static void EntityShadows_MakeTexture(void) { BitmapCol* row = Bitmap_GetRow(&bmp, y); for (x = 0; x < sh_size; x++) { - double dist = - (sh_half - (x + 0.5)) * (sh_half - (x + 0.5)) + - (sh_half - (y + 0.5)) * (sh_half - (y + 0.5)); + float dist = + (sh_half - (x + 0.5f)) * (sh_half - (x + 0.5f)) + + (sh_half - (y + 0.5f)) * (sh_half - (y + 0.5f)); row[x] = dist < sh_half * sh_half ? color : 0; } } diff --git a/src/EnvRenderer.c b/src/EnvRenderer.c index 453d504e9..5e5cafbff 100644 --- a/src/EnvRenderer.c +++ b/src/EnvRenderer.c @@ -73,7 +73,7 @@ static void UpdateFogMinimal(float fogDensity) { /* Exp fog mode: f = e^(-density*coord) */ /* Solve coord for f = 0.05 (good approx for fog end) */ /* i.e. log(0.05) = -density * coord */ - #define LOG_005 -2.99573227355399 + #define LOG_005 -2.99573227355399f dist = (int)(LOG_005 / -fogDensity); Game_SetViewDistance(min(dist, Game_UserViewDistance)); @@ -83,7 +83,7 @@ static void UpdateFogMinimal(float fogDensity) { } static void UpdateFogNormal(float fogDensity, PackedCol fogColor) { - double density; + float density; if (fogDensity != 0.0f) { Gfx_SetFogMode(FOG_EXP); @@ -96,10 +96,10 @@ static void UpdateFogNormal(float fogDensity, PackedCol fogColor) { 0.99=z/end --> z=end*0.99 therefore d = -ln(0.01)/(end*0.99) */ - #define LOG_001 -4.60517018598809 + #define LOG_001 -4.60517018598809f - density = -(LOG_001) / (Game_ViewDistance * 0.99); - Gfx_SetFogDensity((float)density); + density = -LOG_001 / (Game_ViewDistance * 0.99f); + Gfx_SetFogDensity(density); } else { Gfx_SetFogMode(FOG_LINEAR); Gfx_SetFogEnd((float)Game_ViewDistance); diff --git a/src/ExtMath.c b/src/ExtMath.c index 6196c6785..28f8a1a81 100644 --- a/src/ExtMath.c +++ b/src/ExtMath.c @@ -94,7 +94,7 @@ cc_bool Math_IsPowOf2(int value) { #define RND_MASK ((1ULL << 48) - 1) void Random_SeedFromCurrentTime(RNGState* rnd) { - TimeMS now = DateTime_CurrentUTC_MS(); + cc_uint64 now = Stopwatch_Measure(); Random_Seed(rnd, (int)now); } diff --git a/src/Game.c b/src/Game.c index e813b4b6e..0624b12f9 100644 --- a/src/Game.c +++ b/src/Game.c @@ -53,11 +53,11 @@ int Game_MaxViewDistance = DEFAULT_MAX_VIEWDIST; int Game_FpsLimit, Game_Vertices; cc_bool Game_SimpleArmsAnim; static cc_bool gameRunning; -static cc_bool anaglyph3D; cc_bool Game_ClassicMode, Game_ClassicHacks; cc_bool Game_AllowCustomBlocks; cc_bool Game_AllowServerTextures; +cc_bool Game_Anaglyph3D; cc_bool Game_ViewBobbing, Game_HideGui; cc_bool Game_BreakableLiquids, Game_ScreenshotRequested; @@ -344,12 +344,10 @@ static void LoadOptions(void) { ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; Options.Set("skip-ssl-check", false); }*/ - anaglyph3D = Options_GetBool("anaglyph3D", false); + Game_Anaglyph3D = Options_GetBool(OPT_ANAGLYPH3D, false); } -#ifdef CC_BUILD_MINFILES -static void LoadPlugins(void) { } -#else +#ifdef CC_BUILD_PLUGINS static void LoadPlugin(const cc_string* path, void* obj) { void* lib; void* verSym; /* EXPORT int Plugin_ApiVersion = GAME_API_VER; */ @@ -390,6 +388,8 @@ static void LoadPlugins(void) { res = Directory_Enum(&dir, NULL, LoadPlugin); if (res) Logger_SysWarn(res, "enumerating plugins directory"); } +#else +static void LoadPlugins(void) { } #endif static void Game_Free(void* obj); @@ -637,7 +637,9 @@ static void Game_RenderFrame(double delta) { Game.Time += delta; Game_Vertices = 0; + if (Input.Sources & INPUT_SOURCE_GAMEPAD) Gamepad_Tick(delta); Camera.Active->UpdateMouse(delta); + if (!Window_Main.Focused && !Gui.InputGrab) Gui_ShowPauseMenu(); if (KeyBind_IsPressed(KEYBIND_ZOOM_SCROLL) && !Gui.InputGrab) { @@ -664,7 +666,7 @@ static void Game_RenderFrame(double delta) { Camera_KeyLookUpdate(delta); InputHandler_Tick(); - if (anaglyph3D) { + if (Game_Anaglyph3D) { Render3D_Anaglyph(delta, t); } else { Render3DFrame(delta, t); diff --git a/src/Game.h b/src/Game.h index 6001cac5b..e796dae06 100644 --- a/src/Game.h +++ b/src/Game.h @@ -26,7 +26,7 @@ extern cc_string Game_Mppass; #if defined CC_BUILD_N64 #define DEFAULT_VIEWDIST 20 -#elif defined CC_BUILD_NDS +#elif defined CC_BUILD_NDS || defined CC_BUILD_PS1 #define DEFAULT_VIEWDIST 192 #else #define DEFAULT_VIEWDIST 512 @@ -48,6 +48,7 @@ extern cc_bool Game_ClassicHacks; extern cc_bool Game_AllowCustomBlocks; extern cc_bool Game_AllowServerTextures; +extern cc_bool Game_Anaglyph3D; extern cc_bool Game_ViewBobbing; extern cc_bool Game_BreakableLiquids; /* Whether a screenshot should be taken at the end of this frame */ diff --git a/src/Graphics_3DS.c b/src/Graphics_3DS.c index eff743854..08164e8d9 100644 --- a/src/Graphics_3DS.c +++ b/src/Graphics_3DS.c @@ -148,8 +148,9 @@ static GfxResourceID white_square; void Gfx_Create(void) { if (!Gfx.Created) InitCitro3D(); - Gfx.MaxTexWidth = 512; - Gfx.MaxTexHeight = 512; + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; Gfx.Created = true; gfx_vsync = true; @@ -406,8 +407,34 @@ static void SetColorWrite(cc_bool r, cc_bool g, cc_bool b, cc_bool a) { /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ +static BitmapCol* _3DS_GetRow(struct Bitmap* bmp, int y, void* ctx) { + u8* fb = (u8*)ctx; + // Framebuffer is rotated 90 degrees + int width = bmp->width, height = bmp->height; + + for (int x = 0; x < width; x++) + { + int addr = (height - 1 - y + x * height) * 3; // TODO -1 or not + int b = fb[addr + 0]; + int g = fb[addr + 1]; + int r = fb[addr + 2]; + bmp->scan0[x] = BitmapColor_RGB(r, g, b); + } + return bmp->scan0; +} + cc_result Gfx_TakeScreenshot(struct Stream* output) { - return ERR_NOT_SUPPORTED; + BitmapCol tmp[512]; + u16 width, height; + u8* fb = gfxGetFramebuffer(GFX_TOP, GFX_LEFT, &width, &height); + + // Framebuffer is rotated 90 degrees + struct Bitmap bmp; + bmp.scan0 = tmp; + bmp.width = height; + bmp.height = width; + + return Png_Encode(&bmp, output, _3DS_GetRow, false, fb); } void Gfx_GetApiInfo(cc_string* info) { @@ -445,7 +472,7 @@ void Gfx_EndFrame(void) { if (gfx_minFrameMs) LimitFPS(); GPUBuffers_DeleteUnreferenced(); - //GPUTextures_DeleteUnreferenced(); + GPUTextures_DeleteUnreferenced(); frameCounter++; } @@ -853,4 +880,4 @@ void Gfx_Draw2DTexture(const struct Texture* tex, PackedCol color) { C3D_ImmSendAttrib(v[0].U, v[0].V, 0.0f, 0.0f); C3D_ImmDrawEnd(); } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_D3D11.c b/src/Graphics_D3D11.c index bcb596f09..4954db3a6 100644 --- a/src/Graphics_D3D11.c +++ b/src/Graphics_D3D11.c @@ -1055,13 +1055,8 @@ void Gfx_DepthOnlyRendering(cc_bool depthOnly) { /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ -static BitmapCol* D3D11_GetRow(struct Bitmap* bmp, int y) { - // You were expecting a BitmapCol*, but it was me, D3D11_MAPPED_SUBRESOURCE*! - // This is necessary because the stride of the mapped backbuffer often doesn't equal width of the bitmap - // e.g. with backbuffer width of 854, stride is 3456 bytes instead of expected 3416 (854*4) - // Therefore have to calculate row address manually instead of using Bitmap_GetRow - D3D11_MAPPED_SUBRESOURCE* buffer = (D3D11_MAPPED_SUBRESOURCE*)bmp->scan0; - +static BitmapCol* D3D11_GetRow(struct Bitmap* bmp, int y, void* ctx) { + D3D11_MAPPED_SUBRESOURCE* buffer = (D3D11_MAPPED_SUBRESOURCE*)ctx; char* row = (char*)buffer->pData + y * buffer->RowPitch; return (BitmapCol*)row; } @@ -1094,8 +1089,8 @@ cc_result Gfx_TakeScreenshot(struct Stream* output) { hr = ID3D11DeviceContext_Map(context, tmp, 0, D3D11_MAP_READ, 0, &buffer); if (hr) goto finished; { - Bitmap_Init(bmp, desc.Width, desc.Height, (BitmapCol*)&buffer); - hr = Png_Encode(&bmp, output, D3D11_GetRow, false); + Bitmap_Init(bmp, desc.Width, desc.Height, NULL); + hr = Png_Encode(&bmp, output, D3D11_GetRow, false, &buffer); } ID3D11DeviceContext_Unmap(context, tmp, 0); diff --git a/src/Graphics_D3D9.c b/src/Graphics_D3D9.c index cd980b9ae..8ec050be2 100644 --- a/src/Graphics_D3D9.c +++ b/src/Graphics_D3D9.c @@ -335,7 +335,7 @@ static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_boo int mipmapsLevels = CalcMipmapsLevels(bmp->width, bmp->height); int levels = 1 + (mipmaps ? mipmapsLevels : 0); - + if (flags & TEXTURE_FLAG_MANAGED) { while ((res = IDirect3DDevice9_CreateTexture(device, bmp->width, bmp->height, levels, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &tex, NULL))) @@ -812,7 +812,7 @@ cc_result Gfx_TakeScreenshot(struct Stream* output) { if (res) goto finished; { Bitmap_Init(bmp, desc.Width, desc.Height, (BitmapCol*)rect.pBits); - res = Png_Encode(&bmp, output, NULL, false); + res = Png_Encode(&bmp, output, NULL, false, NULL); if (res) { IDirect3DSurface9_UnlockRect(temp); goto finished; } } res = IDirect3DSurface9_UnlockRect(temp); diff --git a/src/Graphics_Dreamcast.c b/src/Graphics_Dreamcast.c index af8e53a7a..1dceb9dd1 100644 --- a/src/Graphics_Dreamcast.c +++ b/src/Graphics_Dreamcast.c @@ -543,4 +543,4 @@ void Gfx_EndFrame(void) { void Gfx_OnWindowResize(void) { glViewport(0, 0, Game.Width, Game.Height); } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_GCWii.c b/src/Graphics_GCWii.c index d5eb44973..498af41e9 100644 --- a/src/Graphics_GCWii.c +++ b/src/Graphics_GCWii.c @@ -50,8 +50,9 @@ static void InitGX(void) { void Gfx_Create(void) { if (!Gfx.Created) InitGX(); - Gfx.MaxTexWidth = 512; - Gfx.MaxTexHeight = 512; + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; Gfx.Created = true; gfx_vsync = true; @@ -236,8 +237,53 @@ void Gfx_SetDepthTest(cc_bool enabled) { /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ +static BitmapCol* GCWii_GetRow(struct Bitmap* bmp, int y, void* ctx) { + u8* buffer = (u8*)ctx; + u8 a, r, g, b; + int blockYStride = 4 * (bmp->width * 4); // tile row stride = 4 * row stride + int blockXStride = (4 * 4) * 4; // 16 pixels per tile + + // Do the inverse of converting from 4x4 tiled to linear + for (u32 x = 0; x < bmp->width; x++){ + int tileY = y >> 2, tileX = x >> 2; + int locY = y & 0x3, locX = x & 0x3; + int idx = (tileY * blockYStride) + (tileX * blockXStride) + ((locY << 2) + locX) * 2; + + // All 16 pixels are stored with AR first, then GB + //a = buffer[idx ]; + r = buffer[idx + 1]; + g = buffer[idx + 32]; + b = buffer[idx + 33]; + + bmp->scan0[x] = BitmapColor_RGB(r, g, b); + } + return bmp->scan0; +} + cc_result Gfx_TakeScreenshot(struct Stream* output) { - return ERR_NOT_SUPPORTED; + BitmapCol tmp[1024]; + GXRModeObj* vmode = VIDEO_GetPreferredMode(NULL); + int width = vmode->fbWidth; + int height = vmode->efbHeight; + + u8* buffer = memalign(32, width * height * 4); + if (!buffer) return ERR_OUT_OF_MEMORY; + + GX_SetTexCopySrc(0, 0, width, height); + GX_SetTexCopyDst(width, height, GX_TF_RGBA8, GX_FALSE); + GX_CopyTex(buffer, GX_FALSE); + GX_PixModeSync(); + GX_Flush(); + DCFlushRange(buffer, width * height * 4); + + struct Bitmap bmp; + bmp.scan0 = tmp; + bmp.width = width; + bmp.height = height; + + cc_result res = Png_Encode(&bmp, output, GCWii_GetRow, false, buffer); + free(buffer); + return res; } void Gfx_GetApiInfo(cc_string* info) { @@ -282,7 +328,7 @@ cc_bool Gfx_WarnIfNecessary(void) { return false; } GfxResourceID Gfx_CreateIb2(int count, Gfx_FillIBFunc fillFunc, void* obj) { //fillFunc(gfx_indices, count, obj); // not used since render using GX_QUADS anyways - return 1; + return (void*)1; } void Gfx_BindIb(GfxResourceID ib) { } @@ -552,4 +598,4 @@ void Gfx_DrawVb_IndexedTris(int verticesCount) { void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { Draw_TexturedTriangles(verticesCount, startVertex); } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_PS1.c b/src/Graphics_PS1.c index 8b427d81f..286949f9c 100644 --- a/src/Graphics_PS1.c +++ b/src/Graphics_PS1.c @@ -22,7 +22,7 @@ // Size of the buffer GPU commands and primitives are written to. If the program // crashes due to too many primitives being drawn, increase this value. -#define BUFFER_LENGTH 8192 +#define BUFFER_LENGTH 32768 typedef struct { DISPENV disp_env; @@ -82,12 +82,20 @@ static void* new_primitive(int size) { return (void*)prim; } +static GfxResourceID white_square; void Gfx_RestoreState(void) { InitDefaultResources(); + + // 2x2 dummy white texture + struct Bitmap bmp; + BitmapCol pixels[4] = { BITMAPCOLOR_WHITE, BITMAPCOLOR_WHITE, BITMAPCOLOR_WHITE, BITMAPCOLOR_WHITE }; + Bitmap_Init(bmp, 2, 2, pixels); + white_square = Gfx_CreateTexture(&bmp, 0, false); } void Gfx_FreeState(void) { - FreeDefaultResources(); + FreeDefaultResources(); + Gfx_DeleteTexture(&white_square); } void Gfx_Create(void) { @@ -115,16 +123,143 @@ void Gfx_Free(void) { /*########################################################################################################################* *---------------------------------------------------------Textures--------------------------------------------------------* *#########################################################################################################################*/ +// VRAM can be divided into texture pages +// 32 texture pages total - each page is 64 x 256 +// 10 texture pages are occupied by the doublebuffered display +// 22 texture packs are usable, and are then divided into +// - 4 pages for 256 wide textures, 8 for 128 wide, 10 for 64 +#define TPAGE_START_HOR 5 +#define TPAGES_PER_HALF 16 + +#define TPAGE_WIDTH 64 +#define TPAGE_HEIGHT 256 +#define MAX_TEX_PAGES 22 +static cc_uint8 vram_used[(MAX_TEX_PAGES * TPAGE_HEIGHT) / 8]; + +#define VRAM_SetUsed(line) (vram_used[(line) / 8] |= (1 << ((line) % 8))) +#define VRAM_UnUsed(line) (vram_used[(line) / 8] &= ~(1 << ((line) % 8))) +#define VRAM_IsUsed(line) (vram_used[(line) / 8] & (1 << ((line) % 8))) + +static void VRAM_GetBlockRange(int width, int* beg, int* end) { + if (width >= 256) { + *beg = 0; + *end = 4 * TPAGE_HEIGHT; + } else if (width >= 128) { + *beg = 4 * TPAGE_HEIGHT; + *end = 12 * TPAGE_HEIGHT; + } else { + *beg = 12 * TPAGE_HEIGHT; + *end = 22 * TPAGE_HEIGHT; + } +} + +static cc_bool VRAM_IsRangeFree(int beg, int end) { + for (int i = beg; i < end; i++) + { + if (VRAM_IsUsed(i)) return false; + } + return true; +} + +static int VRAM_FindFreeBlock(int width, int height) { + int beg, end; + VRAM_GetBlockRange(width, &beg, &end); + + // TODO kinda inefficient + for (int i = beg; i < end - height; i++) + { + if (VRAM_IsUsed(i)) continue; + + if (VRAM_IsRangeFree(i, i + height)) return i; + } + return -1; +} + +#define TEXTURES_MAX_COUNT 64 +typedef struct GPUTexture { + cc_uint16 width, height; + cc_uint16 line, tpage; +} GPUTexture; +static GPUTexture textures[TEXTURES_MAX_COUNT]; +static GPUTexture* active_tex; + +#define BGRA8_to_PS1(src) \ + ((src[2] & 0xF8) >> 3) | ((src[1] & 0xF8) << 2) | ((src[0] & 0xF8) << 7) | 0x8000 + +static void* AllocTextureAt(int i, struct Bitmap* bmp) { + cc_uint16* tmp = Mem_TryAlloc(bmp->width * bmp->height, 2); + if (!tmp) return NULL; + + for (int y = 0; y < bmp->height; y++) + { + cc_uint32* src = bmp->scan0 + y * bmp->width; + cc_uint16* dst = tmp + y * bmp->width; + + for (int x = 0; x < bmp->width; x++) { + cc_uint8* color = (cc_uint8*)&src[x]; + dst[x] = BGRA8_to_PS1(color); + } + } + + GPUTexture* tex = &textures[i]; + int line = VRAM_FindFreeBlock(bmp->width, bmp->height); + if (line == -1) { Mem_Free(tmp); return NULL; } + + tex->width = bmp->width; + tex->height = bmp->height; + tex->line = line; + + int page = TPAGE_START_HOR + (line / TPAGE_HEIGHT); + // In bottom half of VRAM? Need to offset horizontally again + if (page >= TPAGES_PER_HALF) page += TPAGE_START_HOR; + + for (int i = tex->line; i < tex->line + tex->height; i++) + { + VRAM_SetUsed(i); + } + tex->tpage = page; + Platform_Log4("%i x %i = %i,%i", &bmp->width, &bmp->height, &line, &page); + + RECT rect; + rect.x = ((page % TPAGES_PER_HALF)) * TPAGE_WIDTH; + rect.y = ((page / TPAGES_PER_HALF)) * TPAGE_HEIGHT + (line % TPAGE_HEIGHT); + rect.w = bmp->width; + rect.h = bmp->height; + + int RX = rect.x, RY = rect.y; + Platform_Log2("LOAD AT: %i, %i", &RX, &RY); + LoadImage2(&rect, tmp); + + Mem_Free(tmp); + return tex; +} + static GfxResourceID Gfx_AllocTexture(struct Bitmap* bmp, cc_uint8 flags, cc_bool mipmaps) { + for (int i = 0; i < TEXTURES_MAX_COUNT; i++) + { + if (textures[i].width) continue; + return AllocTextureAt(i, bmp); + } + + Platform_LogConst("No room for more textures"); return NULL; } void Gfx_BindTexture(GfxResourceID texId) { + if (!texId) texId = white_square; + active_tex = (GPUTexture*)texId; } void Gfx_DeleteTexture(GfxResourceID* texId) { GfxResourceID data = *texId; - if (data) Mem_Free(data); + if (!data) return; + GPUTexture* tex = (GPUTexture*)data; + + for (int i = tex->line; i < tex->line + tex->height; i++) + { + VRAM_UnUsed(i); + } + tex->width = 0; tex->height = 0; *texId = NULL; } @@ -133,7 +268,7 @@ void Gfx_UpdateTexture(GfxResourceID texId, int x, int y, struct Bitmap* part, i } void Gfx_UpdateTexturePart(GfxResourceID texId, int x, int y, struct Bitmap* part, cc_bool mipmaps) { - // TODO + Gfx_UpdateTexture(texId, x, y, part, part->width, mipmaps); } void Gfx_EnableMipmaps(void) { } @@ -387,17 +522,19 @@ static void Transform(Vec3* result, struct VertexTextured* a, const struct Matri float x = a->x * mat->row1.x + a->y * mat->row2.x + a->z * mat->row3.x + mat->row4.x; float y = a->x * mat->row1.y + a->y * mat->row2.y + a->z * mat->row3.y + mat->row4.y; float z = a->x * mat->row1.z + a->y * mat->row2.z + a->z * mat->row3.z + mat->row4.z; + float w = a->x * mat->row1.w + a->y * mat->row2.w + a->z * mat->row3.w + mat->row4.w; - result->x = x * (320/2) + (320/2); - result->y = y * -(240/2) + (240/2); - result->z = z * OT_LENGTH; + result->x = (x/w) * (320/2) + (320/2); + result->y = (y/w) * -(240/2) + (240/2); + result->z = (z/w) * OT_LENGTH; } cc_bool VERTEX_LOGGING; -static void DrawQuads(int verticesCount, int startVertex) { +static void DrawColouredQuads(int verticesCount, int startVertex) { + return; for (int i = 0; i < verticesCount; i += 4) { - struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + struct VertexColoured* v = (struct VertexColoured*)gfx_vertices + startVertex + i; POLY_F4* poly = new_primitive(sizeof(POLY_F4)); setPolyF4(poly); @@ -413,18 +550,55 @@ static void DrawQuads(int verticesCount, int startVertex) { poly->x2 = coords[2].x; poly->y2 = coords[2].y; poly->x3 = coords[3].x; poly->y3 = coords[3].y; + int p = (coords[0].z + coords[1].z + coords[2].z + coords[3].z) / 4; + if (p < 0 || p >= OT_LENGTH) continue; + int X = v[0].x, Y = v[0].y, Z = v[0].z; - if (VERTEX_LOGGING) Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z); + //if (VERTEX_LOGGING) Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z); X = poly->x1; Y = poly->y1, Z = coords[0].z; poly->r0 = PackedCol_R(v->Col); poly->g0 = PackedCol_G(v->Col); poly->b0 = PackedCol_B(v->Col); + //if (VERTEX_LOGGING) Platform_Log4("OUT: %i, %i, %i (%i)", &X, &Y, &Z, &p); - int p = (coords[0].z + coords[1].z + coords[2].z + coords[3].z) / 4; - if (VERTEX_LOGGING) Platform_Log4("OUT: %i, %i, %i (%i)", &X, &Y, &Z, &p); + addPrim(&buffer->ot[p >> 2], poly); + } +} + +static void DrawTexturedQuads(int verticesCount, int startVertex) { + for (int i = 0; i < verticesCount; i += 4) + { + struct VertexTextured* v = (struct VertexTextured*)gfx_vertices + startVertex + i; + + POLY_FT4* poly = new_primitive(sizeof(POLY_FT4)); + setPolyFT4(poly); + poly->tpage = active_tex->tpage; + poly->clut = 0; + + Vec3 coords[4]; + Transform(&coords[0], &v[0], &mvp); + Transform(&coords[1], &v[1], &mvp); + Transform(&coords[2], &v[2], &mvp); + Transform(&coords[3], &v[3], &mvp); + poly->x0 = coords[1].x; poly->y0 = coords[1].y; poly->u0 = (int)(v[1].U * active_tex->width) % active_tex->width; poly->v0 = (int)(v[1].V * active_tex->height) % active_tex->height + active_tex->line; + poly->x1 = coords[0].x; poly->y1 = coords[0].y; poly->u1 = (int)(v[0].U * active_tex->width) % active_tex->width; poly->v1 = (int)(v[0].V * active_tex->height) % active_tex->height + active_tex->line; + poly->x2 = coords[2].x; poly->y2 = coords[2].y; poly->u2 = (int)(v[2].U * active_tex->width) % active_tex->width; poly->v2 = (int)(v[2].V * active_tex->height) % active_tex->height + active_tex->line; + poly->x3 = coords[3].x; poly->y3 = coords[3].y; poly->u3 = (int)(v[3].U * active_tex->width) % active_tex->width; poly->v3 = (int)(v[3].V * active_tex->height) % active_tex->height + active_tex->line; + + int p = (coords[0].z + coords[1].z + coords[2].z + coords[3].z) / 4; if (p < 0 || p >= OT_LENGTH) continue; + + int X = v[0].x, Y = v[0].y, Z = v[0].z; + //if (VERTEX_LOGGING) Platform_Log3("IN: %i, %i, %i", &X, &Y, &Z); + X = poly->x1; Y = poly->y1, Z = coords[0].z; + + poly->r0 = PackedCol_R(v->Col); + poly->g0 = PackedCol_G(v->Col); + poly->b0 = PackedCol_B(v->Col); + //if (VERTEX_LOGGING) Platform_Log4("OUT: %i, %i, %i (%i)", &X, &Y, &Z, &p); + addPrim(&buffer->ot[p >> 2], poly); } } @@ -498,17 +672,23 @@ static void DrawQuads(int verticesCount, int startVertex) { }*/ void Gfx_DrawVb_IndexedTris_Range(int verticesCount, int startVertex) { - if (gfx_format == VERTEX_FORMAT_COLOURED) return; - DrawQuads(verticesCount, startVertex); + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + DrawTexturedQuads(verticesCount, startVertex); + } else { + DrawColouredQuads(verticesCount, startVertex); + } } void Gfx_DrawVb_IndexedTris(int verticesCount) { - if (gfx_format == VERTEX_FORMAT_COLOURED) return; - DrawQuads(verticesCount, 0); + if (gfx_format == VERTEX_FORMAT_TEXTURED) { + DrawTexturedQuads(verticesCount, 0); + } else { + DrawColouredQuads(verticesCount, 0); + } } void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) { - DrawQuads(verticesCount, startVertex); + DrawTexturedQuads(verticesCount, startVertex); } diff --git a/src/Graphics_PS3.c b/src/Graphics_PS3.c index 63c9d4e9d..8bcd00447 100644 --- a/src/Graphics_PS3.c +++ b/src/Graphics_PS3.c @@ -711,4 +711,4 @@ void Gfx_DrawVb_IndexedTris(int verticesCount) {/* TODO */ void Gfx_DrawIndexedTris_T2fC4b(int verticesCount, int startVertex) {/* TODO */ rsxDrawVertexArray(context, GCM_TYPE_QUADS, startVertex, verticesCount); } -#endif \ No newline at end of file +#endif diff --git a/src/Graphics_PSP.c b/src/Graphics_PSP.c index 308c0884b..b382f4f3b 100644 --- a/src/Graphics_PSP.c +++ b/src/Graphics_PSP.c @@ -44,7 +44,7 @@ static void guInit(void) { sceGuOffset(2048 - (SCREEN_WIDTH / 2), 2048 - (SCREEN_HEIGHT / 2)); sceGuViewport(2048, 2048, SCREEN_WIDTH, SCREEN_HEIGHT); sceGuDepthRange(65535,0); - sceGuFrontFace(GU_CW); + sceGuFrontFace(GU_CCW); sceGuShadeModel(GU_SMOOTH); sceGuDisable(GU_TEXTURE_2D); @@ -166,7 +166,7 @@ void Gfx_BindTexture(GfxResourceID texId) { *-----------------------------------------------------State management----------------------------------------------------* *#########################################################################################################################*/ static PackedCol gfx_clearColor; -void Gfx_SetFaceCulling(cc_bool enabled) { /*GU_Toggle(GU_CULL_FACE); */ } // TODO: Fix? GU_CCW instead?? +void Gfx_SetFaceCulling(cc_bool enabled) { GU_Toggle(GU_CULL_FACE); } void Gfx_SetAlphaBlending(cc_bool enabled) { GU_Toggle(GU_BLEND); } void Gfx_SetAlphaArgBlend(cc_bool enabled) { } @@ -233,8 +233,25 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ +static BitmapCol* PSP_GetRow(struct Bitmap* bmp, int y, void* ctx) { + cc_uint8* fb = (cc_uint8*)ctx; + return (BitmapCol*)(fb + y * BUFFER_WIDTH * 4); +} + cc_result Gfx_TakeScreenshot(struct Stream* output) { - return ERR_NOT_SUPPORTED; + int fbWidth, fbFormat; + void* fb; + + int res = sceDisplayGetFrameBuf(&fb, &fbWidth, &fbFormat, PSP_DISPLAY_SETBUF_NEXTFRAME); + if (res < 0) return res; + if (!fb) return ERR_NOT_SUPPORTED; + + struct Bitmap bmp; + bmp.scan0 = NULL; + bmp.width = SCREEN_WIDTH; + bmp.height = SCREEN_HEIGHT; + + return Png_Encode(&bmp, output, PSP_GetRow, false, fb); } void Gfx_GetApiInfo(cc_string* info) { diff --git a/src/Graphics_PSVita.c b/src/Graphics_PSVita.c index 2ef543520..fbfc41d9d 100644 --- a/src/Graphics_PSVita.c +++ b/src/Graphics_PSVita.c @@ -563,8 +563,9 @@ void Gfx_Create(void) { if (!Gfx.Created) InitGPU(); in_scene = false; - Gfx.MaxTexWidth = 512; - Gfx.MaxTexHeight = 512; + Gfx.MaxTexWidth = 1024; + Gfx.MaxTexHeight = 1024; + Gfx.MaxTexSize = 512 * 512; Gfx.Created = true; gfx_vsync = true; @@ -624,15 +625,11 @@ static void GPUTexture_Unref(GfxResourceID* resource) { struct GPUTexture* tex = (struct GPUTexture*)(*resource); if (!tex) return; *resource = NULL; - - cc_uintptr addr = tex; - Platform_Log1("TEX UNREF %h", &addr); + LinkedList_Append(tex, del_textures_head, del_textures_tail); } static void GPUTexture_Free(struct GPUTexture* tex) { - cc_uintptr addr = tex; - Platform_Log1("TEX DELETE %h", &addr); FreeGPUMemory(tex->uid); Mem_Free(tex); } @@ -838,9 +835,6 @@ struct GPUBuffer* GPUBuffer_Alloc(int size) { buffer->data = AllocGPUMemory(size, SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, SCE_GXM_MEMORY_ATTRIB_READ, &buffer->uid); - - cc_uintptr addr = buffer->data; - Platform_Log2("VB ALLOC %h = %i bytes", &addr, &size); return buffer; } @@ -851,14 +845,10 @@ static void GPUBuffer_Unref(GfxResourceID* resource) { if (!buf) return; *resource = NULL; - cc_uintptr addr = buf; - Platform_Log1("VB UNREF %h", &addr); LinkedList_Append(buf, del_buffers_head, del_buffers_tail); } static void GPUBuffer_Free(struct GPUBuffer* buf) { - cc_uintptr addr = buf; - Platform_Log1("VB DELETE %h", &addr); FreeGPUMemory(buf->uid); Mem_Free(buf); } diff --git a/src/Http_Web.c b/src/Http_Web.c index fe15a6516..c6374acec 100644 --- a/src/Http_Web.c +++ b/src/Http_Web.c @@ -139,7 +139,7 @@ static void Http_Init(void) { Http_InitCommon(); /* If this webpage is https://, browsers deny any http:// downloading */ httpsOnly = interop_IsHttpsOnly(); - startTime = DateTime_CurrentUTC_MS(); + startTime = DateTime_CurrentUTC(); RequestList_Init(&queuedReqs); RequestList_Init(&workingReqs); diff --git a/src/Http_Worker.c b/src/Http_Worker.c index e5f7b170a..b7c0a625d 100644 --- a/src/Http_Worker.c +++ b/src/Http_Worker.c @@ -1164,6 +1164,24 @@ static cc_result HttpBackend_Do(struct HttpRequest* req, cc_string* url) { CFRelease(request); return result; } +#elif !defined CC_BUILD_NETWORKING +/*########################################################################################################################* +*------------------------------------------------------Null backend-------------------------------------------------------* +*#########################################################################################################################*/ +#include "Errors.h" + +static cc_bool HttpBackend_DescribeError(cc_result res, cc_string* dst) { + return false; +} + +static void HttpBackend_Init(void) { } + +static void Http_AddHeader(struct HttpRequest* req, const char* key, const cc_string* value) { } + +static cc_result HttpBackend_Do(struct HttpRequest* req, cc_string* url) { + req->progress = 100; + return ERR_NOT_SUPPORTED; +} #endif diff --git a/src/Input.c b/src/Input.c index a648ef063..6edcfb66a 100644 --- a/src/Input.c +++ b/src/Input.c @@ -414,6 +414,54 @@ static void KeyBind_Init(void) { } +/*########################################################################################################################* +*---------------------------------------------------------Gamepad---------------------------------------------------------* +*#########################################################################################################################*/ +#define GAMEPAD_BEG_BTN CCPAD_A +static float pad_holdtime[INPUT_COUNT - GAMEPAD_BEG_BTN]; + +int Gamepad_AxisBehaviour[2] = { AXIS_BEHAVIOUR_MOVEMENT, AXIS_BEHAVIOUR_CAMERA }; +int Gamepad_AxisSensitivity[2] = { AXIS_SENSI_NORMAL, AXIS_SENSI_NORMAL }; +static const float axis_sensiFactor[] = { 0.25f, 0.5f, 1.0f, 2.0f, 4.0f }; + +void Gamepad_SetButton(int btn, int pressed) { + /* Reset hold tracking time */ + if (pressed && !Input.Pressed[btn]) pad_holdtime[btn - GAMEPAD_BEG_BTN] = 0; + + Input_SetNonRepeatable(btn, pressed); +} + +void Gamepad_SetAxis(int axis, float x, float y, double delta) { + if (!Input.RawMode) return; + + if (Gamepad_AxisBehaviour[axis] == AXIS_BEHAVIOUR_MOVEMENT) { + if (x == 0 && y == 0) return; + + Input.JoystickMovement = true; + Input.JoystickAngle = Math_Atan2(x, -y); + } else { + int sensi = Gamepad_AxisSensitivity[axis]; + float scale = delta * 60.0 * axis_sensiFactor[sensi]; + Event_RaiseRawMove(&ControllerEvents.RawMoved, x * scale, y * scale); + } +} + +void Gamepad_Tick(double delta) { + int btn; + + for (btn = GAMEPAD_BEG_BTN; btn < INPUT_COUNT; btn++) + { + if (!Input.Pressed[btn]) continue; + pad_holdtime[btn - GAMEPAD_BEG_BTN] += delta; + if (pad_holdtime[btn - GAMEPAD_BEG_BTN] < 1.0) continue; + + /* Held for over a second, trigger a fake press */ + pad_holdtime[btn - GAMEPAD_BEG_BTN] = 0; + Input_SetPressed(btn); + } +} + + /*########################################################################################################################* *---------------------------------------------------------Hotkeys---------------------------------------------------------* *#########################################################################################################################*/ diff --git a/src/Input.h b/src/Input.h index bd2c9715d..38d5ad316 100644 --- a/src/Input.h +++ b/src/Input.h @@ -102,15 +102,16 @@ void Input_Clear(void); #define Input_IsEscapeButton(btn) ((btn) == CCKEY_ESCAPE || (btn) == CCPAD_SELECT) #if defined CC_BUILD_HAIKU -/* Haiku uses ALT instead of CTRL for clipboard and stuff */ -#define Input_IsActionPressed() Input_IsAltPressed() + /* Haiku uses ALT instead of CTRL for clipboard and stuff */ + #define Input_IsActionPressed() Input_IsAltPressed() #elif defined CC_BUILD_DARWIN -/* macOS uses CMD instead of CTRL for clipboard and stuff */ -#define Input_IsActionPressed() Input_IsWinPressed() + /* macOS uses CMD instead of CTRL for clipboard and stuff */ + #define Input_IsActionPressed() Input_IsWinPressed() #else -#define Input_IsActionPressed() Input_IsCtrlPressed() + #define Input_IsActionPressed() Input_IsCtrlPressed() #endif + #ifdef CC_BUILD_TOUCH #define INPUT_MAX_POINTERS 32 enum INPUT_MODE { INPUT_MODE_PLACE, INPUT_MODE_DELETE, INPUT_MODE_NONE, INPUT_MODE_COUNT }; @@ -184,6 +185,23 @@ CC_API cc_bool KeyBind_IsPressed(KeyBind binding); /* Set the key that the given key binding is bound to. (also updates options list) */ void KeyBind_Set(KeyBind binding, int key, cc_uint8* binds); + +/* Gamepad axes. Default behaviour is: */ +/* - left axis: player movement */ +/* - right axis: camera movement */ +enum PAD_AXIS { PAD_AXIS_LEFT, PAD_AXIS_RIGHT }; +enum AXIS_SENSITIVITY { AXIS_SENSI_LOWER, AXIS_SENSI_LOW, AXIS_SENSI_NORMAL, AXIS_SENSI_HIGH, AXIS_SENSI_HIGHER }; +enum AXIS_BEHAVIOUR { AXIS_BEHAVIOUR_MOVEMENT, AXIS_BEHAVIOUR_CAMERA }; +extern int Gamepad_AxisBehaviour[2]; +extern int Gamepad_AxisSensitivity[2]; + +/* Sets value of the given gamepad button */ +void Gamepad_SetButton(int btn, int pressed); +/* Sets value of the given axis */ +void Gamepad_SetAxis(int axis, float x, float y, double delta); +void Gamepad_Tick(double delta); + + /* whether to leave text input open for user to enter further input */ #define HOTKEY_FLAG_STAYS_OPEN 0x01 /* Whether the hotkey was auto defined (e.g. by server) */ @@ -219,6 +237,7 @@ void StoredHotkeys_Remove(int trigger, cc_uint8 modifiers); /* Adds the given hotkey from options. */ void StoredHotkeys_Add(int trigger, cc_uint8 modifiers, cc_bool moreInput, const cc_string* text); + cc_bool InputHandler_SetFOV(int fov); cc_bool Input_HandleMouseWheel(float delta); void InputHandler_Tick(void); diff --git a/src/LBackend.c b/src/LBackend.c index 28f7610b2..dc55743e5 100644 --- a/src/LBackend.c +++ b/src/LBackend.c @@ -534,7 +534,7 @@ void LBackend_CheckboxDraw(struct LCheckbox* w) { /*########################################################################################################################* *------------------------------------------------------InputWidget--------------------------------------------------------* *#########################################################################################################################*/ -static TimeMS caretStart; +static cc_uint64 caretStart; static Rect2D caretRect, lastCaretRect; #define Rect2D_Equals(a, b) a.x == b.x && a.y == b.y && a.Width == b.Width && a.Height == b.Height @@ -620,7 +620,7 @@ void LBackend_InputTick(struct LInput* w) { Rect2D r; if (!caretStart) return; - elapsed = (int)(DateTime_CurrentUTC_MS() - caretStart); + elapsed = Stopwatch_ElapsedMS(caretStart, Stopwatch_Measure()); caretShow = (elapsed % 1000) < 500; if (caretShow == w->caretShow) return; @@ -641,7 +641,7 @@ void LBackend_InputTick(struct LInput* w) { void LBackend_InputSelect(struct LInput* w, int idx, cc_bool wasSelected) { struct OpenKeyboardArgs args; - caretStart = DateTime_CurrentUTC_MS(); + caretStart = Stopwatch_Measure(); w->caretShow = true; LInput_MoveCaretToCursor(w, idx); LBackend_MarkDirty(w); diff --git a/src/LScreens.c b/src/LScreens.c index ea611bf06..a4a5e25b5 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -1012,9 +1012,11 @@ static void CheckResourcesScreen_Next(void* w) { } static void CheckResourcesScreen_AddWidgets(struct CheckResourcesScreen* s) { + const char* line1_msg = Resources_MissingRequired ? "Some required resources weren't found" + : "Some optional resources weren't found"; s->lblStatus.small = true; - LLabel_Add(s, &s->lblLine1, "Some required resources weren't found", cres_lblLine1); + LLabel_Add(s, &s->lblLine1, line1_msg, cres_lblLine1); LLabel_Add(s, &s->lblLine2, "Okay to download?", cres_lblLine2); LLabel_Add(s, &s->lblStatus, "", cres_lblStatus); @@ -1030,7 +1032,7 @@ static void CheckResourcesScreen_Activated(struct LScreen* s_) { float size; CheckResourcesScreen_AddWidgets(s); - size = Resources_Size / 1024.0f; + size = Resources_MissingSize / 1024.0f; String_InitArray(str, buffer); String_Format1(&str, "&eDownload size: %f2 megabytes", &size); LLabel_SetText(&s->lblStatus, &str); @@ -1121,7 +1123,7 @@ static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, in String_InitArray(str, strBuffer); count = Fetcher_Downloaded + 1; - String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_Count); + String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_MissingCount); if (String_Equals(&str, &s->lblStatus.text)) return; LLabel_SetText(&s->lblStatus, &str); @@ -1600,9 +1602,9 @@ static void UpdatesScreen_Format(struct LLabel* lbl, const char* prefix, cc_uint if (!timestamp) { String_AppendConst(&str, "&cCheck failed"); } else { - now = DateTime_CurrentUTC_MS() - UNIX_EPOCH; + now = DateTime_CurrentUTC() - UNIX_EPOCH_SECONDS; /* must divide as cc_uint64, int delta overflows after 26 days */ - delta = (int)((now / 1000) - timestamp); + delta = (int)(now - timestamp); UpdatesScreen_FormatTime(&str, delta); } LLabel_SetText(lbl, &str); diff --git a/src/Launcher.c b/src/Launcher.c index 390ead03b..18c939cbc 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -260,7 +260,7 @@ void Launcher_Run(void) { #ifdef CC_BUILD_RESOURCES Resources_CheckExistence(); - if (Resources_Count) { + if (Resources_MissingCount) { CheckResourcesScreen_SetActive(); } else { MainScreen_SetActive(); diff --git a/src/Logger.c b/src/Logger.c index 2957adfb1..06795c5d3 100644 --- a/src/Logger.c +++ b/src/Logger.c @@ -8,17 +8,17 @@ #include "Utils.h" #if defined CC_BUILD_WEB -/* Can't see native CPU state with javascript */ + /* Can't see native CPU state with javascript */ #elif defined CC_BUILD_WIN -#define WIN32_LEAN_AND_MEAN -#define NOSERVICE -#define NOMCX -#define NOIME -#define CUR_PROCESS_HANDLE ((HANDLE)-1) /* GetCurrentProcess() always returns -1 */ - -#include -#include -static HANDLE curProcess = CUR_PROCESS_HANDLE; + #define WIN32_LEAN_AND_MEAN + #define NOSERVICE + #define NOMCX + #define NOIME + #define CUR_PROCESS_HANDLE ((HANDLE)-1) /* GetCurrentProcess() always returns -1 */ + + #include + #include + static HANDLE curProcess = CUR_PROCESS_HANDLE; #elif defined CC_BUILD_OPENBSD || defined CC_BUILD_HAIKU || defined CC_BUILD_SERENITY #include /* These operating systems don't provide sys/ucontext.h */ @@ -26,20 +26,21 @@ static HANDLE curProcess = CUR_PROCESS_HANDLE; #elif defined CC_BUILD_OS2 #include #include <386/ucontext.h> + #elif defined CC_BUILD_LINUX || defined CC_BUILD_ANDROID -/* Need to define this to get REG_ constants */ -#define _GNU_SOURCE -#include -#include + /* Need to define this to get REG_ constants */ + #define _GNU_SOURCE + #include + #include #elif defined CC_BUILD_POSIX -#include -#include + #include + #include #endif + #ifdef CC_BUILD_DARWIN /* Need this to detect macOS < 10.4, and switch to NS* api instead if so */ #include #endif - /* Only show up to 50 frames in backtrace */ #define MAX_BACKTRACE_FRAMES 50 diff --git a/src/Menus.c b/src/Menus.c index 4ace2f29f..d6a247be5 100644 --- a/src/Menus.c +++ b/src/Menus.c @@ -2728,9 +2728,9 @@ static void ClassicOptionsScreen_SetViewDist(const cc_string* v) { Game_UserSetViewDistance(dist); } -static void ClassicOptionsScreen_GetPhysics(cc_string* v) { Menu_GetBool(v, Physics.Enabled); } -static void ClassicOptionsScreen_SetPhysics(const cc_string* v) { - Physics_SetEnabled(Menu_SetBool(v, OPT_BLOCK_PHYSICS)); +static void ClassicOptionsScreen_GetAnaglyph(cc_string* v) { Menu_GetBool(v, Game_Anaglyph3D); } +static void ClassicOptionsScreen_SetAnaglyph(const cc_string* v) { + Game_Anaglyph3D = Menu_SetBool(v, OPT_ANAGLYPH3D); } static void ClassicOptionsScreen_GetSounds(cc_string* v) { Menu_GetBool(v, Audio_SoundsVolume > 0); } @@ -2763,8 +2763,8 @@ static void ClassicOptionsScreen_InitWidgets(struct MenuOptionsScreen* s) { ClassicOptionsScreen_GetInvert, ClassicOptionsScreen_SetInvert }, { -1, -50, "Render distance", MenuOptionsScreen_Enum, ClassicOptionsScreen_GetViewDist, ClassicOptionsScreen_SetViewDist }, - { -1, 0, "Block physics", MenuOptionsScreen_Bool, - ClassicOptionsScreen_GetPhysics, ClassicOptionsScreen_SetPhysics }, + { -1, 0, "3D anaglyph", MenuOptionsScreen_Bool, + ClassicOptionsScreen_GetAnaglyph, ClassicOptionsScreen_SetAnaglyph }, { 1, -150, "Sound", MenuOptionsScreen_Bool, ClassicOptionsScreen_GetSounds, ClassicOptionsScreen_SetSounds }, diff --git a/src/Model.c b/src/Model.c index 1d0dd2678..aa24c21ba 100644 --- a/src/Model.c +++ b/src/Model.c @@ -98,7 +98,8 @@ void Model_Render(struct Model* model, struct Entity* e) { Vec3 pos = e->Position; if (model->bobbing) pos.y += e->Anim.BobbingModel; /* Original classic offsets models slightly into ground */ - if (Game_ClassicMode) pos.y -= 1.5f / 16.0f; + if (Game_ClassicMode && (e->Flags & ENTITY_FLAG_CLASSIC_ADJUST)) + pos.y -= 1.5f / 16.0f; Model_SetupState(model, e); Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED); diff --git a/src/Options.h b/src/Options.h index b4d1716c2..62672a86f 100644 --- a/src/Options.h +++ b/src/Options.h @@ -79,6 +79,7 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 #define OPT_DPI_SCALING "win-dpi-scaling" #define OPT_GAME_VERSION "game-version" #define OPT_INV_SCROLLBAR_SCALE "inv-scrollbar-scale" +#define OPT_ANAGLYPH3D "anaglyph-3d" #define OPT_SELECTED_BLOCK_OUTLINE_COLOR "selected-block-outline-color" #define OPT_SELECTED_BLOCK_OUTLINE_OPACITY "selected-block-outline-opacity" diff --git a/src/Particle.c b/src/Particle.c index 4c3e3b81c..3228758d3 100644 --- a/src/Particle.c +++ b/src/Particle.c @@ -187,6 +187,30 @@ static void Rain_Tick(double delta) { } } +void Particles_RainSnowEffect(float x, float y, float z) { + struct Particle* p; + int i, type; + + for (i = 0; i < 2; i++) { + if (rain_count == PARTICLES_MAX) Rain_RemoveAt(0); + p = &rain_Particles[rain_count++]; + + p->velocity.x = Random_Float(&rnd) * 0.8f - 0.4f; /* [-0.4, 0.4] */ + p->velocity.z = Random_Float(&rnd) * 0.8f - 0.4f; + p->velocity.y = Random_Float(&rnd) + 0.4f; + + p->lastPos.x = x + Random_Float(&rnd); /* [0.0, 1.0] */ + p->lastPos.y = y + Random_Float(&rnd) * 0.1f + 0.01f; + p->lastPos.z = z + Random_Float(&rnd); + + p->nextPos = p->lastPos; + p->lifetime = 40.0f; + + type = Random_Next(&rnd, 30); + p->size = type >= 28 ? 2 : (type >= 25 ? 4 : 3); + } +} + /*########################################################################################################################* *------------------------------------------------------Terrain particle---------------------------------------------------* @@ -291,9 +315,92 @@ static void Terrain_Tick(double delta) { } } +void Particles_BreakBlockEffect(IVec3 coords, BlockID old, BlockID now) { + struct TerrainParticle* p; + TextureLoc loc; + int texIndex; + TextureRec baseRec, rec; + Vec3 origin, minBB, maxBB; + + /* texture UV variables */ + float uScale, vScale, maxU2, maxV2; + int minX, minZ, maxX, maxZ; + int minU, minV, maxU, maxV; + int maxUsedU, maxUsedV; + + /* per-particle variables */ + float cellX, cellY, cellZ; + Vec3 cell; + int x, y, z, type; + + if (now != BLOCK_AIR || Blocks.Draw[old] == DRAW_GAS) return; + IVec3_ToVec3(&origin, &coords); + loc = Block_Tex(old, FACE_XMIN); + + baseRec = Atlas1D_TexRec(loc, 1, &texIndex); + uScale = (1.0f/16.0f); vScale = (1.0f/16.0f) * Atlas1D.InvTileSize; + + minBB = Blocks.MinBB[old]; maxBB = Blocks.MaxBB[old]; + minX = (int)(minBB.x * 16); maxX = (int)(maxBB.x * 16); + minZ = (int)(minBB.z * 16); maxZ = (int)(maxBB.z * 16); + + minU = min(minX, minZ); minV = (int)(16 - maxBB.y * 16); + maxU = min(maxX, maxZ); maxV = (int)(16 - minBB.y * 16); + /* This way we can avoid creating particles which outside the bounds and need to be clamped */ + maxUsedU = maxU; maxUsedV = maxV; + if (minU < 12 && maxU > 12) maxUsedU = 12; + if (minV < 12 && maxV > 12) maxUsedV = 12; + + #define GRID_SIZE 4 + /* gridOffset gives the centre of the cell on a grid */ + #define CELL_CENTRE ((1.0f / GRID_SIZE) * 0.5f) + + maxU2 = baseRec.U1 + maxU * uScale; + maxV2 = baseRec.V1 + maxV * vScale; + for (x = 0; x < GRID_SIZE; x++) { + for (y = 0; y < GRID_SIZE; y++) { + for (z = 0; z < GRID_SIZE; z++) { + + cellX = (float)x / GRID_SIZE; cellY = (float)y / GRID_SIZE; cellZ = (float)z / GRID_SIZE; + cell = Vec3_Create3(CELL_CENTRE + cellX, CELL_CENTRE / 2 + cellY, CELL_CENTRE + cellZ); + if (cell.x < minBB.x || cell.x > maxBB.x || cell.y < minBB.y + || cell.y > maxBB.y || cell.z < minBB.z || cell.z > maxBB.z) continue; + + if (terrain_count == PARTICLES_MAX) Terrain_RemoveAt(0); + p = &terrain_particles[terrain_count++]; + + /* centre random offset around [-0.2, 0.2] */ + p->base.velocity.x = CELL_CENTRE + (cellX - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); + p->base.velocity.y = CELL_CENTRE + (cellY - 0.0f) + (Random_Float(&rnd) * 0.4f - 0.2f); + p->base.velocity.z = CELL_CENTRE + (cellZ - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); + + rec = baseRec; + rec.U1 = baseRec.U1 + Random_Range(&rnd, minU, maxUsedU) * uScale; + rec.V1 = baseRec.V1 + Random_Range(&rnd, minV, maxUsedV) * vScale; + rec.U2 = rec.U1 + 4 * uScale; + rec.V2 = rec.V1 + 4 * vScale; + rec.U2 = min(rec.U2, maxU2) - 0.01f * uScale; + rec.V2 = min(rec.V2, maxV2) - 0.01f * vScale; + + Vec3_Add(&p->base.lastPos, &origin, &cell); + p->base.nextPos = p->base.lastPos; + p->base.lifetime = 0.3f + Random_Float(&rnd) * 1.2f; + + p->rec = rec; + p->texLoc = loc; + p->block = old; + type = Random_Next(&rnd, 30); + p->base.size = type >= 28 ? 12 : (type >= 25 ? 10 : 8); + } + } + } +} + + /*########################################################################################################################* *-------------------------------------------------------Custom particle---------------------------------------------------* *#########################################################################################################################*/ +#ifdef CC_BUILD_NETWORKING struct CustomParticle { struct Particle base; int effectId; @@ -388,139 +495,6 @@ static void Custom_Tick(double delta) { } } - -/*########################################################################################################################* -*--------------------------------------------------------Particles--------------------------------------------------------* -*#########################################################################################################################*/ -void Particles_Render(float t) { - if (!terrain_count && !rain_count && !custom_count) return; - - if (Gfx.LostContext) return; - if (!particles_VB) - particles_VB = Gfx_CreateDynamicVb(VERTEX_FORMAT_TEXTURED, PARTICLES_MAX * 4); - - Gfx_SetAlphaTest(true); - - Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED); - Terrain_Render(t); - Rain_Render(t); - Custom_Render(t); - - Gfx_SetAlphaTest(false); -} - -static void Particles_Tick(struct ScheduledTask* task) { - double delta = task->interval; - Terrain_Tick(delta); - Rain_Tick(delta); - Custom_Tick(delta); -} - -void Particles_BreakBlockEffect(IVec3 coords, BlockID old, BlockID now) { - struct TerrainParticle* p; - TextureLoc loc; - int texIndex; - TextureRec baseRec, rec; - Vec3 origin, minBB, maxBB; - - /* texture UV variables */ - float uScale, vScale, maxU2, maxV2; - int minX, minZ, maxX, maxZ; - int minU, minV, maxU, maxV; - int maxUsedU, maxUsedV; - - /* per-particle variables */ - float cellX, cellY, cellZ; - Vec3 cell; - int x, y, z, type; - - if (now != BLOCK_AIR || Blocks.Draw[old] == DRAW_GAS) return; - IVec3_ToVec3(&origin, &coords); - loc = Block_Tex(old, FACE_XMIN); - - baseRec = Atlas1D_TexRec(loc, 1, &texIndex); - uScale = (1.0f/16.0f); vScale = (1.0f/16.0f) * Atlas1D.InvTileSize; - - minBB = Blocks.MinBB[old]; maxBB = Blocks.MaxBB[old]; - minX = (int)(minBB.x * 16); maxX = (int)(maxBB.x * 16); - minZ = (int)(minBB.z * 16); maxZ = (int)(maxBB.z * 16); - - minU = min(minX, minZ); minV = (int)(16 - maxBB.y * 16); - maxU = min(maxX, maxZ); maxV = (int)(16 - minBB.y * 16); - /* This way we can avoid creating particles which outside the bounds and need to be clamped */ - maxUsedU = maxU; maxUsedV = maxV; - if (minU < 12 && maxU > 12) maxUsedU = 12; - if (minV < 12 && maxV > 12) maxUsedV = 12; - - #define GRID_SIZE 4 - /* gridOffset gives the centre of the cell on a grid */ - #define CELL_CENTRE ((1.0f / GRID_SIZE) * 0.5f) - - maxU2 = baseRec.U1 + maxU * uScale; - maxV2 = baseRec.V1 + maxV * vScale; - for (x = 0; x < GRID_SIZE; x++) { - for (y = 0; y < GRID_SIZE; y++) { - for (z = 0; z < GRID_SIZE; z++) { - - cellX = (float)x / GRID_SIZE; cellY = (float)y / GRID_SIZE; cellZ = (float)z / GRID_SIZE; - cell = Vec3_Create3(CELL_CENTRE + cellX, CELL_CENTRE / 2 + cellY, CELL_CENTRE + cellZ); - if (cell.x < minBB.x || cell.x > maxBB.x || cell.y < minBB.y - || cell.y > maxBB.y || cell.z < minBB.z || cell.z > maxBB.z) continue; - - if (terrain_count == PARTICLES_MAX) Terrain_RemoveAt(0); - p = &terrain_particles[terrain_count++]; - - /* centre random offset around [-0.2, 0.2] */ - p->base.velocity.x = CELL_CENTRE + (cellX - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); - p->base.velocity.y = CELL_CENTRE + (cellY - 0.0f) + (Random_Float(&rnd) * 0.4f - 0.2f); - p->base.velocity.z = CELL_CENTRE + (cellZ - 0.5f) + (Random_Float(&rnd) * 0.4f - 0.2f); - - rec = baseRec; - rec.U1 = baseRec.U1 + Random_Range(&rnd, minU, maxUsedU) * uScale; - rec.V1 = baseRec.V1 + Random_Range(&rnd, minV, maxUsedV) * vScale; - rec.U2 = rec.U1 + 4 * uScale; - rec.V2 = rec.V1 + 4 * vScale; - rec.U2 = min(rec.U2, maxU2) - 0.01f * uScale; - rec.V2 = min(rec.V2, maxV2) - 0.01f * vScale; - - Vec3_Add(&p->base.lastPos, &origin, &cell); - p->base.nextPos = p->base.lastPos; - p->base.lifetime = 0.3f + Random_Float(&rnd) * 1.2f; - - p->rec = rec; - p->texLoc = loc; - p->block = old; - type = Random_Next(&rnd, 30); - p->base.size = type >= 28 ? 12 : (type >= 25 ? 10 : 8); - } - } - } -} - -void Particles_RainSnowEffect(float x, float y, float z) { - struct Particle* p; - int i, type; - - for (i = 0; i < 2; i++) { - if (rain_count == PARTICLES_MAX) Rain_RemoveAt(0); - p = &rain_Particles[rain_count++]; - - p->velocity.x = Random_Float(&rnd) * 0.8f - 0.4f; /* [-0.4, 0.4] */ - p->velocity.z = Random_Float(&rnd) * 0.8f - 0.4f; - p->velocity.y = Random_Float(&rnd) + 0.4f; - - p->lastPos.x = x + Random_Float(&rnd); /* [0.0, 1.0] */ - p->lastPos.y = y + Random_Float(&rnd) * 0.1f + 0.01f; - p->lastPos.z = z + Random_Float(&rnd); - - p->nextPos = p->lastPos; - p->lifetime = 40.0f; - - type = Random_Next(&rnd, 30); - p->size = type >= 28 ? 2 : (type >= 25 ? 4 : 3); - } -} - void Particles_CustomEffect(int effectID, float x, float y, float z, float originX, float originY, float originZ) { struct CustomParticle* p; struct CustomParticleEffect* e = &Particles_CustomEffects[effectID]; @@ -568,6 +542,40 @@ void Particles_CustomEffect(int effectID, float x, float y, float z, float origi if (IntersectsBlock(&p->base, CustomParticle_CanPass)) custom_count--; } } +#else +static int custom_count; + +static void Custom_Render(float t) { } +static void Custom_Tick(double delta) { } +#endif + + +/*########################################################################################################################* +*--------------------------------------------------------Particles--------------------------------------------------------* +*#########################################################################################################################*/ +void Particles_Render(float t) { + if (!terrain_count && !rain_count && !custom_count) return; + + if (Gfx.LostContext) return; + if (!particles_VB) + particles_VB = Gfx_CreateDynamicVb(VERTEX_FORMAT_TEXTURED, PARTICLES_MAX * 4); + + Gfx_SetAlphaTest(true); + + Gfx_SetVertexFormat(VERTEX_FORMAT_TEXTURED); + Terrain_Render(t); + Rain_Render(t); + Custom_Render(t); + + Gfx_SetAlphaTest(false); +} + +static void Particles_Tick(struct ScheduledTask* task) { + double delta = task->interval; + Terrain_Tick(delta); + Rain_Tick(delta); + Custom_Tick(delta); +} /*########################################################################################################################* diff --git a/src/Platform.h b/src/Platform.h index 8049a4c5d..7c78eb0d6 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -24,8 +24,8 @@ typedef int cc_file; /* Origin points for when seeking in a file. */ /* NOTE: These have same values as SEEK_SET/SEEK_CUR/SEEK_END, do not change them */ enum File_SeekFrom { FILE_SEEKFROM_BEGIN, FILE_SEEKFROM_CURRENT, FILE_SEEKFROM_END }; -/* Number of milliseconds since 01/01/0001 to start of unix time. */ -#define UNIX_EPOCH 62135596800000ULL +/* Number of seconds since 01/01/0001 to start of unix time. */ +#define UNIX_EPOCH_SECONDS 62135596800ULL extern const cc_result ReturnCode_FileShareViolation; extern const cc_result ReturnCode_FileNotFound; @@ -166,8 +166,8 @@ void Platform_Log2(const char* format, const void* a1, const void* a2); void Platform_Log3(const char* format, const void* a1, const void* a2, const void* a3); void Platform_Log4(const char* format, const void* a1, const void* a2, const void* a3, const void* a4); -/* Returns the current UTC time, as number of milliseconds since 1/1/0001 */ -CC_API TimeMS DateTime_CurrentUTC_MS(void); +/* Returns the current UTC time, as number of seconds since 1/1/0001 */ +CC_API TimeMS DateTime_CurrentUTC(void); /* Returns the current local Time. */ CC_API void DateTime_CurrentLocal(struct DateTime* t); /* Takes a platform-specific stopwatch measurement. */ diff --git a/src/Platform_3DS.c b/src/Platform_3DS.c index 89fd1291d..0c40cd584 100644 --- a/src/Platform_3DS.c +++ b/src/Platform_3DS.c @@ -62,11 +62,10 @@ void Platform_Log(const char* msg, int len) { svcOutputDebugString("\n", 1); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_Dreamcast.c b/src/Platform_Dreamcast.c index f2ecccad7..095d06bdd 100644 --- a/src/Platform_Dreamcast.c +++ b/src/Platform_Dreamcast.c @@ -75,7 +75,7 @@ void Platform_Log(const char* msg, int len) { LogOnscreen(msg, len); } -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { uint32 secs, ms; timer_ms_gettime(&secs, &ms); @@ -86,7 +86,7 @@ TimeMS DateTime_CurrentUTC_MS(void) { if (boot_time < boot_time_2000) boot_time = boot_time_2024; cc_uint64 curSecs = boot_time + secs; - return (curSecs * 1000 + ms) + UNIX_EPOCH; + return curSecs + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_GCWii.c b/src/Platform_GCWii.c index 97acf81df..33519a7e8 100644 --- a/src/Platform_GCWii.c +++ b/src/Platform_GCWii.c @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef HW_RVL #include #endif @@ -42,30 +43,43 @@ const char* Platform_AppNameSuffix = " GameCube"; /*########################################################################################################################* *------------------------------------------------------Logging/Time-------------------------------------------------------* *#########################################################################################################################*/ -// dolphin recognises this function name (if loaded as .elf), and will patch it -// to also log the message to dolphin's console at OSREPORT-HLE log level -void CC_NOINLINE __write_console(int fd, const char* msg, const u32* size) { - write(STDOUT_FILENO, msg, *size); // this can be intercepted by libogc debug console +// To see these log messages: +// 1) In the UI, make sure 'Show log configuration' checkbox is checked in View menu +// 2) Make sure "OSReport EXI (OSREPORT)" log type is enabled +// 3) In the UI, make sure 'Show log' checkbox is checked in View menu +static void LogOverEXI(char* msg, int len) { + u32 cmd = 0x80000000 | (0x800400 << 6); // write flag, UART base address + + // https://hitmen.c02.at/files/yagcd/yagcd/chap10.html + // Try to acquire "MASK ROM"/"IPL" link + // Writing to the IPL is used for debug message logging + if (EXI_Lock(EXI_CHANNEL_0, EXI_DEVICE_1, NULL) == 0) return; + if (EXI_Select(EXI_CHANNEL_0, EXI_DEVICE_1, EXI_SPEED8MHZ) == 0) { + EXI_Unlock(EXI_CHANNEL_0); return; + } + + EXI_Imm( EXI_CHANNEL_0, &cmd, 4, EXI_WRITE, NULL); + EXI_Sync( EXI_CHANNEL_0); + EXI_ImmEx( EXI_CHANNEL_0, msg, len, EXI_WRITE); + EXI_Deselect(EXI_CHANNEL_0); + EXI_Unlock( EXI_CHANNEL_0); } + void Platform_Log(const char* msg, int len) { - char buffer[256]; - cc_string str = String_Init(buffer, 0, 254); // 2 characters (\n and \0) - u32 size; - - String_AppendAll(&str, msg, len); - buffer[str.length + 0] = '\n'; - buffer[str.length + 1] = '\0'; // needed to make Dolphin logger happy - - size = str.length + 1; // +1 for '\n' - __write_console(0, buffer, &size); - // TODO: Just use printf("%s", somehow ??? + char tmp[256 + 1]; + len = min(len, 256); + // See EXI_DeviceIPL.cpp in Dolphin, \r is what triggers buffered message to be logged + Mem_Copy(tmp, msg, len); tmp[len] = '\r'; + + LogOverEXI(tmp, len + 1); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { - struct timeval cur; - gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); +#define GCWII_EPOCH_ADJUST 946684800ULL // GameCube/Wii time epoch is year 2000, not 1970 + +TimeMS DateTime_CurrentUTC(void) { + u64 raw = gettime(); + u64 secs = ticks_to_secs(raw); + return secs + UNIX_EPOCH_SECONDS + GCWII_EPOCH_ADJUST; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -594,4 +608,4 @@ static cc_result GetMachineID(cc_uint32* key) { } #endif -#endif \ No newline at end of file +#endif diff --git a/src/Platform_N64.c b/src/Platform_N64.c index 7b7428caa..1419f94c1 100644 --- a/src/Platform_N64.c +++ b/src/Platform_N64.c @@ -48,11 +48,8 @@ void Platform_Log(const char* msg, int len) { write(STDERR_FILENO, "\n", 1); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { - struct timeval cur; - gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); +TimeMS DateTime_CurrentUTC(void) { + return 0; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -177,10 +174,6 @@ void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* *handle = NULL; } -void Thread_Start2(void* handle, Thread_StartFunc func) { - // TODO: actual multithreading ??? -} - void Thread_Detach(void* handle) { } @@ -270,10 +263,6 @@ void Platform_Init(void) { // TODO: Redesign Drawer2D to better handle this Options_SetBool(OPT_USE_CHAT_FONT, true); - //console_init(); - //console_set_render_mode(RENDER_AUTOMATIC); - //console_set_debug(true); - dfs_init(DFS_DEFAULT_LOCATION); timer_init(); rtc_init(); diff --git a/src/Platform_NDS.c b/src/Platform_NDS.c index 14f0d2b90..e1dc5e382 100644 --- a/src/Platform_NDS.c +++ b/src/Platform_NDS.c @@ -84,11 +84,10 @@ void Platform_Log(const char* msg, int len) { LogNocash(msg, len); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); // no usec on the DS + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_PS1.c b/src/Platform_PS1.c index e654127c9..4708a1534 100644 --- a/src/Platform_PS1.c +++ b/src/Platform_PS1.c @@ -8,6 +8,7 @@ #include "Window.h" #include "Utils.h" #include "Errors.h" +#include "Options.h" #include "PackedCol.h" #include #include @@ -16,7 +17,7 @@ #include #include #include -void exit(int code) { } // TODO how to fix +void exit(int code) { _boot(); } #include "_PlatformConsole.h" const cc_result ReturnCode_FileShareViolation = 1000000000; // not used @@ -39,8 +40,7 @@ void Platform_Log(const char* msg, int len) { printf("%s\n", tmp); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { return 0; } @@ -78,12 +78,20 @@ static void Stopwatch_Init(void) { /*########################################################################################################################* *-----------------------------------------------------Directory/File------------------------------------------------------* *#########################################################################################################################*/ +static const cc_string root_path = String_FromConst("cdrom:/"); + +static void GetNativePath(char* str, const cc_string* path) { + Mem_Copy(str, root_path.buffer, root_path.length); + str += root_path.length; + String_EncodeUtf8(str, path); +} + cc_result Directory_Create(const cc_string* path) { return ERR_NOT_SUPPORTED; } int File_Exists(const cc_string* path) { - return 0; + return ERR_NOT_SUPPORTED; } cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCallback callback) { @@ -93,9 +101,11 @@ cc_result Directory_Enum(const cc_string* dirPath, void* obj, Directory_EnumCall cc_result File_Open(cc_file* file, const cc_string* path) { return ERR_NOT_SUPPORTED; } + cc_result File_Create(cc_file* file, const cc_string* path) { return ERR_NOT_SUPPORTED; } + cc_result File_OpenOrCreate(cc_file* file, const cc_string* path) { return ERR_NOT_SUPPORTED; } @@ -109,10 +119,10 @@ cc_result File_Write(cc_file file, const void* data, cc_uint32 count, cc_uint32* } cc_result File_Close(cc_file file) { - return 0; + return ERR_NOT_SUPPORTED; } -cc_result File_Seek(cc_file file, int offset, int seekType) { +cc_result File_Seek(cc_file file, int offset, int seekType) { return ERR_NOT_SUPPORTED; } @@ -208,8 +218,10 @@ cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable) { *--------------------------------------------------------Platform---------------------------------------------------------* *#########################################################################################################################*/ void Platform_Init(void) { - ResetGraph( 0 ); + ResetGraph(0); Stopwatch_Init(); + + Options_SetBool(OPT_USE_CHAT_FONT, true); } void Platform_Free(void) { } diff --git a/src/Platform_PS2.c b/src/Platform_PS2.c index ada09b7c4..5be4c5480 100644 --- a/src/Platform_PS2.c +++ b/src/Platform_PS2.c @@ -56,11 +56,10 @@ void Platform_Log(const char* msg, int len) { _print("%s", tmp); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_PS3.c b/src/Platform_PS3.c index a70279d0c..b24744bd0 100644 --- a/src/Platform_PS3.c +++ b/src/Platform_PS3.c @@ -52,11 +52,10 @@ void Platform_Log(const char* msg, int len) { sysTtyWrite(STDOUT_FILENO, "\n", 1, &done); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -483,4 +482,4 @@ cc_bool Platform_DescribeError(cc_result res, cc_string* dst) { static cc_result GetMachineID(cc_uint32* key) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Platform_PSP.c b/src/Platform_PSP.c index 3667b5dee..014edd575 100644 --- a/src/Platform_PSP.c +++ b/src/Platform_PSP.c @@ -54,11 +54,10 @@ void Platform_Log(const char* msg, int len) { //File_Close(file); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct SceKernelTimeval cur; sceKernelLibcGettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_PSVita.c b/src/Platform_PSVita.c index e5e47c232..e002e5945 100644 --- a/src/Platform_PSVita.c +++ b/src/Platform_PSVita.c @@ -37,11 +37,10 @@ void Platform_Log(const char* msg, int len) { sceIoWrite(stdout_fd, msg, len); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.sec * 1000 + UNIX_EPOCH + (time.usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct SceKernelTimeval cur; sceKernelLibcGettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_Posix.c b/src/Platform_Posix.c index 91d81d5e2..4959c00bc 100644 --- a/src/Platform_Posix.c +++ b/src/Platform_Posix.c @@ -107,11 +107,10 @@ void Platform_Log(const char* msg, int len) { } #endif -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_Switch.c b/src/Platform_Switch.c index 846b61b27..381831a86 100644 --- a/src/Platform_Switch.c +++ b/src/Platform_Switch.c @@ -23,13 +23,10 @@ #include #include #include -#include #include #include #include "_PlatformConsole.h" - - const cc_result ReturnCode_FileShareViolation = 1000000000; // not used const cc_result ReturnCode_FileNotFound = ENOENT; const cc_result ReturnCode_SocketInProgess = EINPROGRESS; @@ -37,7 +34,6 @@ const cc_result ReturnCode_SocketWouldBlock = EWOULDBLOCK; const cc_result ReturnCode_DirectoryExists = EEXIST; const char* Platform_AppNameSuffix = " Switch"; - alignas(16) u8 __nx_exception_stack[0x1000]; u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); @@ -74,38 +70,37 @@ void __libnx_exception_handler(ThreadExceptionDump *ctx) cc_uint64 Stopwatch_ElapsedMicroseconds(cc_uint64 beg, cc_uint64 end) { if (end < beg) return 0; - return (end - beg) / 1000; + // See include/switch/arm/counter.h + // static inline u64 armTicksToNs(u64 tick) { return (tick * 625) / 12; } + return ((end - beg) * 625) / 12000; } cc_uint64 Stopwatch_Measure(void) { - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - return (cc_uint64)t.tv_sec * 1e9 + t.tv_nsec; + return armGetSystemTick(); } void Platform_Log(const char* msg, int len) { svcOutputDebugString(msg, len); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { - struct timeval cur; - gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); +TimeMS DateTime_CurrentUTC(void) { + u64 timestamp = 0; + timeGetCurrentTime(TimeType_Default, ×tamp); + return timestamp + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { - struct timeval cur; - struct tm loc_time; - gettimeofday(&cur, NULL); - localtime_r(&cur.tv_sec, &loc_time); + u64 timestamp = 0; + TimeCalendarTime calTime = { 0 }; + timeGetCurrentTime(TimeType_Default, ×tamp); + timeToCalendarTimeWithMyRule(timestamp, &calTime, NULL); - t->year = loc_time.tm_year + 1900; - t->month = loc_time.tm_mon + 1; - t->day = loc_time.tm_mday; - t->hour = loc_time.tm_hour; - t->minute = loc_time.tm_min; - t->second = loc_time.tm_sec; + t->year = calTime.year; + t->month = calTime.month; + t->day = calTime.day; + t->hour = calTime.hour; + t->minute = calTime.minute; + t->second = calTime.second; } @@ -228,7 +223,7 @@ cc_result File_Length(cc_file file, cc_uint32* len) { *--------------------------------------------------------Threading--------------------------------------------------------* *#########################################################################################################################*/ void Thread_Sleep(cc_uint32 milliseconds) { - cc_uint64 timeout_ns = milliseconds * (1000 * 1000); // to nanoseconds + cc_uint64 timeout_ns = (cc_uint64)milliseconds * (1000 * 1000); // to nanoseconds svcSleepThread(timeout_ns); } @@ -244,7 +239,11 @@ void Thread_Run(void** handle, Thread_StartFunc func, int stackSize, const char* threadStart(thread); } -void Thread_Detach(void* handle) { } +void Thread_Detach(void* handle) { + // threadClose frees up resources, **including the stack of the thread** + // Which obviously completely breaks the thread - so instead just accept + // that there will be a small memory leak when non-joined threads exit +} void Thread_Join(void* handle) { Thread* thread = (Thread*)handle; @@ -316,7 +315,6 @@ void Waitable_Wait(void* handle) { void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { struct WaitData* ptr = (struct WaitData*)handle; - cc_uint64 timeout_ns = (cc_uint64)milliseconds * (1000 * 1000); // to nanoseconds Mutex_Lock(&ptr->mutex); @@ -360,10 +358,8 @@ void Waitable_WaitFor(void* handle, cc_uint32 milliseconds) { union SocketAddress { struct sockaddr raw; struct sockaddr_in v4; - #ifdef AF_INET6 struct sockaddr_in6 v6; struct sockaddr_storage total; - #endif }; static cc_result ParseHost(const char* host, int port, cc_sockaddr* addrs, int* numValidAddrs) { @@ -420,7 +416,6 @@ cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* a return 0; } - #ifdef AF_INET6 if (inet_pton(AF_INET6, str, &addr->v6.sin6_addr) > 0) { addr->v6.sin6_family = AF_INET6; addr->v6.sin6_port = htons(port); @@ -429,7 +424,6 @@ cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* a *numValidAddrs = 1; return 0; } - #endif return ParseHost(str, port, addrs, numValidAddrs); } @@ -442,8 +436,7 @@ cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { if (*s == -1) return errno; if (nonblocking) { - int blocking_raw = -1; /* non-blocking mode */ - ioctl(*s, FIONBIO, &blocking_raw); + fcntl(*s, F_SETFL, O_NONBLOCK); } res = connect(*s, raw, addr->size); diff --git a/src/Platform_Web.c b/src/Platform_Web.c index f1b43bec2..2fd862701 100644 --- a/src/Platform_Web.c +++ b/src/Platform_Web.c @@ -84,11 +84,10 @@ void Platform_Log(const char* msg, int len) { interop_Log(msg, len); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } extern void interop_GetLocalTime(struct DateTime* t); diff --git a/src/Platform_WiiU.c b/src/Platform_WiiU.c index ba3069e48..384956904 100644 --- a/src/Platform_WiiU.c +++ b/src/Platform_WiiU.c @@ -58,15 +58,12 @@ void Platform_Log(const char* msg, int len) { OSReport("%s\n", tmp); } -#define WIIU_EPOCH_ADJUST 946684800000ULL // Wii U time epoch is year 2000, not 1970 +#define WIIU_EPOCH_ADJUST 946684800ULL // Wii U time epoch is year 2000, not 1970 -TimeMS DateTime_CurrentUTC_MS(void) { - OSTime time = OSGetTime(); - // avoid overflow in time calculation +TimeMS DateTime_CurrentUTC(void) { + OSTime time = OSGetTime(); cc_int64 secs = (time_t)OSTicksToSeconds(time); - time -= OSSecondsToTicks(secs); - cc_uint64 msecs = OSTicksToMilliseconds(time); - return (secs * 1000 + msecs) + UNIX_EPOCH + WIIU_EPOCH_ADJUST; + return secs + UNIX_EPOCH_SECONDS + WIIU_EPOCH_ADJUST; } void DateTime_CurrentLocal(struct DateTime* t) { @@ -455,4 +452,4 @@ void Platform_Init(void) { *-------------------------------------------------------Encryption--------------------------------------------------------* *#########################################################################################################################*/ static cc_result GetMachineID(cc_uint32* key) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Platform_Windows.c b/src/Platform_Windows.c index 52d128dab..00aa6e3b6 100644 --- a/src/Platform_Windows.c +++ b/src/Platform_Windows.c @@ -96,18 +96,18 @@ void Platform_Log(const char* msg, int len) { OutputDebugStringA("\n"); } -#define FILETIME_EPOCH 50491123200000ULL -#define FILETIME_UNIX_EPOCH 11644473600LL -#define FileTime_TotalMS(time) ((time / 10000) + FILETIME_EPOCH) -#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) -TimeMS DateTime_CurrentUTC_MS(void) { +#define FILETIME_EPOCH 50491123200ULL +#define FILETIME_UNIX_EPOCH 11644473600ULL +#define FileTime_TotalSecs(time) ((time / 10000000) + FILETIME_EPOCH) +#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) +TimeMS DateTime_CurrentUTC(void) { FILETIME ft; cc_uint64 raw; GetSystemTimeAsFileTime(&ft); /* in 100 nanosecond units, since Jan 1 1601 */ raw = ft.dwLowDateTime | ((cc_uint64)ft.dwHighDateTime << 32); - return FileTime_TotalMS(raw); + return FileTime_TotalSecs(raw); } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_Xbox.c b/src/Platform_Xbox.c index f53fe1dd0..80f3f1422 100644 --- a/src/Platform_Xbox.c +++ b/src/Platform_Xbox.c @@ -39,16 +39,16 @@ void Platform_Log(const char* msg, int len) { OutputDebugStringA(tmp); } -#define FILETIME_EPOCH 50491123200000ULL -#define FILETIME_UNIX_EPOCH 11644473600LL -#define FileTime_TotalMS(time) ((time / 10000) + FILETIME_EPOCH) -#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) -TimeMS DateTime_CurrentUTC_MS(void) { +#define FILETIME_EPOCH 50491123200ULL +#define FILETIME_UNIX_EPOCH 11644473600ULL +#define FileTime_TotalSecs(time) ((time / 10000000) + FILETIME_EPOCH) +#define FileTime_UnixTime(time) ((time / 10000000) - FILETIME_UNIX_EPOCH) +TimeMS DateTime_CurrentUTC(void) { LARGE_INTEGER ft; KeQuerySystemTime(&ft); /* in 100 nanosecond units, since Jan 1 1601 */ - return FileTime_TotalMS(ft.QuadPart); + return FileTime_TotalSecs(ft.QuadPart); } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Platform_Xbox360.c b/src/Platform_Xbox360.c index c230bc370..d18920e81 100644 --- a/src/Platform_Xbox360.c +++ b/src/Platform_Xbox360.c @@ -41,11 +41,10 @@ void Platform_Log(const char* msg, int len) { puts(tmp); } -#define UnixTime_TotalMS(time) ((cc_uint64)time.tv_sec * 1000 + UNIX_EPOCH + (time.tv_usec / 1000)) -TimeMS DateTime_CurrentUTC_MS(void) { +TimeMS DateTime_CurrentUTC(void) { struct timeval cur; gettimeofday(&cur, NULL); - return UnixTime_TotalMS(cur); + return (cc_uint64)cur.tv_sec + UNIX_EPOCH_SECONDS; } void DateTime_CurrentLocal(struct DateTime* t) { diff --git a/src/Resources.c b/src/Resources.c index 8420cfb30..167fc7aba 100644 --- a/src/Resources.c +++ b/src/Resources.c @@ -14,6 +14,7 @@ #include "LWeb.h" #include "Http.h" #include "Game.h" +#include "Audio.h" /* Represents a set of assets/resources */ /* E.g. music set, sounds set, textures set */ @@ -28,9 +29,12 @@ struct AssetSet { const char* (*GetRequestName)(int reqID); /* Checks if any assets have been downloaded, and processes them if so */ void (*CheckStatus)(void); + /* Resets state and frees and allocated memory */ + void (*ResetState)(void); }; -int Resources_Count, Resources_Size; +int Resources_MissingCount, Resources_MissingSize; +cc_bool Resources_MissingRequired; union ResourceValue { cc_uint8* data; @@ -44,12 +48,336 @@ struct ResourceZipEntry { union ResourceValue value; cc_uint32 offset, crc32; }; +#define RESOURCE_TYPE_DATA 1 +#define RESOURCE_TYPE_PNG 2 +#define RESOURCE_TYPE_CONST 3 +#define RESOURCE_TYPE_SOUND 4 static CC_NOINLINE cc_bool Fetcher_Get(int reqID, struct HttpRequest* item); CC_NOINLINE static struct ResourceZipEntry* ZipEntries_Find(const cc_string* name); static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* data, struct ZipEntry* source); +/*########################################################################################################################* +*------------------------------------------------------Utility functions -------------------------------------------------* +*#########################################################################################################################*/ +static void ZipFile_InspectEntries(const cc_string* path, Zip_SelectEntry selector) { + struct Stream stream; + cc_result res; + + res = Stream_OpenFile(&stream, path); + if (res == ReturnCode_FileNotFound) return; + if (res) { Logger_SysWarn2(res, "opening", path); return; } + + res = Zip_Extract(&stream, selector, NULL); + if (res) Logger_SysWarn2(res, "inspecting", path); + + /* No point logging error for closing readonly file */ + (void)stream.Close(&stream); +} + +static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* data, struct ZipEntry* source) { + cc_uint32 size = source->UncompressedSize; + e->value.data = Mem_TryAlloc(size, 1); + e->size = size; + + if (!e->value.data) return ERR_OUT_OF_MEMORY; + return Stream_Read(data, e->value.data, size); +} + + +/*########################################################################################################################* +*-----------------------------------------------------Sound asset writing ------------------------------------------------* +*#########################################################################################################################*/ +#define WAV_FourCC(a, b, c, d) (((cc_uint32)a << 24) | ((cc_uint32)b << 16) | ((cc_uint32)c << 8) | (cc_uint32)d) +#define WAV_HDR_SIZE 44 + +/* Fixes up the .WAV header after having written all samples */ +static cc_result SoundPatcher_FixupHeader(struct Stream* s, struct VorbisState* ctx, cc_uint32 offset, cc_uint32 len) { + cc_uint8 header[WAV_HDR_SIZE]; + cc_result res = s->Seek(s, offset); + if (res) return res; + + Stream_SetU32_BE(header + 0, WAV_FourCC('R','I','F','F')); + Stream_SetU32_LE(header + 4, len - 8); + Stream_SetU32_BE(header + 8, WAV_FourCC('W','A','V','E')); + Stream_SetU32_BE(header + 12, WAV_FourCC('f','m','t',' ')); + Stream_SetU32_LE(header + 16, 16); /* fmt chunk size */ + Stream_SetU16_LE(header + 20, 1); /* PCM audio format */ + Stream_SetU16_LE(header + 22, ctx->channels); + Stream_SetU32_LE(header + 24, ctx->sampleRate); + + Stream_SetU32_LE(header + 28, ctx->sampleRate * ctx->channels * 2); /* byte rate */ + Stream_SetU16_LE(header + 32, ctx->channels * 2); /* block align */ + Stream_SetU16_LE(header + 34, 16); /* bits per sample */ + Stream_SetU32_BE(header + 36, WAV_FourCC('d','a','t','a')); + Stream_SetU32_LE(header + 40, len - WAV_HDR_SIZE); + + return Stream_Write(s, header, WAV_HDR_SIZE); +} + +/* Decodes all samples, then produces a .WAV file from them */ +static cc_result SoundPatcher_WriteWav(struct Stream* s, struct VorbisState* ctx) { + cc_int16* samples; + cc_uint32 begOffset; + cc_uint32 len = WAV_HDR_SIZE; + cc_result res; + int count; + + if ((res = s->Position(s, &begOffset))) return res; + + /* reuse context here for a temp garbage header */ + if ((res = Stream_Write(s, (const cc_uint8*)ctx, WAV_HDR_SIZE))) return res; + if ((res = Vorbis_DecodeHeaders(ctx))) return res; + + samples = (cc_int16*)Mem_TryAlloc(ctx->blockSizes[1] * ctx->channels, 2); + if (!samples) return ERR_OUT_OF_MEMORY; + + for (;;) { + res = Vorbis_DecodeFrame(ctx); + if (res == ERR_END_OF_STREAM) { + /* reached end of samples, so done */ + res = SoundPatcher_FixupHeader(s, ctx, begOffset, len); + break; + } + if (res) break; + + count = Vorbis_OutputFrame(ctx, samples); + len += count * 2; + +#ifdef CC_BUILD_BIGENDIAN + Utils_SwapEndian16(samples, count); +#endif + res = Stream_Write(s, (cc_uint8*)samples, count * 2); + if (res) break; + } + + Mem_Free(samples); + if (!res) res = s->Seek(s, begOffset + len); + return res; +} + +/* Converts an OGG sound to a WAV sound for faster decoding later */ +static cc_result SoundPatcher_Save(struct Stream* s, struct ResourceZipEntry* e) { + struct OggState* ogg = NULL; + struct VorbisState* ctx = NULL; + struct Stream src; + cc_result res; + + ogg = (struct OggState*)Mem_TryAlloc(1, sizeof(struct OggState)); + if (!ogg) { res = ERR_OUT_OF_MEMORY; goto cleanup; } + + ctx = (struct VorbisState*)Mem_TryAlloc(1, sizeof(struct VorbisState)); + if (!ctx) { res = ERR_OUT_OF_MEMORY; goto cleanup; } + + Stream_ReadonlyMemory(&src, e->value.data, e->size); + + Ogg_Init(ogg, &src); + Vorbis_Init(ctx); + ctx->source = ogg; + res = SoundPatcher_WriteWav(s, ctx); + +cleanup: + if (ctx) Vorbis_Free(ctx); + Mem_Free(ctx); + Mem_Free(ogg); + return res; +} + + +/*########################################################################################################################* +*------------------------------------------------------Zip entry writer---------------------------------------------------* +*#########################################################################################################################*/ +static void GetCurrentZipDate(int* modTime, int* modDate) { + struct DateTime now; + DateTime_CurrentLocal(&now); + + *modTime = (now.second / 2) | (now.minute << 5) | (now.hour << 11); + *modDate = (now.day) | (now.month << 5) | ((now.year - 1980) << 9); +} + +static cc_result ZipWriter_LocalFile(struct Stream* s, struct ResourceZipEntry* e) { + int filenameLen = String_Length(e->filename); + cc_uint8 header[30 + STRING_SIZE]; + cc_result res; + int modTime, modDate; + + GetCurrentZipDate(&modTime, &modDate); + if ((res = s->Position(s, &e->offset))) return res; + + Stream_SetU32_LE(header + 0, 0x04034b50); /* signature */ + Stream_SetU16_LE(header + 4, 20); /* version needed */ + Stream_SetU16_LE(header + 6, 0); /* bitflags */ + Stream_SetU16_LE(header + 8, 0); /* compression method */ + Stream_SetU16_LE(header + 10, modTime); /* last modified */ + Stream_SetU16_LE(header + 12, modDate); /* last modified */ + + Stream_SetU32_LE(header + 14, e->crc32); /* CRC32 */ + Stream_SetU32_LE(header + 18, e->size); /* Compressed size */ + Stream_SetU32_LE(header + 22, e->size); /* Uncompressed size */ + + Stream_SetU16_LE(header + 26, filenameLen); /* name length */ + Stream_SetU16_LE(header + 28, 0); /* extra field length */ + + Mem_Copy(header + 30, e->filename, filenameLen); + return Stream_Write(s, header, 30 + filenameLen); +} + +static cc_result ZipWriter_CentralDir(struct Stream* s, struct ResourceZipEntry* e) { + int filenameLen = String_Length(e->filename); + cc_uint8 header[46 + STRING_SIZE]; + int modTime, modDate; + GetCurrentZipDate(&modTime, &modDate); + + Stream_SetU32_LE(header + 0, 0x02014b50); /* signature */ + Stream_SetU16_LE(header + 4, 20); /* version */ + Stream_SetU16_LE(header + 6, 20); /* version needed */ + Stream_SetU16_LE(header + 8, 0); /* bitflags */ + Stream_SetU16_LE(header + 10, 0); /* compression method */ + Stream_SetU16_LE(header + 12, modTime); /* last modified */ + Stream_SetU16_LE(header + 14, modDate); /* last modified */ + + Stream_SetU32_LE(header + 16, e->crc32); /* CRC32 */ + Stream_SetU32_LE(header + 20, e->size); /* compressed size */ + Stream_SetU32_LE(header + 24, e->size); /* uncompressed size */ + + Stream_SetU16_LE(header + 28, filenameLen); /* name length */ + Stream_SetU16_LE(header + 30, 0); /* extra field length */ + Stream_SetU16_LE(header + 32, 0); /* file comment length */ + Stream_SetU16_LE(header + 34, 0); /* disk number */ + Stream_SetU16_LE(header + 36, 0); /* internal attributes */ + Stream_SetU32_LE(header + 38, 0); /* external attributes */ + Stream_SetU32_LE(header + 42, e->offset); /* local header offset */ + + Mem_Copy(header + 46, e->filename, filenameLen); + return Stream_Write(s, header, 46 + filenameLen); +} + +static cc_result ZipWriter_EndOfCentralDir(struct Stream* s, int numEntries, + cc_uint32 centralDirBeg, cc_uint32 centralDirEnd) { + cc_uint8 header[22]; + + Stream_SetU32_LE(header + 0, 0x06054b50); /* signature */ + Stream_SetU16_LE(header + 4, 0); /* disk number */ + Stream_SetU16_LE(header + 6, 0); /* disk number of start */ + Stream_SetU16_LE(header + 8, numEntries); /* disk entries */ + Stream_SetU16_LE(header + 10, numEntries); /* total entries */ + Stream_SetU32_LE(header + 12, centralDirEnd - centralDirBeg); /* central dir size */ + Stream_SetU32_LE(header + 16, centralDirBeg); /* central dir start */ + Stream_SetU16_LE(header + 20, 0); /* comment length */ + return Stream_Write(s, header, 22); +} + +static cc_result ZipWriter_FixupLocalFile(struct Stream* s, struct ResourceZipEntry* e) { + int filenameLen = String_Length(e->filename); + cc_uint8 tmp[2048]; + cc_uint32 dataBeg, dataEnd; + cc_uint32 i, crc, toRead, read; + cc_result res; + + dataBeg = e->offset + 30 + filenameLen; + if ((res = s->Position(s, &dataEnd))) return res; + e->size = dataEnd - dataBeg; + + /* work out the CRC 32 */ + crc = 0xffffffffUL; + if ((res = s->Seek(s, dataBeg))) return res; + + for (; dataBeg < dataEnd; dataBeg += read) { + toRead = dataEnd - dataBeg; + toRead = min(toRead, sizeof(tmp)); + + if ((res = s->Read(s, tmp, toRead, &read))) return res; + if (!read) return ERR_END_OF_STREAM; + + for (i = 0; i < read; i++) { + crc = Utils_Crc32Table[(crc ^ tmp[i]) & 0xFF] ^ (crc >> 8); + } + } + e->crc32 = crc ^ 0xffffffffUL; + + /* then fixup the header */ + if ((res = s->Seek(s, e->offset))) return res; + if ((res = ZipWriter_LocalFile(s, e))) return res; + return s->Seek(s, dataEnd); +} + +static cc_result ZipWriter_WriteData(struct Stream* dst, struct ResourceZipEntry* e) { + cc_uint8* data = e->value.data; + cc_result res; + e->crc32 = Utils_CRC32(data, e->size); + + if ((res = ZipWriter_LocalFile(dst, e))) return res; + return Stream_Write(dst, data, e->size); +} + +static cc_result ZipWriter_WritePng(struct Stream* dst, struct ResourceZipEntry* e) { + struct Bitmap* src = &e->value.bmp; + cc_result res; + + if ((res = ZipWriter_LocalFile(dst, e))) return res; + if ((res = Png_Encode(src, dst, NULL, true, NULL))) return res; + return ZipWriter_FixupLocalFile(dst, e); +} + +static cc_result ZipWriter_WriteWav(struct Stream* dst, struct ResourceZipEntry* e) { + cc_result res; + + if ((res = ZipWriter_LocalFile(dst, e))) return res; + if ((res = SoundPatcher_Save(dst, e))) return res; + return ZipWriter_FixupLocalFile(dst, e); +} + + +/*########################################################################################################################* +*------------------------------------------------------Zip file writer----------------------------------------------------* +*#########################################################################################################################*/ +static cc_result ZipFile_WriteEntries(struct Stream* s, struct ResourceZipEntry* entries, int numEntries) { + struct ResourceZipEntry* e; + cc_uint32 beg, end; + int i; + cc_result res; + + for (i = 0; i < numEntries; i++) + { + e = &entries[i]; + + if (e->type == RESOURCE_TYPE_PNG) { + if ((res = ZipWriter_WritePng(s, e))) return res; + } else if (e->type == RESOURCE_TYPE_SOUND) { + if ((res = ZipWriter_WriteWav(s, e))) return res; + } else { + if ((res = ZipWriter_WriteData(s, e))) return res; + } + } + + if ((res = s->Position(s, &beg))) return res; + for (i = 0; i < numEntries; i++) + { + if ((res = ZipWriter_CentralDir(s, &entries[i]))) return res; + } + + if ((res = s->Position(s, &end))) return res; + return ZipWriter_EndOfCentralDir(s, numEntries, beg, end); +} + +static void ZipFile_Create(const cc_string* path, struct ResourceZipEntry* entries, int numEntries) { + struct Stream s; + cc_result res; + + res = Stream_CreateFile(&s, path); + if (res) { + Logger_SysWarn2(res, "creating", path); return; + } + + res = ZipFile_WriteEntries(&s, entries, numEntries); + if (res) Logger_SysWarn2(res, "making", path); + + res = s.Close(&s); + if (res) Logger_SysWarn2(res, "closing", path); +} + + /*########################################################################################################################* *---------------------------------------------------------Music assets----------------------------------------------------* *#########################################################################################################################*/ @@ -89,8 +417,8 @@ static void MusicAssets_CountMissing(void) { { if (musicAssets[i].downloaded) continue; - Resources_Size += musicAssets[i].size; - Resources_Count++; + Resources_MissingSize += musicAssets[i].size; + Resources_MissingCount++; } } @@ -158,12 +486,16 @@ static void MusicAssets_CheckStatus(void) { } } +static void MusicAssets_ResetState(void) { +} + static const struct AssetSet mccMusicAssetSet = { MusicAssets_CheckExistence, MusicAssets_CountMissing, MusicAssets_DownloadAssets, MusicAssets_GetRequestName, - MusicAssets_CheckStatus + MusicAssets_CheckStatus, + MusicAssets_ResetState }; @@ -171,200 +503,161 @@ static const struct AssetSet mccMusicAssetSet = { *---------------------------------------------------------Sound assets----------------------------------------------------* *#########################################################################################################################*/ static struct SoundAsset { - const char* name; + const char* filename; const char* hash; - int reqID; + int reqID, size; + void* data; } soundAssets[] = { - { "dig_cloth1", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "dig_cloth2", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, - { "dig_cloth3", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "dig_cloth4", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, - { "dig_grass1", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "dig_grass2", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, - { "dig_grass3", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "dig_grass4", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, - { "dig_gravel1", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "dig_gravel2", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, - { "dig_gravel3", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "dig_gravel4", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, - { "dig_sand1", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "dig_sand2", "0fa4234797f336ada4e3735e013e44d1099afe57" }, - { "dig_sand3", "c75589cc0087069f387de127dd1499580498738e" }, { "dig_sand4", "37afa06f97d58767a1cd1382386db878be1532dd" }, - { "dig_snow1", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "dig_snow2", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, - { "dig_snow3", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "dig_snow4", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, - { "dig_stone1", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "dig_stone2", "9c92f697142ae320584bf64c0d54381d59703528" }, - { "dig_stone3", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "dig_stone4", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, - { "dig_wood1", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "dig_wood2", "98102533e6085617a2962157b4f3658f59aea018" }, - { "dig_wood3", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "dig_wood4", "dc66978374a46ab2b87db6472804185824868095" }, - { "dig_glass1", "7274a2231ed4544a37e599b7b014e589e5377094" }, { "dig_glass2", "87c47bda3645c68f18a49e83cbf06e5302d087ff" }, - { "dig_glass3", "ad7d770b7fff3b64121f75bd60cecfc4866d1cd6" }, - - { "step_cloth1", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "step_cloth2", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, - { "step_cloth3", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "step_cloth4", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, - { "step_grass1", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "step_grass2", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, - { "step_grass3", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "step_grass4", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, - { "step_gravel1", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "step_gravel2", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, - { "step_gravel3", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "step_gravel4", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, - { "step_sand1", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "step_sand2", "0fa4234797f336ada4e3735e013e44d1099afe57" }, - { "step_sand3", "c75589cc0087069f387de127dd1499580498738e" }, { "step_sand4", "37afa06f97d58767a1cd1382386db878be1532dd" }, - { "step_snow1", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "step_snow2", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, - { "step_snow3", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "step_snow4", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, - { "step_stone1", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "step_stone2", "9c92f697142ae320584bf64c0d54381d59703528" }, - { "step_stone3", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "step_stone4", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, - { "step_wood1", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "step_wood2", "98102533e6085617a2962157b4f3658f59aea018" }, - { "step_wood3", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "step_wood4", "dc66978374a46ab2b87db6472804185824868095" } + { "dig_cloth1.wav", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "dig_cloth2.wav", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, + { "dig_cloth3.wav", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "dig_cloth4.wav", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, + { "dig_grass1.wav", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "dig_grass2.wav", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, + { "dig_grass3.wav", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "dig_grass4.wav", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, + { "dig_gravel1.wav", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "dig_gravel2.wav", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, + { "dig_gravel3.wav", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "dig_gravel4.wav", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, + { "dig_sand1.wav", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "dig_sand2.wav", "0fa4234797f336ada4e3735e013e44d1099afe57" }, + { "dig_sand3.wav", "c75589cc0087069f387de127dd1499580498738e" }, { "dig_sand4.wav", "37afa06f97d58767a1cd1382386db878be1532dd" }, + { "dig_snow1.wav", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "dig_snow2.wav", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, + { "dig_snow3.wav", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "dig_snow4.wav", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, + { "dig_stone1.wav", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "dig_stone2.wav", "9c92f697142ae320584bf64c0d54381d59703528" }, + { "dig_stone3.wav", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "dig_stone4.wav", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, + { "dig_wood1.wav", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "dig_wood2.wav", "98102533e6085617a2962157b4f3658f59aea018" }, + { "dig_wood3.wav", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "dig_wood4.wav", "dc66978374a46ab2b87db6472804185824868095" }, + { "dig_glass1.wav", "7274a2231ed4544a37e599b7b014e589e5377094" }, { "dig_glass2.wav", "87c47bda3645c68f18a49e83cbf06e5302d087ff" }, + { "dig_glass3.wav", "ad7d770b7fff3b64121f75bd60cecfc4866d1cd6" }, + + { "step_cloth1.wav", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "step_cloth2.wav", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, + { "step_cloth3.wav", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "step_cloth4.wav", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, + { "step_grass1.wav", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "step_grass2.wav", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, + { "step_grass3.wav", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "step_grass4.wav", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, + { "step_gravel1.wav", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "step_gravel2.wav", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, + { "step_gravel3.wav", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "step_gravel4.wav", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, + { "step_sand1.wav", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "step_sand2.wav", "0fa4234797f336ada4e3735e013e44d1099afe57" }, + { "step_sand3.wav", "c75589cc0087069f387de127dd1499580498738e" }, { "step_sand4.wav", "37afa06f97d58767a1cd1382386db878be1532dd" }, + { "step_snow1.wav", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "step_snow2.wav", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, + { "step_snow3.wav", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "step_snow4.wav", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, + { "step_stone1.wav", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "step_stone2.wav", "9c92f697142ae320584bf64c0d54381d59703528" }, + { "step_stone3.wav", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "step_stone4.wav", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, + { "step_wood1.wav", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "step_wood2.wav", "98102533e6085617a2962157b4f3658f59aea018" }, + { "step_wood3.wav", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "step_wood4.wav", "dc66978374a46ab2b87db6472804185824868095" } }; static cc_bool allSoundsExist; -static void SoundAssets_CheckExistence(void) { - cc_string path; char pathBuffer[FILENAME_SIZE]; +static void SoundAssets_ResetState(void) { int i; - String_InitArray(path, pathBuffer); + allSoundsExist = false; - for (i = 0; i < Array_Elems(soundAssets); i++) + for (i = 0; i < Array_Elems(soundAssets); i++) { - path.length = 0; - String_Format1(&path, "audio/%c.wav", soundAssets[i].name); - - if (File_Exists(&path)) continue; - allSoundsExist = false; - return; + Mem_Free(soundAssets[i].data); + soundAssets[i].data = NULL; + soundAssets[i].size = 0; } - allSoundsExist = true; } -static void SoundAssets_CountMissing(void) { - if (allSoundsExist) return; - - Resources_Count += Array_Elems(soundAssets); - Resources_Size += 417; -} /*########################################################################################################################* -*-----------------------------------------------------Sound asset fetching -----------------------------------------------* +*-----------------------------------------------------Sound asset checking -----------------------------------------------* *#########################################################################################################################*/ -#define SoundAsset_Download(hash) MusicAsset_Download(hash) +static int soundEntriesFound; -static void SoundAssets_DownloadAssets(void) { +static struct SoundAsset* SoundAssest_Find(const cc_string* name) { + struct SoundAsset* a; int i; - for (i = 0; i < Array_Elems(soundAssets); i++) - { - if (allSoundsExist) continue; - soundAssets[i].reqID = SoundAsset_Download(soundAssets[i].hash); - } -} -static const char* SoundAssets_GetRequestName(int reqID) { - int i; for (i = 0; i < Array_Elems(soundAssets); i++) { - if (reqID == soundAssets[i].reqID) return soundAssets[i].name; + a = &soundAssets[i]; + if (String_CaselessEqualsConst(name, a->filename)) return a; } return NULL; } +static cc_bool SoundAssets_CheckEntry(const cc_string* path) { + cc_string name = *path; + Utils_UNSAFE_GetFilename(&name); -/*########################################################################################################################* -*----------------------------------------------------Sound asset processing ----------------------------------------------* -*#########################################################################################################################*/ -#define WAV_FourCC(a, b, c, d) (((cc_uint32)a << 24) | ((cc_uint32)b << 16) | ((cc_uint32)c << 8) | (cc_uint32)d) -#define WAV_HDR_SIZE 44 - -/* Fixes up the .WAV header after having written all samples */ -static void SoundPatcher_FixupHeader(struct Stream* s, struct VorbisState* ctx, cc_uint32 len) { - cc_uint8 header[WAV_HDR_SIZE]; - cc_result res = s->Seek(s, 0); - if (res) { Logger_SysWarn(res, "seeking to .wav start"); return; } - - Stream_SetU32_BE(header + 0, WAV_FourCC('R','I','F','F')); - Stream_SetU32_LE(header + 4, len - 8); - Stream_SetU32_BE(header + 8, WAV_FourCC('W','A','V','E')); - Stream_SetU32_BE(header + 12, WAV_FourCC('f','m','t',' ')); - Stream_SetU32_LE(header + 16, 16); /* fmt chunk size */ - Stream_SetU16_LE(header + 20, 1); /* PCM audio format */ - Stream_SetU16_LE(header + 22, ctx->channels); - Stream_SetU32_LE(header + 24, ctx->sampleRate); - - Stream_SetU32_LE(header + 28, ctx->sampleRate * ctx->channels * 2); /* byte rate */ - Stream_SetU16_LE(header + 32, ctx->channels * 2); /* block align */ - Stream_SetU16_LE(header + 34, 16); /* bits per sample */ - Stream_SetU32_BE(header + 36, WAV_FourCC('d','a','t','a')); - Stream_SetU32_LE(header + 40, len - WAV_HDR_SIZE); - - res = Stream_Write(s, header, WAV_HDR_SIZE); - if (res) Logger_SysWarn(res, "fixing .wav header"); + if (SoundAssest_Find(&name)) soundEntriesFound++; + return false; } -/* Decodes all samples, then produces a .WAV file from them */ -static void SoundPatcher_WriteWav(struct Stream* s, struct VorbisState* ctx) { - cc_int16* samples; - cc_uint32 len = WAV_HDR_SIZE; - cc_result res; - int count; - - /* ctx is all 0, so reuse here for empty header */ - res = Stream_Write(s, (const cc_uint8*)ctx, WAV_HDR_SIZE); - if (res) { Logger_SysWarn(res, "writing .wav header"); return; } - - res = Vorbis_DecodeHeaders(ctx); - if (res) { Logger_SysWarn(res, "decoding .ogg header"); return; } +static void SoundAssets_CheckExistence(void) { + soundEntriesFound = 0; + ZipFile_InspectEntries(&Sounds_ZipPathMC, SoundAssets_CheckEntry); - samples = (cc_int16*)Mem_TryAlloc(ctx->blockSizes[1] * ctx->channels, 2); - if (!samples) { Logger_SysWarn(ERR_OUT_OF_MEMORY, "allocating .ogg samples"); return; } + /* >= in case somehow have say "gui.png", "GUI.png" */ + allSoundsExist = soundEntriesFound >= Array_Elems(soundAssets); +} - for (;;) { - res = Vorbis_DecodeFrame(ctx); - if (res == ERR_END_OF_STREAM) { - /* reached end of samples, so done */ - SoundPatcher_FixupHeader(s, ctx, len); break; - } - if (res) { Logger_SysWarn(res, "decoding .ogg"); break; } +static void SoundAssets_CountMissing(void) { + if (allSoundsExist) return; - count = Vorbis_OutputFrame(ctx, samples); - len += count * 2; - /* TODO: Do we need to account for big endian */ - res = Stream_Write(s, samples, count * 2); - if (res) { Logger_SysWarn(res, "writing samples"); break; } - } - Mem_Free(samples); + Resources_MissingCount += Array_Elems(soundAssets); + Resources_MissingSize += 417; } -/* Converts an OGG sound to a WAV sound for faster decoding later */ -static void SoundPatcher_Save(const char* name, struct HttpRequest* req) { - cc_string path; char pathBuffer[STRING_SIZE]; - struct OggState ogg; - struct Stream src, dst; - struct VorbisState* ctx; - cc_result res; - ctx = (struct VorbisState*)Mem_TryAllocCleared(1, sizeof(struct VorbisState)); - if (!ctx) { Logger_SysWarn(ERR_OUT_OF_MEMORY, "allocating memory"); return; } +/*########################################################################################################################* +*----------------------------------------------------Sound asset generation ----------------------------------------------* +*#########################################################################################################################*/ +static void SoundAsset_CreateZip(void) { + struct ResourceZipEntry entries[Array_Elems(soundAssets)]; + int i; + + for (i = 0; i < Array_Elems(soundAssets); i++) + { + entries[i].filename = soundAssets[i].filename; + entries[i].type = RESOURCE_TYPE_SOUND; - Stream_ReadonlyMemory(&src, req->data, req->size); - String_InitArray(path, pathBuffer); - String_Format1(&path, "audio/%c.wav", name); + entries[i].value.data = soundAssets[i].data; + entries[i].size = soundAssets[i].size; + } - res = Stream_CreateFile(&dst, &path); - if (res) { Logger_SysWarn(res, "creating .wav file"); return; } + ZipFile_Create(&Sounds_ZipPathMC, entries, Array_Elems(soundAssets)); + SoundAssets_ResetState(); +} - Ogg_Init(&ogg, &src); - ctx->source = &ogg; - SoundPatcher_WriteWav(&dst, ctx); - res = dst.Close(&dst); - if (res) Logger_SysWarn(res, "closing .wav file"); +/*########################################################################################################################* +*-----------------------------------------------------Sound asset fetching -----------------------------------------------* +*#########################################################################################################################*/ +#define SoundAsset_Download(hash) MusicAsset_Download(hash) - Vorbis_Free(ctx); - Mem_Free(ctx); +static void SoundAssets_DownloadAssets(void) { + int i; + for (i = 0; i < Array_Elems(soundAssets); i++) + { + if (allSoundsExist) continue; + soundAssets[i].reqID = SoundAsset_Download(soundAssets[i].hash); + } } +static const char* SoundAssets_GetRequestName(int reqID) { + int i; + for (i = 0; i < Array_Elems(soundAssets); i++) + { + if (reqID == soundAssets[i].reqID) return soundAssets[i].filename; + } + return NULL; +} -static void SoundAsset_Check(const struct SoundAsset* sound) { +static void SoundAsset_Check(struct SoundAsset* sound, int i) { struct HttpRequest item; if (!Fetcher_Get(sound->reqID, &item)) return; - SoundPatcher_Save(sound->name, &item); + sound->data = item.data; + sound->size = item.size; + item.data = NULL; HttpRequest_Free(&item); + + if (i == Array_Elems(soundAssets) - 1) + SoundAsset_CreateZip(); } static void SoundAssets_CheckStatus(void) { int i; for (i = 0; i < Array_Elems(soundAssets); i++) { - SoundAsset_Check(&soundAssets[i]); + SoundAsset_Check(&soundAssets[i], i); } } @@ -373,7 +666,8 @@ static const struct AssetSet mccSoundAssetSet = { SoundAssets_CountMissing, SoundAssets_DownloadAssets, SoundAssets_GetRequestName, - SoundAssets_CheckStatus + SoundAssets_CheckStatus, + SoundAssets_ResetState }; @@ -391,8 +685,9 @@ static void CCTextures_CheckExistence(void) { static void CCTextures_CountMissing(void) { if (ccTexturesExist) return; - Resources_Count++; - Resources_Size += 83; + Resources_MissingCount++; + Resources_MissingSize += 83; + Resources_MissingRequired = true; } @@ -414,30 +709,28 @@ static const char* CCTextures_GetRequestName(int reqID) { /*########################################################################################################################* *-------------------------------------------------CC texture assets processing -------------------------------------------* *#########################################################################################################################*/ -#ifdef CC_BUILD_MOBILE /* Android needs the touch.png */ /* TODO: Unify both android and desktop platforms to both just extract from default.zip */ static cc_bool CCTextures_SelectEntry(const cc_string* path) { return String_CaselessEqualsConst(path, "touch.png"); } + static cc_result CCTextures_ProcessEntry(const cc_string* path, struct Stream* data, struct ZipEntry* source) { struct ResourceZipEntry* e = ZipEntries_Find(path); + if (!e) return 0; /* TODO exteact on PC too */ + return ZipEntry_ExtractData(e, data, source); } static cc_result CCTextures_ExtractZip(struct HttpRequest* req) { struct Stream src; - Stream_WriteAllTo(&ccTexPack, req->data, req->size); - Stream_ReadonlyMemory(&src, req->data, req->size); + cc_result res; + + Stream_ReadonlyMemory(&src, req->data, req->size); + if ((res = Zip_Extract(&src, CCTextures_SelectEntry, CCTextures_ProcessEntry))) return res; - return Zip_Extract(&src, - CCTextures_SelectEntry, CCTextures_ProcessEntry); -} -#else -static cc_result CCTextures_ExtractZip(struct HttpRequest* req) { return Stream_WriteAllTo(&ccTexPack, req->data, req->size); } -#endif static void CCTextures_CheckStatus(void) { struct HttpRequest item; @@ -453,158 +746,24 @@ static void CCTextures_CheckStatus(void) { HttpRequest_Free(&item); } +static void CCTextures_ResetState(void) { + ccTexturesExist = false; + ccTexturesDownloaded = false; +} + static const struct AssetSet ccTexsAssetSet = { CCTextures_CheckExistence, CCTextures_CountMissing, CCTextures_DownloadAssets, CCTextures_GetRequestName, - CCTextures_CheckStatus + CCTextures_CheckStatus, + CCTextures_ResetState }; -/*########################################################################################################################* -*---------------------------------------------------------Zip writer------------------------------------------------------* -*#########################################################################################################################*/ -static void GetCurrentZipDate(int* modTime, int* modDate) { - struct DateTime now; - DateTime_CurrentLocal(&now); - - *modTime = (now.second / 2) | (now.minute << 5) | (now.hour << 11); - *modDate = (now.day) | (now.month << 5) | ((now.year - 1980) << 9); -} - -static cc_result ZipWriter_LocalFile(struct Stream* s, struct ResourceZipEntry* e) { - int filenameLen = String_Length(e->filename); - cc_uint8 header[30 + STRING_SIZE]; - cc_result res; - int modTime, modDate; - - GetCurrentZipDate(&modTime, &modDate); - if ((res = s->Position(s, &e->offset))) return res; - - Stream_SetU32_LE(header + 0, 0x04034b50); /* signature */ - Stream_SetU16_LE(header + 4, 20); /* version needed */ - Stream_SetU16_LE(header + 6, 0); /* bitflags */ - Stream_SetU16_LE(header + 8, 0); /* compression method */ - Stream_SetU16_LE(header + 10, modTime); /* last modified */ - Stream_SetU16_LE(header + 12, modDate); /* last modified */ - - Stream_SetU32_LE(header + 14, e->crc32); /* CRC32 */ - Stream_SetU32_LE(header + 18, e->size); /* Compressed size */ - Stream_SetU32_LE(header + 22, e->size); /* Uncompressed size */ - - Stream_SetU16_LE(header + 26, filenameLen); /* name length */ - Stream_SetU16_LE(header + 28, 0); /* extra field length */ - - Mem_Copy(header + 30, e->filename, filenameLen); - return Stream_Write(s, header, 30 + filenameLen); -} - -static cc_result ZipWriter_CentralDir(struct Stream* s, struct ResourceZipEntry* e) { - int filenameLen = String_Length(e->filename); - cc_uint8 header[46 + STRING_SIZE]; - int modTime, modDate; - GetCurrentZipDate(&modTime, &modDate); - - Stream_SetU32_LE(header + 0, 0x02014b50); /* signature */ - Stream_SetU16_LE(header + 4, 20); /* version */ - Stream_SetU16_LE(header + 6, 20); /* version needed */ - Stream_SetU16_LE(header + 8, 0); /* bitflags */ - Stream_SetU16_LE(header + 10, 0); /* compression method */ - Stream_SetU16_LE(header + 12, modTime); /* last modified */ - Stream_SetU16_LE(header + 14, modDate); /* last modified */ - - Stream_SetU32_LE(header + 16, e->crc32); /* CRC32 */ - Stream_SetU32_LE(header + 20, e->size); /* compressed size */ - Stream_SetU32_LE(header + 24, e->size); /* uncompressed size */ - - Stream_SetU16_LE(header + 28, filenameLen); /* name length */ - Stream_SetU16_LE(header + 30, 0); /* extra field length */ - Stream_SetU16_LE(header + 32, 0); /* file comment length */ - Stream_SetU16_LE(header + 34, 0); /* disk number */ - Stream_SetU16_LE(header + 36, 0); /* internal attributes */ - Stream_SetU32_LE(header + 38, 0); /* external attributes */ - Stream_SetU32_LE(header + 42, e->offset); /* local header offset */ - - Mem_Copy(header + 46, e->filename, filenameLen); - return Stream_Write(s, header, 46 + filenameLen); -} - -static cc_result ZipWriter_EndOfCentralDir(struct Stream* s, int numEntries, - cc_uint32 centralDirBeg, cc_uint32 centralDirEnd) { - cc_uint8 header[22]; - - Stream_SetU32_LE(header + 0, 0x06054b50); /* signature */ - Stream_SetU16_LE(header + 4, 0); /* disk number */ - Stream_SetU16_LE(header + 6, 0); /* disk number of start */ - Stream_SetU16_LE(header + 8, numEntries); /* disk entries */ - Stream_SetU16_LE(header + 10, numEntries); /* total entries */ - Stream_SetU32_LE(header + 12, centralDirEnd - centralDirBeg); /* central dir size */ - Stream_SetU32_LE(header + 16, centralDirBeg); /* central dir start */ - Stream_SetU16_LE(header + 20, 0); /* comment length */ - return Stream_Write(s, header, 22); -} - -static cc_result ZipWriter_FixupLocalFile(struct Stream* s, struct ResourceZipEntry* e) { - int filenameLen = String_Length(e->filename); - cc_uint8 tmp[2048]; - cc_uint32 dataBeg, dataEnd; - cc_uint32 i, crc, toRead, read; - cc_result res; - - dataBeg = e->offset + 30 + filenameLen; - if ((res = s->Position(s, &dataEnd))) return res; - e->size = dataEnd - dataBeg; - - /* work out the CRC 32 */ - crc = 0xffffffffUL; - if ((res = s->Seek(s, dataBeg))) return res; - - for (; dataBeg < dataEnd; dataBeg += read) { - toRead = dataEnd - dataBeg; - toRead = min(toRead, sizeof(tmp)); - - if ((res = s->Read(s, tmp, toRead, &read))) return res; - if (!read) return ERR_END_OF_STREAM; - - for (i = 0; i < read; i++) { - crc = Utils_Crc32Table[(crc ^ tmp[i]) & 0xFF] ^ (crc >> 8); - } - } - e->crc32 = crc ^ 0xffffffffUL; - - /* then fixup the header */ - if ((res = s->Seek(s, e->offset))) return res; - if ((res = ZipWriter_LocalFile(s, e))) return res; - return s->Seek(s, dataEnd); -} - -static cc_result ZipWriter_WriteData(struct Stream* dst, struct ResourceZipEntry* e) { - cc_uint8* data = e->value.data; - cc_result res; - e->crc32 = Utils_CRC32(data, e->size); - - if ((res = ZipWriter_LocalFile(dst, e))) return res; - return Stream_Write(dst, data, e->size); -} - -static cc_result ZipWriter_WritePng(struct Stream* dst, struct ResourceZipEntry* e) { - struct Bitmap* src = &e->value.bmp; - cc_result res; - - if ((res = ZipWriter_LocalFile(dst, e))) return res; - if ((res = Png_Encode(src, dst, NULL, true))) return res; - return ZipWriter_FixupLocalFile(dst, e); -} - - /*########################################################################################################################* *----------------------------------------------------default.zip resources------------------------------------------------* *#########################################################################################################################*/ -#define RESOURCE_TYPE_DATA 1 -#define RESOURCE_TYPE_PNG 2 -#define RESOURCE_TYPE_CONST 3 - #define ANIMS_TXT \ "# This file defines the animations used in a texture pack for ClassiCube.\r\n" \ "# Each line is in the format : \r\n" \ @@ -656,15 +815,6 @@ CC_NOINLINE static struct ResourceZipEntry* ZipEntries_Find(const cc_string* nam return NULL; } -static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* data, struct ZipEntry* source) { - cc_uint32 size = source->UncompressedSize; - e->value.data = Mem_TryAlloc(size, 1); - e->size = size; - - if (!e->value.data) return ERR_OUT_OF_MEMORY; - return Stream_Read(data, e->value.data, size); -} - static cc_result ClassicPatcher_ExtractFiles(struct HttpRequest* req); static cc_result ModernPatcher_ExtractFiles(struct HttpRequest* req); @@ -689,6 +839,19 @@ static struct ZipfileSource { }; static int numDefaultZipSources, numDefaultZipProcessed; +static void MCCTextures_ResetState(void) { + int i; + for (i = 0; i < Array_Elems(defaultZipEntries); i++) + { + if (defaultZipEntries[i].type == RESOURCE_TYPE_CONST) continue; + + /* can reuse value.data for value.bmp case too */ + Mem_Free(defaultZipEntries[i].value.data); + defaultZipEntries[i].value.data = NULL; + defaultZipEntries[i].size = 0; + } +} + /*########################################################################################################################* *------------------------------------------------default.zip entry generators---------------------------------------------* @@ -883,52 +1046,6 @@ static cc_result Classic0023Patcher_OldGold(struct HttpRequest* req) { } -/*########################################################################################################################* -*------------------------------------------------------default.zip writer-------------------------------------------------* -*#########################################################################################################################*/ -static cc_result DefaultZip_WriteEntries(struct Stream* s) { - struct ResourceZipEntry* e; - cc_uint32 beg, end; - int i; - cc_result res; - - for (i = 0; i < Array_Elems(defaultZipEntries); i++) { - e = &defaultZipEntries[i]; - - if (e->type == RESOURCE_TYPE_PNG) { - if ((res = ZipWriter_WritePng(s, e))) return res; - } else { - if ((res = ZipWriter_WriteData(s, e))) return res; - } - } - - if ((res = s->Position(s, &beg))) return res; - for (i = 0; i < Array_Elems(defaultZipEntries); i++) { - if ((res = ZipWriter_CentralDir(s, &defaultZipEntries[i]))) return res; - } - - if ((res = s->Position(s, &end))) return res; - return ZipWriter_EndOfCentralDir(s, Array_Elems(defaultZipEntries), beg, end); -} - -static void DefaultZip_Create(void) { - cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); - struct Stream s; - cc_result res; - - res = Stream_CreateFile(&s, &path); - if (res) { - Logger_SysWarn2(res, "creating", &path); return; - } - - res = DefaultZip_WriteEntries(&s); - if (res) Logger_SysWarn2(res, "making", &path); - - res = s.Close(&s); - if (res) Logger_SysWarn2(res, "closing", &path); -} - - /*########################################################################################################################* *-----------------------------------------------Minecraft Classic texture assets------------------------------------------* *#########################################################################################################################*/ @@ -944,19 +1061,10 @@ static cc_bool DefaultZip_SelectEntry(const cc_string* path) { } static void MCCTextures_CheckExistence(void) { - cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); - struct Stream stream; - cc_result res; + cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); + zipEntriesFound = 0; - res = Stream_OpenFile(&stream, &path); - if (res == ReturnCode_FileNotFound) return; - if (res) { Logger_SysWarn2(res, "opening", &path); return; } - - res = Zip_Extract(&stream, DefaultZip_SelectEntry, NULL); - if (res) Logger_SysWarn2(res, "inspecting", &path); - - /* No point logging error for closing readonly file */ - (void)stream.Close(&stream); + ZipFile_InspectEntries(&path, DefaultZip_SelectEntry); /* >= in case somehow have say "gui.png", "GUI.png" */ allZipEntriesExist = zipEntriesFound >= Array_Elems(defaultZipEntries); @@ -973,8 +1081,8 @@ static void MCCTextures_CountMissing(void) { if (Game_Version.Version > VERSION_0023) numDefaultZipSources--; for (i = 0; i < numDefaultZipSources; i++) { - Resources_Count++; - Resources_Size += defaultZipSources[i].size; + Resources_MissingCount++; + Resources_MissingSize += defaultZipSources[i].size; } } @@ -992,6 +1100,7 @@ static void MCCTextures_DownloadAssets(void) { { url = String_FromReadonly(defaultZipSources[i].url); defaultZipSources[i].reqID = Http_AsyncGetData(&url, 0); + defaultZipSources[i].downloaded = false; } } @@ -1008,6 +1117,12 @@ static const char* MCCTextures_GetRequestName(int reqID) { /*########################################################################################################################* *------------------------------------------Minecraft Classic texture assets processing -----------------------------------* *#########################################################################################################################*/ +static void MCCTextures_CreateDefaultZip(void) { + cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); + ZipFile_Create(&path, defaultZipEntries, Array_Elems(defaultZipEntries)); + MCCTextures_ResetState(); +} + static void MCCTextures_CheckSource(struct ZipfileSource* source) { struct HttpRequest item; cc_result res; @@ -1023,7 +1138,7 @@ static void MCCTextures_CheckSource(struct ZipfileSource* source) { HttpRequest_Free(&item); if (++numDefaultZipProcessed < numDefaultZipSources) return; - DefaultZip_Create(); + MCCTextures_CreateDefaultZip(); } static void MCCTextures_CheckStatus(void) { @@ -1040,7 +1155,8 @@ static const struct AssetSet mccTexsAssetSet = { MCCTextures_CountMissing, MCCTextures_DownloadAssets, MCCTextures_GetRequestName, - MCCTextures_CheckStatus + MCCTextures_CheckStatus, + MCCTextures_ResetState }; @@ -1058,10 +1174,20 @@ static const struct AssetSet* const asset_sets[] = { &mccSoundAssetSet }; +static void ResetState() { + int i; + Resources_MissingCount = 0; + Resources_MissingSize = 0; + + for (i = 0; i < Array_Elems(asset_sets); i++) + { + asset_sets[i]->ResetState(); + } +} + void Resources_CheckExistence(void) { int i; - Resources_Count = 0; - Resources_Size = 0; + ResetState(); for (i = 0; i < Array_Elems(asset_sets); i++) { @@ -1100,18 +1226,9 @@ void Fetcher_Run(void) { } static void Fetcher_Finish(void) { - int i; Fetcher_Completed = true; Fetcher_Working = false; - - for (i = 0; i < Array_Elems(defaultZipEntries); i++) { - if (defaultZipEntries[i].type == RESOURCE_TYPE_CONST) continue; - - /* can reuse value.data for value.bmp case too */ - Mem_Free(defaultZipEntries[i].value.data); - defaultZipEntries[i].value.data = NULL; - defaultZipEntries[i].size = 0; - } + ResetState(); } static void Fetcher_Fail(struct HttpRequest* item) { @@ -1146,7 +1263,7 @@ void Fetcher_Update(void) { asset_sets[i]->CheckStatus(); } - if (Fetcher_Downloaded != Resources_Count) return; + if (Fetcher_Downloaded != Resources_MissingCount) return; Fetcher_Finish(); } #endif diff --git a/src/Resources.h b/src/Resources.h index 427da4221..4e629e55d 100644 --- a/src/Resources.h +++ b/src/Resources.h @@ -8,9 +8,11 @@ struct HttpRequest; typedef void (*FetcherErrorCallback)(struct HttpRequest* req); /* Number of resources that need to be downloaded */ -extern int Resources_Count; +extern int Resources_MissingCount; /* Total size of resources that need to be downloaded */ -extern int Resources_Size; +extern int Resources_MissingSize; +/* Whether required resources need to be downloaded */ +extern cc_bool Resources_MissingRequired; /* Checks existence of all assets */ void Resources_CheckExistence(void); diff --git a/src/SSL.c b/src/SSL.c index 1bf323e69..389117e96 100644 --- a/src/SSL.c +++ b/src/SSL.c @@ -476,7 +476,7 @@ static void InjectEntropy(SSLContext* ctx) { #endif static void SetCurrentTime(SSLContext* ctx) { - cc_uint64 cur = DateTime_CurrentUTC_MS() / 1000; + cc_uint64 cur = DateTime_CurrentUTC(); uint32_t days = (uint32_t)(cur / 86400) + 366; uint32_t secs = (uint32_t)(cur % 86400); diff --git a/src/SelectionBox.c b/src/SelectionBox.c index 4f06cc3de..9ee62dd16 100644 --- a/src/SelectionBox.c +++ b/src/SelectionBox.c @@ -6,6 +6,7 @@ #include "Game.h" #include "Camera.h" +#ifdef CC_BUILD_NETWORKING /* Data for a selection box. */ struct SelectionBox { Vec3 p0, p1; @@ -206,6 +207,11 @@ void Selections_Render(void) { Gfx_SetDepthWrite(true); Gfx_SetAlphaBlending(false); } +#else +static int selections_count; +void Selections_Render(void) { } +static void Selections_ContextLost(void* obj) { } +#endif /*########################################################################################################################* diff --git a/src/Server.c b/src/Server.c index 3d1b8dad3..3a60f5b52 100644 --- a/src/Server.c +++ b/src/Server.c @@ -67,7 +67,7 @@ int Ping_NextPingId(void) { head = (head + 1) % Array_Elems(ping_entries); ping_entries[head].id = next; - ping_entries[head].sent = DateTime_CurrentUTC_MS(); + ping_entries[head].sent = Stopwatch_Measure(); ping_entries[head].recv = 0; ping_head = head; @@ -79,25 +79,27 @@ void Ping_Update(int id) { for (i = 0; i < Array_Elems(ping_entries); i++) { if (ping_entries[i].id != id) continue; - ping_entries[i].recv = DateTime_CurrentUTC_MS(); + ping_entries[i].recv = Stopwatch_Measure(); return; } } int Ping_AveragePingMS(void) { - int i, measures = 0, totalMs = 0; + int i, measures = 0, totalMs; + cc_int64 total = 0; for (i = 0; i < Array_Elems(ping_entries); i++) { struct PingEntry entry = ping_entries[i]; if (!entry.sent || !entry.recv) continue; - totalMs += (int)(entry.recv - entry.sent); + total += entry.recv - entry.sent; measures++; } - if (!measures) return 0; - /* (recv - send) is time for packet to be sent to server and then sent back. */ - /* However for ping, only want time to send data to server, so half the total. */ + + totalMs = Stopwatch_ElapsedMS(0, total); + /* (recv - send) is average time for packet to be sent to server and then sent back. */ + /* However for ping, only want average time to send data to server, so half the total. */ totalMs /= 2; return totalMs / measures; } diff --git a/src/TexturePack.h b/src/TexturePack.h index e4858b128..781e38043 100644 --- a/src/TexturePack.h +++ b/src/TexturePack.h @@ -20,9 +20,9 @@ extern struct IGameComponent Textures_Component; #define ATLAS2D_SHIFT 4 /* Maximum supported number of rows in the atlas. */ #ifdef EXTENDED_TEXTURES -#define ATLAS2D_MAX_ROWS_COUNT 32 + #define ATLAS2D_MAX_ROWS_COUNT 32 #else -#define ATLAS2D_MAX_ROWS_COUNT 16 + #define ATLAS2D_MAX_ROWS_COUNT 16 #endif /* Maximum possible number of 1D terrain atlases. (worst case, each 1D atlas only has 1 tile) */ #define ATLAS1D_MAX_ATLASES (ATLAS2D_TILES_PER_ROW * ATLAS2D_MAX_ROWS_COUNT) diff --git a/src/Utils.c b/src/Utils.c index 9de401d21..6ec7a57b7 100644 --- a/src/Utils.c +++ b/src/Utils.c @@ -138,6 +138,19 @@ void Utils_Resize(void** buffer, int* capacity, cc_uint32 elemSize, int defCapac } } +void Utils_SwapEndian16(cc_int16* values, int numValues) { + cc_uint8* data = (cc_uint8*)values; + int i; + + for (i = 0; i < numValues * 2; i += 2) + { + cc_uint8 tmp = data[i + 0]; + data[i + 0] = data[i + 1]; + data[i + 1] = tmp; + } +} + + static const char base64_table[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', diff --git a/src/Utils.h b/src/Utils.h index 8cfa7f090..78995c0ef 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -9,7 +9,7 @@ Copyright 2014-2023 ClassiCube | Licensed under BSD-3 struct Bitmap; struct StringsBuffer; /* Represents a particular instance in time in some timezone. Not necessarily UTC time. */ -/* NOTE: TimeMS and DateTime_CurrentUTC_MS() should almost always be used instead. */ +/* NOTE: TimeMS and DateTime_CurrentUTC() should almost always be used instead. */ /* This struct should only be used when actually needed. (e.g. log message time) */ struct DateTime { int year; /* Year, ranges from 0 to 65535 */ @@ -45,6 +45,7 @@ cc_uint32 Utils_CRC32(const cc_uint8* data, cc_uint32 length); /* NOTE: This cannot be just indexed by byte value - see Utils_CRC32 implementation. */ extern const cc_uint32 Utils_Crc32Table[256]; CC_NOINLINE void Utils_Resize(void** buffer, int* capacity, cc_uint32 elemSize, int defCapacity, int expandElems); +void Utils_SwapEndian16(cc_int16* values, int numValues); /* Converts blocks of 3 bytes into 4 ASCII characters. (pads if needed) */ /* Returns the number of ASCII characters written. */ diff --git a/src/Vorbis.c b/src/Vorbis.c index 2c75d7f72..b0a2e74d5 100644 --- a/src/Vorbis.c +++ b/src/Vorbis.c @@ -1229,6 +1229,10 @@ static void Vorbis_CalcWindow(struct VorbisWindow* window, int blockSize) { } } +void Vorbis_Init(struct VorbisState* ctx) { + Mem_Set(ctx, 0, sizeof(*ctx) - sizeof(ctx->imdct)); +} + void Vorbis_Free(struct VorbisState* ctx) { int i; for (i = 0; i < ctx->numCodebooks; i++) diff --git a/src/Vorbis.h b/src/Vorbis.h index 56353aa90..b37437ad7 100644 --- a/src/Vorbis.h +++ b/src/Vorbis.h @@ -56,7 +56,9 @@ struct VorbisState { struct imdct_state imdct[2]; }; -/* Frees all dynamic memory allocated to decode the given vorbis audio. */ +/* Initialises the given context to defaults */ +void Vorbis_Init(struct VorbisState* ctx); +/* Frees all memory dynamically allocated by the given context */ void Vorbis_Free(struct VorbisState* ctx); /* Reads and decodes the initial vorbis headers and setup data. */ cc_result Vorbis_DecodeHeaders(struct VorbisState* ctx); diff --git a/src/Window_3DS.c b/src/Window_3DS.c index 81931ea0b..cb99c7de0 100644 --- a/src/Window_3DS.c +++ b/src/Window_3DS.c @@ -36,7 +36,6 @@ void Window_Init(void) { DisplayInfo.Width = top_width; DisplayInfo.Height = top_height; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 0.5f; DisplayInfo.ScaleY = 0.5f; @@ -90,52 +89,43 @@ void Window_RequestClose(void) { *----------------------------------------------------Input processing-----------------------------------------------------* *#########################################################################################################################*/ static void HandleButtons(u32 mods) { - Input_SetNonRepeatable(CCPAD_L, mods & KEY_L); - Input_SetNonRepeatable(CCPAD_R, mods & KEY_R); + Gamepad_SetButton(CCPAD_L, mods & KEY_L); + Gamepad_SetButton(CCPAD_R, mods & KEY_R); - Input_SetNonRepeatable(CCPAD_A, mods & KEY_A); - Input_SetNonRepeatable(CCPAD_B, mods & KEY_B); - Input_SetNonRepeatable(CCPAD_X, mods & KEY_X); - Input_SetNonRepeatable(CCPAD_Y, mods & KEY_Y); + Gamepad_SetButton(CCPAD_A, mods & KEY_A); + Gamepad_SetButton(CCPAD_B, mods & KEY_B); + Gamepad_SetButton(CCPAD_X, mods & KEY_X); + Gamepad_SetButton(CCPAD_Y, mods & KEY_Y); - Input_SetNonRepeatable(CCPAD_START, mods & KEY_START); - Input_SetNonRepeatable(CCPAD_SELECT, mods & KEY_SELECT); + Gamepad_SetButton(CCPAD_START, mods & KEY_START); + Gamepad_SetButton(CCPAD_SELECT, mods & KEY_SELECT); - Input_SetNonRepeatable(CCPAD_LEFT, mods & KEY_DLEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & KEY_DRIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & KEY_DUP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & KEY_DDOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & KEY_DLEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & KEY_DRIGHT); + Gamepad_SetButton(CCPAD_UP, mods & KEY_DUP); + Gamepad_SetButton(CCPAD_DOWN, mods & KEY_DDOWN); - Input_SetNonRepeatable(CCPAD_ZL, mods & KEY_ZL); - Input_SetNonRepeatable(CCPAD_ZR, mods & KEY_ZR); + Gamepad_SetButton(CCPAD_ZL, mods & KEY_ZL); + Gamepad_SetButton(CCPAD_ZR, mods & KEY_ZR); } +#define AXIS_SCALE 8.0f static void ProcessJoystickInput(circlePosition* pos, double delta) { - float scale = (delta * 60.0) / 8.0f; - // May not be exactly 0 on actual hardware if (Math_AbsI(pos->dx) <= 16) pos->dx = 0; if (Math_AbsI(pos->dy) <= 16) pos->dy = 0; - Event_RaiseRawMove(&ControllerEvents.RawMoved, pos->dx * scale, -pos->dy * scale); + Gamepad_SetAxis(PAD_AXIS_RIGHT, pos->dx / AXIS_SCALE, -pos->dy / AXIS_SCALE, delta); } static void ProcessTouchInput(int mods) { - static int currX, currY; // current touch position touchPosition touch; hidTouchRead(&touch); - if (hidKeysDown() & KEY_TOUCH) { // stylus went down - currX = touch.px; - currY = touch.py; - Input_AddTouch(0, currX, currY); - } - else if (mods & KEY_TOUCH) { // stylus is down - currX = touch.px; - currY = touch.py; - Input_UpdateTouch(0, currX, currY); - } - else if (hidKeysUp() & KEY_TOUCH) { // stylus was lifted - Input_RemoveTouch(0, currX, currY); + + if (mods & KEY_TOUCH) { + Input_AddTouch(0, touch.px, touch.py); + } else if (hidKeysUp() & KEY_TOUCH) { + Input_RemoveTouch(0, Pointers[0].x, Pointers[0].y); } } @@ -275,4 +265,4 @@ cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Window_Dreamcast.c b/src/Window_Dreamcast.c index 0a8670031..a793afe82 100644 --- a/src/Window_Dreamcast.c +++ b/src/Window_Dreamcast.c @@ -20,7 +20,6 @@ struct _WindowData WindowInfo; void Window_Init(void) { DisplayInfo.Width = vid_mode->width; DisplayInfo.Height = vid_mode->height; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -175,34 +174,34 @@ static void ProcessKeyboardInput(void) { static void HandleButtons(int mods) { // TODO CONT_Z - Input_SetNonRepeatable(CCPAD_A, mods & CONT_A); - Input_SetNonRepeatable(CCPAD_B, mods & CONT_B); - Input_SetNonRepeatable(CCPAD_X, mods & CONT_X); - Input_SetNonRepeatable(CCPAD_Y, mods & CONT_Y); + Gamepad_SetButton(CCPAD_A, mods & CONT_A); + Gamepad_SetButton(CCPAD_B, mods & CONT_B); + Gamepad_SetButton(CCPAD_X, mods & CONT_X); + Gamepad_SetButton(CCPAD_Y, mods & CONT_Y); - Input_SetNonRepeatable(CCPAD_START, mods & CONT_START); - Input_SetNonRepeatable(CCPAD_SELECT, mods & CONT_D); + Gamepad_SetButton(CCPAD_START, mods & CONT_START); + Gamepad_SetButton(CCPAD_SELECT, mods & CONT_D); - Input_SetNonRepeatable(CCPAD_LEFT, mods & CONT_DPAD_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & CONT_DPAD_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & CONT_DPAD_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & CONT_DPAD_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & CONT_DPAD_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & CONT_DPAD_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & CONT_DPAD_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & CONT_DPAD_DOWN); +} + +#define AXIS_SCALE 8.0f +static void HandleJoystick(int axis, int x, int y, double delta) { + if (Math_AbsI(x) <= 8) x = 0; + if (Math_AbsI(y) <= 8) y = 0; + + Gamepad_SetAxis(axis, x / AXIS_SCALE, y / AXIS_SCALE, delta); } static void HandleController(cont_state_t* state, double delta) { - Input_SetNonRepeatable(CCPAD_L, state->ltrig > 10); - Input_SetNonRepeatable(CCPAD_R, state->rtrig > 10); + Gamepad_SetButton(CCPAD_L, state->ltrig > 10); + Gamepad_SetButton(CCPAD_R, state->rtrig > 10); // TODO CONT_Z, joysticks - // TODO: verify values are right - - if (Input.RawMode) { - float scale = (delta * 60.0) / 8.0f; - int dx = state->joyx, dy = state->joyy; - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, dx * scale, dy * scale); - } + // TODO: verify values are right + HandleJoystick(PAD_AXIS_RIGHT, state->joyx, state->joyy, delta); } static void ProcessControllerInput(double delta) { @@ -307,4 +306,4 @@ cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Window_GCWii.c b/src/Window_GCWii.c index e05826e2c..7433d0896 100644 --- a/src/Window_GCWii.c +++ b/src/Window_GCWii.c @@ -105,48 +105,32 @@ void Window_RequestClose(void) { *#########################################################################################################################*/ static PADStatus gc_pad; -static void ProcessPAD_LeftJoystick(void) { - int dx = gc_pad.stickX; - int dy = gc_pad.stickY; - +#define PAD_AXIS_SCALE 8.0f +static void ProcessPAD_Joystick(int axis, int x, int y, double delta) { // May not be exactly 0 on actual hardware - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; + if (Math_AbsI(x) <= 8) x = 0; + if (Math_AbsI(y) <= 8) y = 0; - if (dx == 0 && dy == 0) return; - Input.JoystickMovement = true; - Input.JoystickAngle = Math_Atan2(dx, -dy); -} - -static void ProcessPAD_RightJoystick(double delta) { - float scale = (delta * 60.0) / 8.0f; - int dx = gc_pad.substickX; - int dy = gc_pad.substickY; - - // May not be exactly 0 on actual hardware - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, dx * scale, -dy * scale); + Gamepad_SetAxis(axis, x / PAD_AXIS_SCALE, -y / PAD_AXIS_SCALE, delta); } static void ProcessPAD_Buttons(void) { int mods = gc_pad.button; - Input_SetNonRepeatable(CCPAD_L, mods & PAD_TRIGGER_L); - Input_SetNonRepeatable(CCPAD_R, mods & PAD_TRIGGER_R); + Gamepad_SetButton(CCPAD_L, mods & PAD_TRIGGER_L); + Gamepad_SetButton(CCPAD_R, mods & PAD_TRIGGER_R); - Input_SetNonRepeatable(CCPAD_A, mods & PAD_BUTTON_A); - Input_SetNonRepeatable(CCPAD_B, mods & PAD_BUTTON_B); - Input_SetNonRepeatable(CCPAD_X, mods & PAD_BUTTON_X); - Input_SetNonRepeatable(CCPAD_Y, mods & PAD_BUTTON_Y); + Gamepad_SetButton(CCPAD_A, mods & PAD_BUTTON_A); + Gamepad_SetButton(CCPAD_B, mods & PAD_BUTTON_B); + Gamepad_SetButton(CCPAD_X, mods & PAD_BUTTON_X); + Gamepad_SetButton(CCPAD_Y, mods & PAD_BUTTON_Y); - Input_SetNonRepeatable(CCPAD_START, mods & PAD_BUTTON_START); - Input_SetNonRepeatable(CCPAD_SELECT, mods & PAD_TRIGGER_Z); + Gamepad_SetButton(CCPAD_START, mods & PAD_BUTTON_START); + Gamepad_SetButton(CCPAD_SELECT, mods & PAD_TRIGGER_Z); - Input_SetNonRepeatable(CCPAD_LEFT, mods & PAD_BUTTON_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & PAD_BUTTON_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & PAD_BUTTON_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & PAD_BUTTON_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & PAD_BUTTON_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & PAD_BUTTON_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & PAD_BUTTON_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & PAD_BUTTON_DOWN); } static void ProcessPADInput(double delta) { @@ -163,10 +147,8 @@ static void ProcessPADInput(double delta) { } ProcessPAD_Buttons(); - if (Input.RawMode) { - ProcessPAD_LeftJoystick(); - ProcessPAD_RightJoystick(delta); - } + ProcessPAD_Joystick(PAD_AXIS_LEFT, gc_pad.stickX, gc_pad.stickY, delta); + ProcessPAD_Joystick(PAD_AXIS_RIGHT, gc_pad.substickX, gc_pad.substickY, delta); } @@ -243,44 +225,44 @@ static int dragStartX, dragStartY; static cc_bool dragActive; static void ProcessWPAD_Buttons(int mods) { - Input_SetNonRepeatable(CCPAD_L, mods & WPAD_BUTTON_1); - Input_SetNonRepeatable(CCPAD_R, mods & WPAD_BUTTON_2); + Gamepad_SetButton(CCPAD_L, mods & WPAD_BUTTON_1); + Gamepad_SetButton(CCPAD_R, mods & WPAD_BUTTON_2); - Input_SetNonRepeatable(CCPAD_A, mods & WPAD_BUTTON_A); - Input_SetNonRepeatable(CCPAD_B, mods & WPAD_BUTTON_B); - Input_SetNonRepeatable(CCPAD_X, mods & WPAD_BUTTON_PLUS); + Gamepad_SetButton(CCPAD_A, mods & WPAD_BUTTON_A); + Gamepad_SetButton(CCPAD_B, mods & WPAD_BUTTON_B); + Gamepad_SetButton(CCPAD_X, mods & WPAD_BUTTON_PLUS); - Input_SetNonRepeatable(CCPAD_START, mods & WPAD_BUTTON_HOME); - Input_SetNonRepeatable(CCPAD_SELECT, mods & WPAD_BUTTON_MINUS); + Gamepad_SetButton(CCPAD_START, mods & WPAD_BUTTON_HOME); + Gamepad_SetButton(CCPAD_SELECT, mods & WPAD_BUTTON_MINUS); - Input_SetNonRepeatable(CCPAD_LEFT, mods & WPAD_BUTTON_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & WPAD_BUTTON_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & WPAD_BUTTON_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & WPAD_BUTTON_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & WPAD_BUTTON_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & WPAD_BUTTON_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & WPAD_BUTTON_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & WPAD_BUTTON_DOWN); } static void ProcessNunchuck_Game(int mods, double delta) { WPADData* wd = WPAD_Data(0); joystick_t analog = wd->exp.nunchuk.js; - Input_SetNonRepeatable(CCPAD_L, mods & WPAD_NUNCHUK_BUTTON_C); - Input_SetNonRepeatable(CCPAD_R, mods & WPAD_NUNCHUK_BUTTON_Z); + Gamepad_SetButton(CCPAD_L, mods & WPAD_NUNCHUK_BUTTON_C); + Gamepad_SetButton(CCPAD_R, mods & WPAD_NUNCHUK_BUTTON_Z); - Input_SetNonRepeatable(CCPAD_A, mods & WPAD_BUTTON_A); - Input_SetNonRepeatable(CCPAD_Y, mods & WPAD_BUTTON_1); - Input_SetNonRepeatable(CCPAD_X, mods & WPAD_BUTTON_2); + Gamepad_SetButton(CCPAD_A, mods & WPAD_BUTTON_A); + Gamepad_SetButton(CCPAD_Y, mods & WPAD_BUTTON_1); + Gamepad_SetButton(CCPAD_X, mods & WPAD_BUTTON_2); - Input_SetNonRepeatable(CCPAD_START, mods & WPAD_BUTTON_HOME); - Input_SetNonRepeatable(CCPAD_SELECT, mods & WPAD_BUTTON_MINUS); + Gamepad_SetButton(CCPAD_START, mods & WPAD_BUTTON_HOME); + Gamepad_SetButton(CCPAD_SELECT, mods & WPAD_BUTTON_MINUS); - Input_SetNonRepeatable(KeyBinds_Normal[KEYBIND_FLY], mods & WPAD_BUTTON_LEFT); + Gamepad_SetButton(KeyBinds_Normal[KEYBIND_FLY], mods & WPAD_BUTTON_LEFT); if (mods & WPAD_BUTTON_RIGHT) { Mouse_ScrollWheel(1.0*delta); } - Input_SetNonRepeatable(KeyBinds_Normal[KEYBIND_THIRD_PERSON], mods & WPAD_BUTTON_UP); - Input_SetNonRepeatable(KeyBinds_Normal[KEYBIND_FLY_DOWN], mods & WPAD_BUTTON_DOWN); + Gamepad_SetButton(KeyBinds_Normal[KEYBIND_THIRD_PERSON], mods & WPAD_BUTTON_UP); + Gamepad_SetButton(KeyBinds_Normal[KEYBIND_FLY_DOWN], mods & WPAD_BUTTON_DOWN); const float ANGLE_DELTA = 50; bool nunchuckUp = (analog.ang > -ANGLE_DELTA) && (analog.ang < ANGLE_DELTA) && (analog.mag > 0.5); @@ -288,57 +270,43 @@ static void ProcessNunchuck_Game(int mods, double delta) { bool nunchuckLeft = (analog.ang > -90-ANGLE_DELTA) && (analog.ang < -90+ANGLE_DELTA) && (analog.mag > 0.5); bool nunchuckRight = (analog.ang > 90-ANGLE_DELTA) && (analog.ang < 90+ANGLE_DELTA) && (analog.mag > 0.5); - Input_SetNonRepeatable(CCPAD_LEFT, nunchuckLeft); - Input_SetNonRepeatable(CCPAD_RIGHT, nunchuckRight); - Input_SetNonRepeatable(CCPAD_UP, nunchuckUp); - Input_SetNonRepeatable(CCPAD_DOWN, nunchuckDown); + Gamepad_SetButton(CCPAD_LEFT, nunchuckLeft); + Gamepad_SetButton(CCPAD_RIGHT, nunchuckRight); + Gamepad_SetButton(CCPAD_UP, nunchuckUp); + Gamepad_SetButton(CCPAD_DOWN, nunchuckDown); } - -static void ProcessClassic_LeftJoystick(struct joystick_t* js) { - // TODO: need to account for min/max?? - int dx = js->pos.x - js->center.x; - int dy = js->pos.y - js->center.y; +#define CLASSIC_AXIS_SCALE 2.0f +static void ProcessClassic_Joystick(int axis, struct joystick_t* js, double delta) { + // TODO: need to account for min/max?? see libogc + int x = js->pos.x - js->center.x; + int y = js->pos.y - js->center.y; - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; + if (Math_AbsI(x) <= 8) x = 0; + if (Math_AbsI(y) <= 8) y = 0; - if (dx == 0 && dy == 0) return; - Input.JoystickMovement = true; - Input.JoystickAngle = (js->ang - 90) * MATH_DEG2RAD; -} - -static void ProcessClassic_RightJoystick(struct joystick_t* js, double delta) { - float scale = (delta * 60.0) / 2.0f; - // TODO: need to account for min/max?? - int dx = js->pos.x - js->center.x; - int dy = js->pos.y - js->center.y; - - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, dx * scale, -dy * scale); + Gamepad_SetAxis(axis, x / CLASSIC_AXIS_SCALE, -y / CLASSIC_AXIS_SCALE, delta); } static void ProcessClassicButtons(int mods) { - Input_SetNonRepeatable(CCPAD_L, mods & CLASSIC_CTRL_BUTTON_FULL_L); - Input_SetNonRepeatable(CCPAD_R, mods & CLASSIC_CTRL_BUTTON_FULL_R); + Gamepad_SetButton(CCPAD_L, mods & CLASSIC_CTRL_BUTTON_FULL_L); + Gamepad_SetButton(CCPAD_R, mods & CLASSIC_CTRL_BUTTON_FULL_R); - Input_SetNonRepeatable(CCPAD_A, mods & CLASSIC_CTRL_BUTTON_A); - Input_SetNonRepeatable(CCPAD_B, mods & CLASSIC_CTRL_BUTTON_B); - Input_SetNonRepeatable(CCPAD_X, mods & CLASSIC_CTRL_BUTTON_X); - Input_SetNonRepeatable(CCPAD_Y, mods & CLASSIC_CTRL_BUTTON_Y); + Gamepad_SetButton(CCPAD_A, mods & CLASSIC_CTRL_BUTTON_A); + Gamepad_SetButton(CCPAD_B, mods & CLASSIC_CTRL_BUTTON_B); + Gamepad_SetButton(CCPAD_X, mods & CLASSIC_CTRL_BUTTON_X); + Gamepad_SetButton(CCPAD_Y, mods & CLASSIC_CTRL_BUTTON_Y); - Input_SetNonRepeatable(CCPAD_START, mods & CLASSIC_CTRL_BUTTON_PLUS); - Input_SetNonRepeatable(CCPAD_SELECT, mods & CLASSIC_CTRL_BUTTON_MINUS); + Gamepad_SetButton(CCPAD_START, mods & CLASSIC_CTRL_BUTTON_PLUS); + Gamepad_SetButton(CCPAD_SELECT, mods & CLASSIC_CTRL_BUTTON_MINUS); - Input_SetNonRepeatable(CCPAD_LEFT, mods & CLASSIC_CTRL_BUTTON_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & CLASSIC_CTRL_BUTTON_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & CLASSIC_CTRL_BUTTON_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & CLASSIC_CTRL_BUTTON_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & CLASSIC_CTRL_BUTTON_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & CLASSIC_CTRL_BUTTON_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & CLASSIC_CTRL_BUTTON_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & CLASSIC_CTRL_BUTTON_DOWN); - Input_SetNonRepeatable(CCPAD_ZL, mods & CLASSIC_CTRL_BUTTON_ZL); - Input_SetNonRepeatable(CCPAD_ZR, mods & CLASSIC_CTRL_BUTTON_ZR); + Gamepad_SetButton(CCPAD_ZL, mods & CLASSIC_CTRL_BUTTON_ZL); + Gamepad_SetButton(CCPAD_ZR, mods & CLASSIC_CTRL_BUTTON_ZR); } static void ProcessClassicInput(double delta) { @@ -347,10 +315,8 @@ static void ProcessClassicInput(double delta) { int mods = ctrls.btns | ctrls.btns_held; ProcessClassicButtons(mods); - if (Input.RawMode) { - ProcessClassic_LeftJoystick(&ctrls.ljs); - ProcessClassic_RightJoystick(&ctrls.rjs, delta); - } + ProcessClassic_Joystick(PAD_AXIS_LEFT, &ctrls.ljs, delta); + ProcessClassic_Joystick(PAD_AXIS_RIGHT, &ctrls.rjs, delta); } @@ -459,13 +425,13 @@ static u32 CvtRGB (u8 r1, u8 g1, u8 b1, u8 r2, u8 g2, u8 b2) { int y1, cb1, cr1, y2, cb2, cr2, cb, cr; - y1 = (299 * r1 + 587 * g1 + 114 * b1) / 1000; + y1 = (299 * r1 + 587 * g1 + 114 * b1) / 1000; cb1 = (-16874 * r1 - 33126 * g1 + 50000 * b1 + 12800000) / 100000; - cr1 = (50000 * r1 - 41869 * g1 - 8131 * b1 + 12800000) / 100000; + cr1 = (50000 * r1 - 41869 * g1 - 8131 * b1 + 12800000) / 100000; - y2 = (299 * r2 + 587 * g2 + 114 * b2) / 1000; + y2 = (299 * r2 + 587 * g2 + 114 * b2) / 1000; cb2 = (-16874 * r2 - 33126 * g2 + 50000 * b2 + 12800000) / 100000; - cr2 = (50000 * r2 - 41869 * g2 - 8131 * b2 + 12800000) / 100000; + cr2 = (50000 * r2 - 41869 * g2 - 8131 * b2 + 12800000) / 100000; cb = (cb1 + cb2) >> 1; cr = (cr1 + cr2) >> 1; @@ -538,4 +504,4 @@ cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Window_N64.c b/src/Window_N64.c index 95633005d..ff7aa100c 100644 --- a/src/Window_N64.c +++ b/src/Window_N64.c @@ -23,7 +23,6 @@ void Window_Init(void) { DisplayInfo.Width = display_get_width(); DisplayInfo.Height = display_get_height(); - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 0.5f; DisplayInfo.ScaleY = 0.5f; @@ -76,35 +75,35 @@ void Window_RequestClose(void) { *----------------------------------------------------Input processing-----------------------------------------------------* *#########################################################################################################################*/ static void HandleButtons(joypad_buttons_t btns) { - Input_SetNonRepeatable(CCPAD_L, btns.l); - Input_SetNonRepeatable(CCPAD_R, btns.r); + Gamepad_SetButton(CCPAD_L, btns.l); + Gamepad_SetButton(CCPAD_R, btns.r); - Input_SetNonRepeatable(CCPAD_A, btns.a); - Input_SetNonRepeatable(CCPAD_B, btns.b); - Input_SetNonRepeatable(CCPAD_Z, btns.z); + Gamepad_SetButton(CCPAD_A, btns.a); + Gamepad_SetButton(CCPAD_B, btns.b); + Gamepad_SetButton(CCPAD_Z, btns.z); - Input_SetNonRepeatable(CCPAD_START, btns.start); + Gamepad_SetButton(CCPAD_START, btns.start); - Input_SetNonRepeatable(CCPAD_LEFT, btns.d_left); - Input_SetNonRepeatable(CCPAD_RIGHT, btns.d_right); - Input_SetNonRepeatable(CCPAD_UP, btns.d_up); - Input_SetNonRepeatable(CCPAD_DOWN, btns.d_down); - - Input_SetNonRepeatable(CCPAD_CLEFT, btns.c_left); - Input_SetNonRepeatable(CCPAD_CRIGHT, btns.c_right); - Input_SetNonRepeatable(CCPAD_CUP, btns.c_up); - Input_SetNonRepeatable(CCPAD_CDOWN, btns.c_down); + Gamepad_SetButton(CCPAD_LEFT, btns.d_left); + Gamepad_SetButton(CCPAD_RIGHT, btns.d_right); + Gamepad_SetButton(CCPAD_UP, btns.d_up); + Gamepad_SetButton(CCPAD_DOWN, btns.d_down); + + Gamepad_SetButton(CCPAD_CLEFT, btns.c_left); + Gamepad_SetButton(CCPAD_CRIGHT, btns.c_right); + Gamepad_SetButton(CCPAD_CUP, btns.c_up); + Gamepad_SetButton(CCPAD_CDOWN, btns.c_down); } +#define AXIS_SCALE 8.0f static void ProcessAnalogInput(joypad_inputs_t* inputs, double delta) { - float scale = (delta * 60.0) / 8.0f; - int dx = inputs->stick_x; - int dy = inputs->stick_y; + int x = inputs->stick_x; + int y = inputs->stick_y; - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, dx * scale, -dy * scale); + if (Math_AbsI(x) <= 8) x = 0; + if (Math_AbsI(y) <= 8) y = 0; + + Gamepad_SetAxis(PAD_AXIS_RIGHT, x / AXIS_SCALE, -y / AXIS_SCALE, delta); } void Window_ProcessEvents(double delta) { @@ -112,8 +111,7 @@ void Window_ProcessEvents(double delta) { joypad_inputs_t inputs = joypad_get_inputs(JOYPAD_PORT_1); HandleButtons(inputs.btn); - - if (Input.RawMode) ProcessAnalogInput(&inputs, delta); + ProcessAnalogInput(&inputs, delta); } void Cursor_SetPosition(int x, int y) { } // Makes no sense for PSP diff --git a/src/Window_NDS.c b/src/Window_NDS.c index b36ee923b..b6e1caba7 100644 --- a/src/Window_NDS.c +++ b/src/Window_NDS.c @@ -43,7 +43,6 @@ static void InitConsoleWindow(void) { void Window_Init(void) { DisplayInfo.Width = SCREEN_WIDTH; DisplayInfo.Height = SCREEN_HEIGHT; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 0.5f; DisplayInfo.ScaleY = 0.5f; @@ -98,40 +97,32 @@ void Window_RequestClose(void) { *----------------------------------------------------Input processing-----------------------------------------------------* *#########################################################################################################################*/ static void HandleButtons(int mods) { - Input_SetNonRepeatable(CCPAD_L, mods & KEY_L); - Input_SetNonRepeatable(CCPAD_R, mods & KEY_R); + Gamepad_SetButton(CCPAD_L, mods & KEY_L); + Gamepad_SetButton(CCPAD_R, mods & KEY_R); - Input_SetNonRepeatable(CCPAD_A, mods & KEY_A); - Input_SetNonRepeatable(CCPAD_B, mods & KEY_B); - Input_SetNonRepeatable(CCPAD_X, mods & KEY_X); - Input_SetNonRepeatable(CCPAD_Y, mods & KEY_Y); + Gamepad_SetButton(CCPAD_A, mods & KEY_A); + Gamepad_SetButton(CCPAD_B, mods & KEY_B); + Gamepad_SetButton(CCPAD_X, mods & KEY_X); + Gamepad_SetButton(CCPAD_Y, mods & KEY_Y); - Input_SetNonRepeatable(CCPAD_START, mods & KEY_START); - Input_SetNonRepeatable(CCPAD_SELECT, mods & KEY_SELECT); + Gamepad_SetButton(CCPAD_START, mods & KEY_START); + Gamepad_SetButton(CCPAD_SELECT, mods & KEY_SELECT); - Input_SetNonRepeatable(CCPAD_LEFT, mods & KEY_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & KEY_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & KEY_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & KEY_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & KEY_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & KEY_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & KEY_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & KEY_DOWN); } -// Copied from Window_3DS.c static void ProcessTouchInput(int mods) { - static int curX, curY; // current touch position touchPosition touch; touchRead(&touch); Camera.Sensitivity = 100; // TODO not hardcode this - if (keysDown() & KEY_TOUCH) { // stylus went down - curX = touch.px; - curY = touch.py; - Input_AddTouch(0, curX, curY); - } else if (mods & KEY_TOUCH) { // stylus is down - curX = touch.px; - curY = touch.py; - Input_UpdateTouch(0, curX, curY); - } else if (keysUp() & KEY_TOUCH) { // stylus was lifted - Input_RemoveTouch(0, curX, curY); + if (mods & KEY_TOUCH) { + Input_AddTouch(0, touch.px, touch.py); + } else if (keysUp() & KEY_TOUCH) { + Input_RemoveTouch(0, Pointers[0].x, Pointers[0].y); } } diff --git a/src/Window_PS1.c b/src/Window_PS1.c index df1fa58d1..2aaccd497 100644 --- a/src/Window_PS1.c +++ b/src/Window_PS1.c @@ -32,7 +32,6 @@ struct _WindowData WindowInfo; void Window_Init(void) { DisplayInfo.Width = SCREEN_XRES; DisplayInfo.Height = SCREEN_YRES; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 0.5f; DisplayInfo.ScaleY = 0.5f; @@ -84,27 +83,40 @@ static void HandleButtons(int buttons) { // So just flip the bits to make more sense buttons = ~buttons; - Input_SetNonRepeatable(CCPAD_A, buttons & PAD_TRIANGLE); - Input_SetNonRepeatable(CCPAD_B, buttons & PAD_SQUARE); - Input_SetNonRepeatable(CCPAD_X, buttons & PAD_CROSS); - Input_SetNonRepeatable(CCPAD_Y, buttons & PAD_CIRCLE); + Gamepad_SetButton(CCPAD_A, buttons & PAD_TRIANGLE); + Gamepad_SetButton(CCPAD_B, buttons & PAD_SQUARE); + Gamepad_SetButton(CCPAD_X, buttons & PAD_CROSS); + Gamepad_SetButton(CCPAD_Y, buttons & PAD_CIRCLE); - Input_SetNonRepeatable(CCPAD_START, buttons & PAD_START); - Input_SetNonRepeatable(CCPAD_SELECT, buttons & PAD_SELECT); + Gamepad_SetButton(CCPAD_START, buttons & PAD_START); + Gamepad_SetButton(CCPAD_SELECT, buttons & PAD_SELECT); - Input_SetNonRepeatable(CCPAD_LEFT, buttons & PAD_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, buttons & PAD_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, buttons & PAD_UP); - Input_SetNonRepeatable(CCPAD_DOWN, buttons & PAD_DOWN); + Gamepad_SetButton(CCPAD_LEFT, buttons & PAD_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, buttons & PAD_RIGHT); + Gamepad_SetButton(CCPAD_UP, buttons & PAD_UP); + Gamepad_SetButton(CCPAD_DOWN, buttons & PAD_DOWN); - Input_SetNonRepeatable(CCPAD_L, buttons & PAD_L1); - Input_SetNonRepeatable(CCPAD_R, buttons & PAD_R1); - Input_SetNonRepeatable(CCPAD_ZL, buttons & PAD_L2); - Input_SetNonRepeatable(CCPAD_ZR, buttons & PAD_R2); + Gamepad_SetButton(CCPAD_L, buttons & PAD_L1); + Gamepad_SetButton(CCPAD_R, buttons & PAD_R1); + Gamepad_SetButton(CCPAD_ZL, buttons & PAD_L2); + Gamepad_SetButton(CCPAD_ZR, buttons & PAD_R2); +} + +#define AXIS_SCALE 16.0f +static void HandleJoystick(int axis, int x, int y, double delta) { + if (Math_AbsI(x) <= 8) x = 0; + if (Math_AbsI(y) <= 8) y = 0; + + Gamepad_SetAxis(axis, x / AXIS_SCALE, y / AXIS_SCALE, delta); } static void ProcessPadInput(PADTYPE* pad, double delta) { HandleButtons(pad->btn); + + if (pad->type == PAD_ID_ANALOG_STICK || pad->type == PAD_ID_ANALOG) { + HandleJoystick(PAD_AXIS_LEFT, pad->ls_x - 0x80, pad->ls_y - 0x80, delta); + HandleJoystick(PAD_AXIS_RIGHT, pad->rs_x - 0x80, pad->rs_y - 0x80, delta); + } } void Window_ProcessEvents(double delta) { diff --git a/src/Window_PS2.c b/src/Window_PS2.c index d15d93d8d..6a387882e 100644 --- a/src/Window_PS2.c +++ b/src/Window_PS2.c @@ -30,7 +30,6 @@ struct _WindowData WindowInfo; void Window_Init(void) { DisplayInfo.Width = 640; DisplayInfo.Height = graph_get_region() == GRAPH_MODE_PAL ? 512 : 448; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -90,48 +89,37 @@ static void HandleButtons(int buttons) { buttons = buttons ^ 0xFFFF; //Platform_Log1("BUTTONS: %h", &buttons); - Input_SetNonRepeatable(CCPAD_A, buttons & PAD_TRIANGLE); - Input_SetNonRepeatable(CCPAD_B, buttons & PAD_SQUARE); - Input_SetNonRepeatable(CCPAD_X, buttons & PAD_CROSS); - Input_SetNonRepeatable(CCPAD_Y, buttons & PAD_CIRCLE); + Gamepad_SetButton(CCPAD_A, buttons & PAD_TRIANGLE); + Gamepad_SetButton(CCPAD_B, buttons & PAD_SQUARE); + Gamepad_SetButton(CCPAD_X, buttons & PAD_CROSS); + Gamepad_SetButton(CCPAD_Y, buttons & PAD_CIRCLE); - Input_SetNonRepeatable(CCPAD_START, buttons & PAD_START); - Input_SetNonRepeatable(CCPAD_SELECT, buttons & PAD_SELECT); + Gamepad_SetButton(CCPAD_START, buttons & PAD_START); + Gamepad_SetButton(CCPAD_SELECT, buttons & PAD_SELECT); - Input_SetNonRepeatable(CCPAD_LEFT, buttons & PAD_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, buttons & PAD_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, buttons & PAD_UP); - Input_SetNonRepeatable(CCPAD_DOWN, buttons & PAD_DOWN); + Gamepad_SetButton(CCPAD_LEFT, buttons & PAD_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, buttons & PAD_RIGHT); + Gamepad_SetButton(CCPAD_UP, buttons & PAD_UP); + Gamepad_SetButton(CCPAD_DOWN, buttons & PAD_DOWN); - Input_SetNonRepeatable(CCPAD_L, buttons & PAD_L1); - Input_SetNonRepeatable(CCPAD_R, buttons & PAD_R1); - Input_SetNonRepeatable(CCPAD_ZL, buttons & PAD_L2); - Input_SetNonRepeatable(CCPAD_ZR, buttons & PAD_R2); + Gamepad_SetButton(CCPAD_L, buttons & PAD_L1); + Gamepad_SetButton(CCPAD_R, buttons & PAD_R1); + Gamepad_SetButton(CCPAD_ZL, buttons & PAD_L2); + Gamepad_SetButton(CCPAD_ZR, buttons & PAD_R2); } -static void HandleJoystick_Left(int x, int y) { - //Platform_Log2("LEFT: %i, %i", &x, &y); +#define AXIS_SCALE 16.0f +static void HandleJoystick(int axis, int x, int y, double delta) { if (Math_AbsI(x) <= 8) x = 0; if (Math_AbsI(y) <= 8) y = 0; - if (x == 0 && y == 0) return; - Input.JoystickMovement = true; - Input.JoystickAngle = Math_Atan2(x, -y); -} -static void HandleJoystick_Right(int x, int y, double delta) { - //Platform_Log2("Right: %i, %i", &x, &y); - float scale = (delta * 60.0) / 16.0f; - - if (Math_AbsI(x) <= 8) x = 0; - if (Math_AbsI(y) <= 8) y = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, x * scale, y * scale); + Gamepad_SetAxis(axis, x / AXIS_SCALE, y / AXIS_SCALE, delta); } static void ProcessPadInput(double delta, struct padButtonStatus* pad) { HandleButtons(pad->btns); - HandleJoystick_Left( pad->ljoy_h - 0x80, pad->ljoy_v - 0x80); - HandleJoystick_Right(pad->rjoy_h - 0x80, pad->rjoy_v - 0x80, delta); + HandleJoystick(PAD_AXIS_LEFT, pad->ljoy_h - 0x80, pad->ljoy_v - 0x80, delta); + HandleJoystick(PAD_AXIS_RIGHT, pad->rjoy_h - 0x80, pad->rjoy_v - 0x80, delta); } static cc_bool setMode; diff --git a/src/Window_PS3.c b/src/Window_PS3.c index d9e200f22..1b63a27de 100644 --- a/src/Window_PS3.c +++ b/src/Window_PS3.c @@ -46,7 +46,6 @@ void Window_Init(void) { DisplayInfo.Width = resolution.width; DisplayInfo.Height = resolution.height; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -243,50 +242,39 @@ static void ProcessKBTextInput(void) { *#########################################################################################################################*/ static void HandleButtons(padData* data) { //Platform_Log2("BUTTONS: %h (%h)", &data->button[2], &data->button[0]); - Input_SetNonRepeatable(CCPAD_A, data->BTN_TRIANGLE); - Input_SetNonRepeatable(CCPAD_B, data->BTN_SQUARE); - Input_SetNonRepeatable(CCPAD_X, data->BTN_CROSS); - Input_SetNonRepeatable(CCPAD_Y, data->BTN_CIRCLE); + Gamepad_SetButton(CCPAD_A, data->BTN_TRIANGLE); + Gamepad_SetButton(CCPAD_B, data->BTN_SQUARE); + Gamepad_SetButton(CCPAD_X, data->BTN_CROSS); + Gamepad_SetButton(CCPAD_Y, data->BTN_CIRCLE); - Input_SetNonRepeatable(CCPAD_START, data->BTN_START); - Input_SetNonRepeatable(CCPAD_SELECT, data->BTN_SELECT); + Gamepad_SetButton(CCPAD_START, data->BTN_START); + Gamepad_SetButton(CCPAD_SELECT, data->BTN_SELECT); - Input_SetNonRepeatable(CCPAD_LEFT, data->BTN_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, data->BTN_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, data->BTN_UP); - Input_SetNonRepeatable(CCPAD_DOWN, data->BTN_DOWN); + Gamepad_SetButton(CCPAD_LEFT, data->BTN_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, data->BTN_RIGHT); + Gamepad_SetButton(CCPAD_UP, data->BTN_UP); + Gamepad_SetButton(CCPAD_DOWN, data->BTN_DOWN); - Input_SetNonRepeatable(CCPAD_L, data->BTN_L1); - Input_SetNonRepeatable(CCPAD_R, data->BTN_R1); - Input_SetNonRepeatable(CCPAD_ZL, data->BTN_L2); - Input_SetNonRepeatable(CCPAD_ZR, data->BTN_R2); + Gamepad_SetButton(CCPAD_L, data->BTN_L1); + Gamepad_SetButton(CCPAD_R, data->BTN_R1); + Gamepad_SetButton(CCPAD_ZL, data->BTN_L2); + Gamepad_SetButton(CCPAD_ZR, data->BTN_R2); } -static void HandleJoystick_Left(int x, int y) { +#define AXIS_SCALE 32.0f +static void HandleJoystick(int axis, int x, int y, double delta) { if (Math_AbsI(x) <= 32) x = 0; if (Math_AbsI(y) <= 32) y = 0; - if (x == 0 && y == 0) return; - Input.JoystickMovement = true; - Input.JoystickAngle = Math_Atan2(x, -y); -} - -static void HandleJoystick_Right(int x, int y, double delta) { - float scale = (delta * 60.0) / 32.0f; - - if (Math_AbsI(x) <= 32) x = 0; - if (Math_AbsI(y) <= 32) y = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, x * scale, y * scale); + Gamepad_SetAxis(axis, x / AXIS_SCALE, y / AXIS_SCALE, delta); } static void ProcessPadInput(double delta, padData* pad) { HandleButtons(pad); - HandleJoystick_Left( pad->ANA_L_H - 0x80, pad->ANA_L_V - 0x80); - HandleJoystick_Right(pad->ANA_R_H - 0x80, pad->ANA_R_V - 0x80, delta); + HandleJoystick(PAD_AXIS_LEFT, pad->ANA_L_H - 0x80, pad->ANA_L_V - 0x80, delta); + HandleJoystick(PAD_AXIS_RIGHT, pad->ANA_R_H - 0x80, pad->ANA_R_V - 0x80, delta); } - void Window_ProcessEvents(double delta) { Input.JoystickMovement = false; @@ -369,4 +357,4 @@ cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Window_PSP.c b/src/Window_PSP.c index 0710b1293..352b4bc1d 100644 --- a/src/Window_PSP.c +++ b/src/Window_PSP.c @@ -26,7 +26,6 @@ struct _WindowData WindowInfo; void Window_Init(void) { DisplayInfo.Width = SCREEN_WIDTH; DisplayInfo.Height = SCREEN_HEIGHT; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -68,32 +67,32 @@ void Window_RequestClose(void) { *----------------------------------------------------Input processing-----------------------------------------------------* *#########################################################################################################################*/ static void HandleButtons(int mods) { - Input_SetNonRepeatable(CCPAD_L, mods & PSP_CTRL_LTRIGGER); - Input_SetNonRepeatable(CCPAD_R, mods & PSP_CTRL_RTRIGGER); + Gamepad_SetButton(CCPAD_L, mods & PSP_CTRL_LTRIGGER); + Gamepad_SetButton(CCPAD_R, mods & PSP_CTRL_RTRIGGER); - Input_SetNonRepeatable(CCPAD_A, mods & PSP_CTRL_TRIANGLE); - Input_SetNonRepeatable(CCPAD_B, mods & PSP_CTRL_SQUARE); - Input_SetNonRepeatable(CCPAD_X, mods & PSP_CTRL_CROSS); - Input_SetNonRepeatable(CCPAD_Y, mods & PSP_CTRL_CIRCLE); + Gamepad_SetButton(CCPAD_A, mods & PSP_CTRL_TRIANGLE); + Gamepad_SetButton(CCPAD_B, mods & PSP_CTRL_SQUARE); + Gamepad_SetButton(CCPAD_X, mods & PSP_CTRL_CROSS); + Gamepad_SetButton(CCPAD_Y, mods & PSP_CTRL_CIRCLE); - Input_SetNonRepeatable(CCPAD_START, mods & PSP_CTRL_START); - Input_SetNonRepeatable(CCPAD_SELECT, mods & PSP_CTRL_SELECT); + Gamepad_SetButton(CCPAD_START, mods & PSP_CTRL_START); + Gamepad_SetButton(CCPAD_SELECT, mods & PSP_CTRL_SELECT); - Input_SetNonRepeatable(CCPAD_LEFT, mods & PSP_CTRL_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & PSP_CTRL_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & PSP_CTRL_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & PSP_CTRL_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & PSP_CTRL_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & PSP_CTRL_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & PSP_CTRL_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & PSP_CTRL_DOWN); } +#define AXIS_SCALE 16.0f static void ProcessCircleInput(SceCtrlData* pad, double delta) { - float scale = (delta * 60.0) / 16.0f; - int dx = pad->Lx - 127; - int dy = pad->Ly - 127; + int x = pad->Lx - 127; + int y = pad->Ly - 127; - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; + if (Math_AbsI(x) <= 8) x = 0; + if (Math_AbsI(y) <= 8) y = 0; - Event_RaiseRawMove(&ControllerEvents.RawMoved, dx * scale, dy * scale); + Gamepad_SetAxis(PAD_AXIS_RIGHT, x / AXIS_SCALE, y / AXIS_SCALE, delta); } void Window_ProcessEvents(double delta) { @@ -104,8 +103,7 @@ void Window_ProcessEvents(double delta) { // TODO: need to use cached version still? like GameCube/Wii HandleButtons(pad.Buttons); - if (Input.RawMode) - ProcessCircleInput(&pad, delta); + ProcessCircleInput(&pad, delta); } void Cursor_SetPosition(int x, int y) { } // Makes no sense for PSP diff --git a/src/Window_PSVita.c b/src/Window_PSVita.c index 2f0638921..db53ec586 100644 --- a/src/Window_PSVita.c +++ b/src/Window_PSVita.c @@ -33,7 +33,6 @@ static void DQ_OnNextFrame2D(void* fb); void Window_Init(void) { DisplayInfo.Width = DISPLAY_WIDTH; DisplayInfo.Height = DISPLAY_HEIGHT; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -42,7 +41,9 @@ void Window_Init(void) { Window_Main.Focused = true; Window_Main.Exists = true; + Input_SetTouchMode(true); Input.Sources = INPUT_SOURCE_GAMEPAD; + sceCtrlSetSamplingMode(SCE_CTRL_MODE_ANALOG); sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); sceTouchSetSamplingState(SCE_TOUCH_PORT_BACK, SCE_TOUCH_SAMPLING_STATE_START); @@ -85,59 +86,42 @@ void Window_RequestClose(void) { *----------------------------------------------------Input processing-----------------------------------------------------* *#########################################################################################################################*/ static void HandleButtons(int mods) { - Input_SetNonRepeatable(CCPAD_A, mods & SCE_CTRL_TRIANGLE); - Input_SetNonRepeatable(CCPAD_B, mods & SCE_CTRL_SQUARE); - Input_SetNonRepeatable(CCPAD_X, mods & SCE_CTRL_CROSS); - Input_SetNonRepeatable(CCPAD_Y, mods & SCE_CTRL_CIRCLE); + Gamepad_SetButton(CCPAD_A, mods & SCE_CTRL_TRIANGLE); + Gamepad_SetButton(CCPAD_B, mods & SCE_CTRL_SQUARE); + Gamepad_SetButton(CCPAD_X, mods & SCE_CTRL_CROSS); + Gamepad_SetButton(CCPAD_Y, mods & SCE_CTRL_CIRCLE); - Input_SetNonRepeatable(CCPAD_START, mods & SCE_CTRL_START); - Input_SetNonRepeatable(CCPAD_SELECT, mods & SCE_CTRL_SELECT); - - Input_SetNonRepeatable(CCPAD_LEFT, mods & SCE_CTRL_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & SCE_CTRL_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & SCE_CTRL_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & SCE_CTRL_DOWN); - - Input_SetNonRepeatable(CCPAD_L, mods & SCE_CTRL_LTRIGGER); - Input_SetNonRepeatable(CCPAD_R, mods & SCE_CTRL_RTRIGGER); -} + Gamepad_SetButton(CCPAD_START, mods & SCE_CTRL_START); + Gamepad_SetButton(CCPAD_SELECT, mods & SCE_CTRL_SELECT); -static void ProcessLCircleInput(SceCtrlData* pad) { - int dx = pad->lx - 127; - int dy = pad->ly - 127; + Gamepad_SetButton(CCPAD_LEFT, mods & SCE_CTRL_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & SCE_CTRL_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & SCE_CTRL_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & SCE_CTRL_DOWN); - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; - - if (dx == 0 && dy == 0) return; - Input.JoystickMovement = true; - Input.JoystickAngle = Math_Atan2(dx, dy); + Gamepad_SetButton(CCPAD_L, mods & SCE_CTRL_LTRIGGER); + Gamepad_SetButton(CCPAD_R, mods & SCE_CTRL_RTRIGGER); } -static void ProcessRCircleInput(SceCtrlData* pad, double delta) { - float scale = (delta * 60.0) / 16.0f; - int dx = pad->rx - 127; - int dy = pad->ry - 127; - - if (Math_AbsI(dx) <= 8) dx = 0; - if (Math_AbsI(dy) <= 8) dy = 0; +#define AXIS_SCALE 16.0f +static void ProcessCircleInput(int axis, int x, int y, double delta) { + // May not be exactly 0 on actual hardware + if (Math_AbsI(x) <= 8) x = 0; + if (Math_AbsI(y) <= 8) y = 0; - Event_RaiseRawMove(&ControllerEvents.RawMoved, dx * scale, dy * scale); + Gamepad_SetAxis(axis, x / AXIS_SCALE, y / AXIS_SCALE, delta); } -static void ProcessTouchPress(int x, int y) { - if (!frontPanel.maxDispX || !frontPanel.maxDispY) { - // TODO: Shouldn't ever happen? need to check - Pointer_SetPosition(0, x, y); - return; - } +static void AdjustTouchPress(int* x, int* y) { + if (!frontPanel.maxDispX || !frontPanel.maxDispY) return; + // TODO: Shouldn't ever happen? need to check // rescale from touch range to screen range - x = (x - frontPanel.minDispX) * DISPLAY_WIDTH / frontPanel.maxDispX; - y = (y - frontPanel.minDispY) * DISPLAY_HEIGHT / frontPanel.maxDispY; - Pointer_SetPosition(0, x, y); + *x = (*x - frontPanel.minDispX) * DISPLAY_WIDTH / frontPanel.maxDispX; + *y = (*y - frontPanel.minDispY) * DISPLAY_HEIGHT / frontPanel.maxDispY; } +static cc_bool touch_pressed; static void ProcessTouchInput(void) { SceTouchData touch; @@ -146,12 +130,19 @@ static void ProcessTouchInput(void) { if (res == 0) return; // no data available yet if (res < 0) return; // error occurred - if (touch.reportNum > 0) { + cc_bool isPressed = touch.reportNum > 0; + if (isPressed) { int x = touch.report[0].x; int y = touch.report[0].y; - ProcessTouchPress(x, y); + AdjustTouchPress(&x, &y); + + Input_AddTouch(0, x, y); + touch_pressed = true; + } else if (touch_pressed) { + // touch.report[0].xy will be 0 when touch.reportNum is 0 + Input_RemoveTouch(0, Pointers[0].x, Pointers[0].y); + touch_pressed = false; } - Input_SetNonRepeatable(CCMOUSE_L, touch.reportNum > 0); } static void ProcessPadInput(double delta) { @@ -164,10 +155,8 @@ static void ProcessPadInput(double delta) { // TODO: need to use cached version still? like GameCube/Wii HandleButtons(pad.buttons); - if (Input.RawMode) { - ProcessLCircleInput(&pad); - ProcessRCircleInput(&pad, delta); - } + ProcessCircleInput(PAD_AXIS_LEFT, pad.lx - 127, pad.ly - 127, delta); + ProcessCircleInput(PAD_AXIS_RIGHT, pad.rx - 127, pad.ry - 127, delta); } void Window_ProcessEvents(double delta) { diff --git a/src/Window_SDL.c b/src/Window_SDL.c index a6fcff2d6..8360878d5 100644 --- a/src/Window_SDL.c +++ b/src/Window_SDL.c @@ -1,5 +1,5 @@ #include "Core.h" -#if defined CC_BUILD_SDL +#if defined CC_BUILD_SDL2 #include "_WindowBase.h" #include "Graphics.h" #include "String.h" @@ -11,8 +11,33 @@ static SDL_Window* win_handle; #ifndef CC_BUILD_OS2 #error "Some features are missing from the SDL backend. If possible, it is recommended that you use a native windowing backend instead" +#else +#define INCL_PM +#include +// Internal OS/2 driver data +typedef struct _WINDATA { + SDL_Window *window; + void *pOutput; /* Video output routines */ + HWND hwndFrame; + HWND hwnd; + PFNWP fnUserWndProc; + PFNWP fnWndFrameProc; + + void *pVOData; /* Video output data */ + + HRGN hrgnShape; + HPOINTER hptrIcon; + RECTL rectlBeforeFS; + + LONG lSkipWMSize; + LONG lSkipWMMove; + LONG lSkipWMMouseMove; + LONG lSkipWMVRNEnabled; + LONG lSkipWMAdjustFramePos; +} WINDATA; #endif + static void RefreshWindowBounds(void) { SDL_GetWindowSize(win_handle, &Window_Main.Width, &Window_Main.Height); } @@ -43,10 +68,7 @@ void Window_Init(void) { void Window_Free(void) { } static void DoCreateWindow(int width, int height, int flags) { - int x = Display_CentreX(width); - int y = Display_CentreY(height); - - win_handle = SDL_CreateWindow(NULL, x, y, width, height, + win_handle = SDL_CreateWindow(NULL, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, flags | SDL_WINDOW_RESIZABLE); if (!win_handle) Window_SDLFail("creating window"); @@ -289,11 +311,53 @@ static void ShowDialogCore(const char* title, const char* msg) { } cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { +#if defined CC_BUILD_OS2 + FILEDLG fileDialog; + HWND hDialog; + + memset(&fileDialog, 0, sizeof(FILEDLG)); + fileDialog.cbSize = sizeof(FILEDLG); + fileDialog.fl = FDS_HELPBUTTON | FDS_CENTER | FDS_PRELOAD_VOLINFO | FDS_OPEN_DIALOG; + fileDialog.pszTitle = args->description; + fileDialog.pszOKButton = NULL; + fileDialog.pfnDlgProc = WinDefFileDlgProc; + + Mem_Copy(fileDialog.szFullFile, *args->filters, CCHMAXPATH); + hDialog = WinFileDlg(HWND_DESKTOP, 0, &fileDialog); + if (fileDialog.lReturn == DID_OK) { + cc_string temp = String_FromRaw(fileDialog.szFullFile, CCHMAXPATH); + args->Callback(&temp); + } + + return 0; +#else return ERR_NOT_SUPPORTED; +#endif } cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) { +#if defined CC_BUILD_OS2 + FILEDLG fileDialog; + HWND hDialog; + + memset(&fileDialog, 0, sizeof(FILEDLG)); + fileDialog.cbSize = sizeof(FILEDLG); + fileDialog.fl = FDS_HELPBUTTON | FDS_CENTER | FDS_PRELOAD_VOLINFO | FDS_SAVEAS_DIALOG; + fileDialog.pszTitle = args->titles; + fileDialog.pszOKButton = NULL; + fileDialog.pfnDlgProc = WinDefFileDlgProc; + + Mem_Copy(fileDialog.szFullFile, *args->filters, CCHMAXPATH); + hDialog = WinFileDlg(HWND_DESKTOP, 0, &fileDialog); + if (fileDialog.lReturn == DID_OK) { + cc_string temp = String_FromRaw(fileDialog.szFullFile, CCHMAXPATH); + args->Callback(&temp); + } + + return 0; +#else return ERR_NOT_SUPPORTED; +#endif } static SDL_Surface* win_surface; @@ -376,6 +440,9 @@ void GLContext_Create(void) { SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, GLCONTEXT_DEFAULT_DEPTH); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, true); +#ifdef CC_BUILD_GLES + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#endif win_ctx = SDL_GL_CreateContext(win_handle); if (!win_ctx) Window_SDLFail("creating OpenGL context"); diff --git a/src/Window_SDL3.c b/src/Window_SDL3.c new file mode 100644 index 000000000..d4731b1fe --- /dev/null +++ b/src/Window_SDL3.c @@ -0,0 +1,502 @@ +#include "Core.h" +#if defined CC_BUILD_SDL3 +#include "_WindowBase.h" +#include "Graphics.h" +#include "String.h" +#include "Funcs.h" +#include "Bitmap.h" +#include "Errors.h" +#include +static SDL_Window* win_handle; +static Uint32 dlg_event; + +static void RefreshWindowBounds(void) { + SDL_GetWindowSize(win_handle, &Window_Main.Width, &Window_Main.Height); +} + +static void Window_SDLFail(const char* place) { + char strBuffer[256]; + cc_string str; + String_InitArray_NT(str, strBuffer); + + String_Format2(&str, "Error when %c: %c", place, SDL_GetError()); + str.buffer[str.length] = '\0'; + Logger_Abort(str.buffer); +} + +void Window_Init(void) { + SDL_Init(SDL_INIT_VIDEO); + #ifdef CC_BUILD_FLATPAK + SDL_SetHint(SDL_HINT_APP_ID, "net.classicube.flatpak.client"); + #endif + int displayID = SDL_GetPrimaryDisplay(); + Input.Sources = INPUT_SOURCE_NORMAL; + + const SDL_DisplayMode* mode = SDL_GetDesktopDisplayMode(displayID); + dlg_event = SDL_RegisterEvents(1); + + DisplayInfo.Width = mode->w; + DisplayInfo.Height = mode->h; + DisplayInfo.Depth = SDL_BITSPERPIXEL(mode->format); + DisplayInfo.ScaleX = 1; + DisplayInfo.ScaleY = 1; +} + +void Window_Free(void) { } + +static void DoCreateWindow(int width, int height, int flags) { + SDL_PropertiesID props = SDL_CreateProperties(); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width); + SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height); + SDL_SetNumberProperty(props, "flags", flags | SDL_WINDOW_RESIZABLE); + + win_handle = SDL_CreateWindowWithProperties(props); + if (!win_handle) Window_SDLFail("creating window"); + SDL_DestroyProperties(props); + + RefreshWindowBounds(); + Window_Main.Exists = true; + Window_Main.Handle = win_handle; + /* TODO grab using SDL_SetWindowGrab? seems to be unnecessary on Linux at least */ +} +void Window_Create2D(int width, int height) { DoCreateWindow(width, height, 0); } +void Window_Create3D(int width, int height) { DoCreateWindow(width, height, SDL_WINDOW_OPENGL); } + +void Window_SetTitle(const cc_string* title) { + char str[NATIVE_STR_LEN]; + String_EncodeUtf8(str, title); + SDL_SetWindowTitle(win_handle, str); +} + +void Clipboard_GetText(cc_string* value) { + char* ptr = SDL_GetClipboardText(); + if (!ptr) return; + + int len = String_Length(ptr); + String_AppendUtf8(value, ptr, len); + SDL_free(ptr); +} + +void Clipboard_SetText(const cc_string* value) { + char str[NATIVE_STR_LEN]; + String_EncodeUtf8(str, value); + SDL_SetClipboardText(str); +} + +int Window_GetWindowState(void) { + Uint32 flags = SDL_GetWindowFlags(win_handle); + + if (flags & SDL_WINDOW_MINIMIZED) return WINDOW_STATE_MINIMISED; + if (flags & SDL_WINDOW_FULLSCREEN) return WINDOW_STATE_FULLSCREEN; + return WINDOW_STATE_NORMAL; +} + +cc_result Window_EnterFullscreen(void) { + return SDL_SetWindowFullscreen(win_handle, true); +} +cc_result Window_ExitFullscreen(void) { + return SDL_SetWindowFullscreen(win_handle, false); +} + +int Window_IsObscured(void) { + Uint32 flags = SDL_GetWindowFlags(win_handle); + return flags & SDL_WINDOW_OCCLUDED; +} + +void Window_Show(void) { + SDL_ShowWindow(win_handle); +} + +void Window_SetSize(int width, int height) { + SDL_SetWindowSize(win_handle, width, height); +} + +void Window_RequestClose(void) { + SDL_Event e; + e.type = SDL_EVENT_QUIT; + SDL_PushEvent(&e); +} + +static int MapNativeKey(SDL_Keycode k) { + if (k >= SDLK_0 && k <= SDLK_9) { return '0' + (k - SDLK_0); } + if (k >= SDLK_a && k <= SDLK_z) { return 'A' + (k - SDLK_a); } + if (k >= SDLK_F1 && k <= SDLK_F12) { return CCKEY_F1 + (k - SDLK_F1); } + if (k >= SDLK_F13 && k <= SDLK_F24) { return CCKEY_F13 + (k - SDLK_F13); } + /* SDLK_KP_0 isn't before SDLK_KP_1 */ + if (k >= SDLK_KP_1 && k <= SDLK_KP_9) { return CCKEY_KP1 + (k - SDLK_KP_1); } + + switch (k) { + case SDLK_RETURN: return CCKEY_ENTER; + case SDLK_ESCAPE: return CCKEY_ESCAPE; + case SDLK_BACKSPACE: return CCKEY_BACKSPACE; + case SDLK_TAB: return CCKEY_TAB; + case SDLK_SPACE: return CCKEY_SPACE; + case SDLK_QUOTE: return CCKEY_QUOTE; + case SDLK_EQUALS: return CCKEY_EQUALS; + case SDLK_COMMA: return CCKEY_COMMA; + case SDLK_MINUS: return CCKEY_MINUS; + case SDLK_PERIOD: return CCKEY_PERIOD; + case SDLK_SLASH: return CCKEY_SLASH; + case SDLK_SEMICOLON: return CCKEY_SEMICOLON; + case SDLK_LEFTBRACKET: return CCKEY_LBRACKET; + case SDLK_BACKSLASH: return CCKEY_BACKSLASH; + case SDLK_RIGHTBRACKET: return CCKEY_RBRACKET; + case SDLK_BACKQUOTE: return CCKEY_TILDE; + case SDLK_CAPSLOCK: return CCKEY_CAPSLOCK; + case SDLK_PRINTSCREEN: return CCKEY_PRINTSCREEN; + case SDLK_SCROLLLOCK: return CCKEY_SCROLLLOCK; + case SDLK_PAUSE: return CCKEY_PAUSE; + case SDLK_INSERT: return CCKEY_INSERT; + case SDLK_HOME: return CCKEY_HOME; + case SDLK_PAGEUP: return CCKEY_PAGEUP; + case SDLK_DELETE: return CCKEY_DELETE; + case SDLK_END: return CCKEY_END; + case SDLK_PAGEDOWN: return CCKEY_PAGEDOWN; + case SDLK_RIGHT: return CCKEY_RIGHT; + case SDLK_LEFT: return CCKEY_LEFT; + case SDLK_DOWN: return CCKEY_DOWN; + case SDLK_UP: return CCKEY_UP; + + case SDLK_NUMLOCKCLEAR: return CCKEY_NUMLOCK; + case SDLK_KP_DIVIDE: return CCKEY_KP_DIVIDE; + case SDLK_KP_MULTIPLY: return CCKEY_KP_MULTIPLY; + case SDLK_KP_MINUS: return CCKEY_KP_MINUS; + case SDLK_KP_PLUS: return CCKEY_KP_PLUS; + case SDLK_KP_ENTER: return CCKEY_KP_ENTER; + case SDLK_KP_0: return CCKEY_KP0; + case SDLK_KP_PERIOD: return CCKEY_KP_DECIMAL; + + case SDLK_LCTRL: return CCKEY_LCTRL; + case SDLK_LSHIFT: return CCKEY_LSHIFT; + case SDLK_LALT: return CCKEY_LALT; + case SDLK_LGUI: return CCKEY_LWIN; + case SDLK_RCTRL: return CCKEY_RCTRL; + case SDLK_RSHIFT: return CCKEY_RSHIFT; + case SDLK_RALT: return CCKEY_RALT; + case SDLK_RGUI: return CCKEY_RWIN; + } + return INPUT_NONE; +} + +static void OnKeyEvent(const SDL_Event* e) { + cc_bool pressed = e->key.state == SDL_PRESSED; + int key = MapNativeKey(e->key.keysym.sym); + if (key) Input_Set(key, pressed); +} + +static void OnMouseEvent(const SDL_Event* e) { + cc_bool pressed = e->button.state == SDL_PRESSED; + int btn; + switch (e->button.button) { + case SDL_BUTTON_LEFT: btn = CCMOUSE_L; break; + case SDL_BUTTON_MIDDLE: btn = CCMOUSE_M; break; + case SDL_BUTTON_RIGHT: btn = CCMOUSE_R; break; + case SDL_BUTTON_X1: btn = CCMOUSE_X1; break; + case SDL_BUTTON_X2: btn = CCMOUSE_X2; break; + default: return; + } + Input_Set(btn, pressed); +} + +static void OnTextEvent(const SDL_Event* e) { + cc_codepoint cp; + const char* src; + int i, len; + + src = e->text.text; + len = String_Length(src); + + while (len > 0) { + i = Convert_Utf8ToCodepoint(&cp, src, len); + if (!i) break; + + Event_RaiseInt(&InputEvents.Press, cp); + src += i; len -= i; + } +} +static void ProcessDialogEvent(SDL_Event* e); + +void Window_ProcessEvents(double delta) { + SDL_Event e; + while (SDL_PollEvent(&e)) { + switch (e.type) { + + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_KEY_UP: + OnKeyEvent(&e); break; + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_BUTTON_UP: + OnMouseEvent(&e); break; + case SDL_EVENT_MOUSE_WHEEL: + Mouse_ScrollWheel(e.wheel.y); + break; + case SDL_EVENT_MOUSE_MOTION: + Pointer_SetPosition(0, e.motion.x, e.motion.y); + if (Input.RawMode) Event_RaiseRawMove(&PointerEvents.RawMoved, e.motion.xrel, e.motion.yrel); + break; + case SDL_EVENT_TEXT_INPUT: + OnTextEvent(&e); break; + + case SDL_EVENT_QUIT: + Window_Main.Exists = false; + Event_RaiseVoid(&WindowEvents.Closing); + SDL_DestroyWindow(win_handle); + break; + + case SDL_EVENT_RENDER_DEVICE_RESET: + Gfx_LoseContext("SDL device reset event"); + Gfx_RecreateContext(); + break; + + + case SDL_EVENT_WINDOW_EXPOSED: + Event_RaiseVoid(&WindowEvents.RedrawNeeded); + break; + case SDL_EVENT_WINDOW_RESIZED: // TODO SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED + RefreshWindowBounds(); + Event_RaiseVoid(&WindowEvents.Resized); + break; + case SDL_EVENT_WINDOW_MINIMIZED: + case SDL_EVENT_WINDOW_MAXIMIZED: + case SDL_EVENT_WINDOW_RESTORED: + Event_RaiseVoid(&WindowEvents.StateChanged); + break; + case SDL_EVENT_WINDOW_FOCUS_GAINED: + Window_Main.Focused = true; + Event_RaiseVoid(&WindowEvents.FocusChanged); + break; + case SDL_EVENT_WINDOW_FOCUS_LOST: + Window_Main.Focused = false; + Event_RaiseVoid(&WindowEvents.FocusChanged); + break; + case SDL_EVENT_WINDOW_CLOSE_REQUESTED: + Window_RequestClose(); + break; + default: + if (e.type == dlg_event) ProcessDialogEvent(&e); + break; + } + } +} + +static void Cursor_GetRawPos(int* x, int* y) { + float xPos, yPos; + SDL_GetMouseState(&xPos, &yPos); + *x = xPos; *y = yPos; +} +void Cursor_SetPosition(int x, int y) { + SDL_WarpMouseInWindow(win_handle, x, y); +} + +static void Cursor_DoSetVisible(cc_bool visible) { + if (visible) { + SDL_ShowCursor(); + } else { + SDL_HideCursor(); + } +} + +static void ShowDialogCore(const char* title, const char* msg) { + SDL_ShowSimpleMessageBox(0, title, msg, win_handle); +} + +static FileDialogCallback dlgCallback; +static SDL_DialogFileFilter* save_filters; + +static void ProcessDialogEvent(SDL_Event* e) { + char* result = e->user.data1; + int length = e->user.code; + + cc_string path; char pathBuffer[1024]; + String_InitArray(path, pathBuffer); + String_AppendUtf8(&path, result, length); + + dlgCallback(&path); + dlgCallback = NULL; + Mem_Free(result); +} + +static void DialogCallback(void *userdata, const char* const* filelist, int filter) { + if (!filelist) return; /* Error occurred */ + const char* result = filelist[0]; + if (!result) return; /* No file provided */ + + char* path = Mem_Alloc(NATIVE_STR_LEN, 1, "Dialog path"); + cc_string str = String_Init(path, 0, NATIVE_STR_LEN); + String_AppendUtf8(&str, result, String_Length(result)); + + // May need to add file extension when saving, e.g. on Windows + if (save_filters && filter >= 0 && save_filters[filter].pattern) + String_Format1(&str, ".%c", save_filters[filter].pattern); + + // Dialog callback may not be called from the main thread + // (E.g. on windows it is called from a background thread) + SDL_Event e = { 0 }; + e.type = SDL_EVENT_USER; + e.user.code = str.length; + e.user.data1 = path; + SDL_PushEvent(&e); +} + +cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { + // TODO free memory + char* pattern = Mem_Alloc(301, 1, "OpenDialog pattern"); + SDL_DialogFileFilter* filters = Mem_Alloc(2, sizeof(SDL_DialogFileFilter), "OpenDialog filters"); + int i; + + cc_string str = String_Init(pattern, 0, 300); + for (i = 0; ; i++) + { + if (!args->filters[i]) break; + if (i) String_Append(&str, ';'); + String_AppendConst(&str, args->filters[i] + 1); + } + + pattern[str.length] = '\0'; + filters[0].name = args->description; + filters[0].pattern = pattern; + filters[1].name = NULL; + filters[1].pattern = NULL; + + dlgCallback = args->Callback; + save_filters = NULL; + SDL_ShowOpenFileDialog(DialogCallback, NULL, win_handle, filters, NULL, false); + return 0; +} + +#define MAX_SAVE_DIALOG_FILTERS 10 +cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) { + // TODO free memory + char* defName = Mem_Alloc(NATIVE_STR_LEN, 1, "SaveDialog default"); + SDL_DialogFileFilter* filters = Mem_Alloc(MAX_SAVE_DIALOG_FILTERS + 1, sizeof(SDL_DialogFileFilter), "SaveDialog filters"); + int i; + String_EncodeUtf8(defName, &args->defaultName); + + for (i = 0; i < MAX_SAVE_DIALOG_FILTERS; i++) + { + if (!args->filters[i]) break; + filters[i].name = args->titles[i]; + filters[i].pattern = args->filters[i] + 1; // skip . + } + + filters[i].name = NULL; + filters[i].pattern = NULL; + + dlgCallback = args->Callback; + save_filters = filters; + SDL_ShowSaveFileDialog(DialogCallback, NULL, win_handle, filters, defName); + return 0; +} + +static SDL_Surface* win_surface; +static SDL_Surface* blit_surface; + +void Window_AllocFramebuffer(struct Bitmap* bmp) { + SDL_PixelFormat* fmt; + win_surface = SDL_GetWindowSurface(win_handle); + if (!win_surface) Window_SDLFail("getting window surface"); + + fmt = win_surface->format; + if (fmt->bits_per_pixel != 32) { + /* Slow path: e.g. 15 or 16 bit pixels */ + Platform_Log1("Slow color depth: %b bpp", &fmt->bits_per_pixel); + blit_surface = SDL_CreateSurface(win_surface->w, win_surface->h, SDL_PIXELFORMAT_RGBA32); + if (!blit_surface) Window_SDLFail("creating blit surface"); + + SDL_SetSurfaceBlendMode(blit_surface, SDL_BLENDMODE_NONE); + bmp->scan0 = blit_surface->pixels; + } else { + /* Fast path: 32 bit pixels */ + if (SDL_MUSTLOCK(win_surface)) { + int ret = SDL_LockSurface(win_surface); + if (ret < 0) Window_SDLFail("locking window surface"); + } + bmp->scan0 = win_surface->pixels; + } +} + +void Window_DrawFramebuffer(Rect2D r, struct Bitmap* bmp) { + SDL_Rect rect; + rect.x = r.x; rect.w = r.Width; + rect.y = r.y; rect.h = r.Height; + + if (blit_surface) SDL_BlitSurface(blit_surface, &rect, win_surface, &rect); + SDL_UpdateWindowSurfaceRects(win_handle, &rect, 1); +} + +void Window_FreeFramebuffer(struct Bitmap* bmp) { + if (blit_surface) SDL_DestroySurface(blit_surface); + blit_surface = NULL; + + /* SDL docs explicitly say to NOT free window surface */ + /* https://wiki.libsdl.org/SDL_GetWindowSurface */ + /* TODO: Do we still need to unlock it though? */ +} + +void Window_OpenKeyboard(struct OpenKeyboardArgs* args) { SDL_StartTextInput(); } +void Window_SetKeyboardText(const cc_string* text) { } +void Window_CloseKeyboard(void) { SDL_StopTextInput(); } + +void Window_EnableRawMouse(void) { + RegrabMouse(); + SDL_SetRelativeMouseMode(true); + Input.RawMode = true; +} +void Window_UpdateRawMouse(void) { CentreMousePosition(); } + +void Window_DisableRawMouse(void) { + RegrabMouse(); + SDL_SetRelativeMouseMode(false); + Input.RawMode = false; +} + + +/*########################################################################################################################* +*-----------------------------------------------------OpenGL context------------------------------------------------------* +*#########################################################################################################################*/ +#if defined CC_BUILD_GL && !defined CC_BUILD_EGL +static SDL_GLContext win_ctx; + +void GLContext_Create(void) { + struct GraphicsMode mode; + InitGraphicsMode(&mode); + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, mode.R); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, mode.G); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, mode.B); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, mode.A); + + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, GLCONTEXT_DEFAULT_DEPTH); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, true); +#ifdef CC_BUILD_GLES + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#endif + + win_ctx = SDL_GL_CreateContext(win_handle); + if (!win_ctx) Window_SDLFail("creating OpenGL context"); +} + +void GLContext_Update(void) { } +cc_bool GLContext_TryRestore(void) { return true; } +void GLContext_Free(void) { + SDL_GL_DeleteContext(win_ctx); + win_ctx = NULL; +} + +void* GLContext_GetAddress(const char* function) { + return SDL_GL_GetProcAddress(function); +} + +cc_bool GLContext_SwapBuffers(void) { + SDL_GL_SwapWindow(win_handle); + return true; +} + +void GLContext_SetFpsLimit(cc_bool vsync, float minFrameMs) { + SDL_GL_SetSwapInterval(vsync); +} +void GLContext_GetApiInfo(cc_string* info) { } +#endif +#endif diff --git a/src/Window_Switch.c b/src/Window_Switch.c index d45708346..8108b5348 100644 --- a/src/Window_Switch.c +++ b/src/Window_Switch.c @@ -58,7 +58,7 @@ void Window_Init(void) { // Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller) padInitializeDefault(&pad); - DisplayInfo.Depth = 4; // 32 bit + DisplayInfo.Depth = 4; // 32 bit TODO wrong, this is actually 4 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -108,58 +108,42 @@ void Window_RequestClose(void) { *----------------------------------------------------Input processing-----------------------------------------------------* *#########################################################################################################################*/ static void HandleButtons(u64 mods) { - Input_SetNonRepeatable(CCPAD_L, mods & HidNpadButton_L); - Input_SetNonRepeatable(CCPAD_R, mods & HidNpadButton_R); + Gamepad_SetButton(CCPAD_L, mods & HidNpadButton_L); + Gamepad_SetButton(CCPAD_R, mods & HidNpadButton_R); - Input_SetNonRepeatable(CCPAD_A, mods & HidNpadButton_A); - Input_SetNonRepeatable(CCPAD_B, mods & HidNpadButton_B); - Input_SetNonRepeatable(CCPAD_X, mods & HidNpadButton_X); - Input_SetNonRepeatable(CCPAD_Y, mods & HidNpadButton_Y); + Gamepad_SetButton(CCPAD_A, mods & HidNpadButton_A); + Gamepad_SetButton(CCPAD_B, mods & HidNpadButton_B); + Gamepad_SetButton(CCPAD_X, mods & HidNpadButton_X); + Gamepad_SetButton(CCPAD_Y, mods & HidNpadButton_Y); - Input_SetNonRepeatable(CCPAD_START, mods & HidNpadButton_Plus); - Input_SetNonRepeatable(CCPAD_SELECT, mods & HidNpadButton_Minus); + Gamepad_SetButton(CCPAD_START, mods & HidNpadButton_Plus); + Gamepad_SetButton(CCPAD_SELECT, mods & HidNpadButton_Minus); - Input_SetNonRepeatable(CCPAD_LEFT, mods & HidNpadButton_Left); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & HidNpadButton_Right); - Input_SetNonRepeatable(CCPAD_UP, mods & HidNpadButton_Up); - Input_SetNonRepeatable(CCPAD_DOWN, mods & HidNpadButton_Down); + Gamepad_SetButton(CCPAD_LEFT, mods & HidNpadButton_Left); + Gamepad_SetButton(CCPAD_RIGHT, mods & HidNpadButton_Right); + Gamepad_SetButton(CCPAD_UP, mods & HidNpadButton_Up); + Gamepad_SetButton(CCPAD_DOWN, mods & HidNpadButton_Down); } -static void ProcessJoystickInput_L(HidAnalogStickState* pos) { +#define AXIS_SCALE 512.0f +static void ProcessJoystickInput(int axis, HidAnalogStickState* pos, double delta) { // May not be exactly 0 on actual hardware if (Math_AbsI(pos->x) <= 16) pos->x = 0; if (Math_AbsI(pos->y) <= 16) pos->y = 0; - Input.JoystickMovement = (pos->x != 0 || pos->y != 0); - if (!Input.JoystickMovement) return; - Input.JoystickAngle = Math_Atan2(pos->x, -pos->y); -} - -static void ProcessJoystickInput_R(HidAnalogStickState* pos) { - // May not be exactly 0 on actual hardware - if (Math_AbsI(pos->x) <= 16) pos->x = 0; - if (Math_AbsI(pos->y) <= 16) pos->y = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, pos->x / 512.f, -pos->y / 512.f); + Gamepad_SetAxis(axis, pos->x / AXIS_SCALE, -pos->y / AXIS_SCALE, delta); } static void ProcessTouchInput(void) { - static int currX, currY, prev_touchcount=0; - HidTouchScreenState state={0}; + static int prev_touchcount = 0; + HidTouchScreenState state = {0}; hidGetTouchScreenStates(&state, 1); - if (state.count && !prev_touchcount) { // stylus went down - currX = state.touches[0].x; - currY = state.touches[0].y; - Input_AddTouch(0, currX, currY); - } else if (state.count) { // stylus is down - currX = state.touches[0].x; - currY = state.touches[0].y; - Input_UpdateTouch(0, currX, currY); - } else if (!state.count && prev_touchcount) { // stylus was lifted - Input_RemoveTouch(0, currX, currY); + if (state.count) { + Input_AddTouch(0, state.touches[0].x, state.touches[0].y); + } else if (prev_touchcount) { + Input_RemoveTouch(0, Pointers[0].x, Pointers[0].y); } - prev_touchcount = state.count; } @@ -179,8 +163,8 @@ void Window_ProcessEvents(double delta) { // Read the sticks' position HidAnalogStickState analog_stick_l = padGetStickPos(&pad, 0); HidAnalogStickState analog_stick_r = padGetStickPos(&pad, 1); - ProcessJoystickInput_L(&analog_stick_l); - ProcessJoystickInput_R(&analog_stick_r); + ProcessJoystickInput(PAD_AXIS_LEFT, &analog_stick_l, delta); + ProcessJoystickInput(PAD_AXIS_RIGHT, &analog_stick_r, delta); ProcessTouchInput(); } diff --git a/src/Window_Web.c b/src/Window_Web.c index f51bbedae..054589b60 100644 --- a/src/Window_Web.c +++ b/src/Window_Web.c @@ -534,46 +534,34 @@ static void ProcessPendingResize(void) { static void ProcessGamepadButtons(EmscriptenGamepadEvent* ev) { int numButtons = ev->numButtons; - Input_SetNonRepeatable(CCPAD_A, GetGamepadButton(0)); - Input_SetNonRepeatable(CCPAD_B, GetGamepadButton(1)); - Input_SetNonRepeatable(CCPAD_X, GetGamepadButton(2)); - Input_SetNonRepeatable(CCPAD_Y, GetGamepadButton(3)); - - Input_SetNonRepeatable(CCPAD_ZL, GetGamepadButton(4)); - Input_SetNonRepeatable(CCPAD_ZR, GetGamepadButton(5)); - Input_SetNonRepeatable(CCPAD_L, GetGamepadButton(6)); - Input_SetNonRepeatable(CCPAD_R, GetGamepadButton(7)); - - Input_SetNonRepeatable(CCPAD_SELECT, GetGamepadButton( 8)); - Input_SetNonRepeatable(CCPAD_START, GetGamepadButton( 9)); - Input_SetNonRepeatable(CCPAD_LSTICK, GetGamepadButton(10)); - Input_SetNonRepeatable(CCPAD_RSTICK, GetGamepadButton(11)); + Gamepad_SetButton(CCPAD_A, GetGamepadButton(0)); + Gamepad_SetButton(CCPAD_B, GetGamepadButton(1)); + Gamepad_SetButton(CCPAD_X, GetGamepadButton(2)); + Gamepad_SetButton(CCPAD_Y, GetGamepadButton(3)); + + Gamepad_SetButton(CCPAD_ZL, GetGamepadButton(4)); + Gamepad_SetButton(CCPAD_ZR, GetGamepadButton(5)); + Gamepad_SetButton(CCPAD_L, GetGamepadButton(6)); + Gamepad_SetButton(CCPAD_R, GetGamepadButton(7)); + + Gamepad_SetButton(CCPAD_SELECT, GetGamepadButton( 8)); + Gamepad_SetButton(CCPAD_START, GetGamepadButton( 9)); + Gamepad_SetButton(CCPAD_LSTICK, GetGamepadButton(10)); + Gamepad_SetButton(CCPAD_RSTICK, GetGamepadButton(11)); - Input_SetNonRepeatable(CCPAD_UP, GetGamepadButton(12)); - Input_SetNonRepeatable(CCPAD_DOWN, GetGamepadButton(13)); - Input_SetNonRepeatable(CCPAD_LEFT, GetGamepadButton(14)); - Input_SetNonRepeatable(CCPAD_RIGHT, GetGamepadButton(15)); + Gamepad_SetButton(CCPAD_UP, GetGamepadButton(12)); + Gamepad_SetButton(CCPAD_DOWN, GetGamepadButton(13)); + Gamepad_SetButton(CCPAD_LEFT, GetGamepadButton(14)); + Gamepad_SetButton(CCPAD_RIGHT, GetGamepadButton(15)); } -static void ProcessGamepadCamera(float x, float y, double delta) { - float scale = (delta * 60.0) * 8.0f; - - /* Deadzone adjustment */ - if (x >= -0.1 && x <= 0.1) x = 0; - if (y >= -0.1 && y <= 0.1) y = 0; - if (x == 0 && y == 0) return; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, x * scale, y * scale); -} - -static void ProcessGamepadMovement(float x, float y) { +#define AXIS_SCALE 8.0f +static void ProcessGamepadAxis(int axis, float x, float y, double delta) { /* Deadzone adjustment */ if (x >= -0.1 && x <= 0.1) x = 0; if (y >= -0.1 && y <= 0.1) y = 0; - if (x == 0 && y == 0) return; - Input.JoystickMovement = true; - Input.JoystickAngle = Math_Atan2(x, y); + Gamepad_SetAxis(axis, x * AXIS_SCALE, y * AXIS_SCALE, delta); } static void ProcessGamepadInput(EmscriptenGamepadEvent* ev, double delta) { @@ -582,10 +570,10 @@ static void ProcessGamepadInput(EmscriptenGamepadEvent* ev, double delta) { ProcessGamepadButtons(ev); if (ev->numAxes >= 4) { - ProcessGamepadMovement(ev->axis[0], ev->axis[1]); - ProcessGamepadCamera( ev->axis[2], ev->axis[3], delta); + ProcessGamepadAxis(PAD_AXIS_LEFT, ev->axis[0], ev->axis[1], delta); + ProcessGamepadAxis(PAD_AXIS_RIGHT, ev->axis[2], ev->axis[3], delta); } else if (ev->numAxes >= 2) { - ProcessGamepadCamera(ev->axis[0], ev->axis[1], delta); + ProcessGamepadAxis(PAD_AXIS_RIGHT, ev->axis[0], ev->axis[1], delta); } } diff --git a/src/Window_WiiU.c b/src/Window_WiiU.c index f910abdc6..f3a09fbb2 100644 --- a/src/Window_WiiU.c +++ b/src/Window_WiiU.c @@ -78,23 +78,23 @@ void Window_RequestClose(void) { } static void ProcessVpadButtons(int mods) { - Input_SetNonRepeatable(CCPAD_L, mods & VPAD_BUTTON_L); - Input_SetNonRepeatable(CCPAD_R, mods & VPAD_BUTTON_R); - Input_SetNonRepeatable(CCPAD_ZL, mods & VPAD_BUTTON_ZL); - Input_SetNonRepeatable(CCPAD_ZR, mods & VPAD_BUTTON_ZR); + Gamepad_SetButton(CCPAD_L, mods & VPAD_BUTTON_L); + Gamepad_SetButton(CCPAD_R, mods & VPAD_BUTTON_R); + Gamepad_SetButton(CCPAD_ZL, mods & VPAD_BUTTON_ZL); + Gamepad_SetButton(CCPAD_ZR, mods & VPAD_BUTTON_ZR); - Input_SetNonRepeatable(CCPAD_A, mods & VPAD_BUTTON_A); - Input_SetNonRepeatable(CCPAD_B, mods & VPAD_BUTTON_B); - Input_SetNonRepeatable(CCPAD_X, mods & VPAD_BUTTON_X); - Input_SetNonRepeatable(CCPAD_Y, mods & VPAD_BUTTON_Y); + Gamepad_SetButton(CCPAD_A, mods & VPAD_BUTTON_A); + Gamepad_SetButton(CCPAD_B, mods & VPAD_BUTTON_B); + Gamepad_SetButton(CCPAD_X, mods & VPAD_BUTTON_X); + Gamepad_SetButton(CCPAD_Y, mods & VPAD_BUTTON_Y); - Input_SetNonRepeatable(CCPAD_START, mods & VPAD_BUTTON_PLUS); - Input_SetNonRepeatable(CCPAD_SELECT, mods & VPAD_BUTTON_MINUS); + Gamepad_SetButton(CCPAD_START, mods & VPAD_BUTTON_PLUS); + Gamepad_SetButton(CCPAD_SELECT, mods & VPAD_BUTTON_MINUS); - Input_SetNonRepeatable(CCPAD_LEFT, mods & VPAD_BUTTON_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & VPAD_BUTTON_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & VPAD_BUTTON_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & VPAD_BUTTON_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & VPAD_BUTTON_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & VPAD_BUTTON_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & VPAD_BUTTON_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & VPAD_BUTTON_DOWN); } @@ -186,4 +186,4 @@ cc_result Window_OpenFileDialog(const struct OpenFileDialogArgs* args) { cc_result Window_SaveFileDialog(const struct SaveFileDialogArgs* args) { return ERR_NOT_SUPPORTED; } -#endif \ No newline at end of file +#endif diff --git a/src/Window_X11.c b/src/Window_X11.c index d820ac7dc..6bb0c01a1 100644 --- a/src/Window_X11.c +++ b/src/Window_X11.c @@ -1,5 +1,5 @@ #include "Core.h" -#if defined CC_BUILD_X11 && !defined CC_BUILD_SDL +#if defined CC_BUILD_X11 && !defined CC_BUILD_SDL2 && !defined CC_BUILD_SDL3 #include "_WindowBase.h" #include "String.h" #include "Funcs.h" @@ -338,8 +338,13 @@ static void DoCreateWindow(int width, int height) { /* So right name appears in e.g. Ubuntu Unity launchbar */ XClassHint hint = { 0 }; - hint.res_name = GAME_APP_TITLE; - hint.res_class = GAME_APP_TITLE; + #ifdef CC_BUILD_FLATPAK + hint.res_name = "net.classicube.flatpak.client"; + hint.res_class = "net.classicube.flatpak.client"; + #else + hint.res_name = GAME_APP_TITLE; + hint.res_class = GAME_APP_TITLE; + #endif XSetClassHint(win_display, win_handle, &hint); ApplyIcon(); diff --git a/src/Window_Xbox.c b/src/Window_Xbox.c index af156d201..f4bfee284 100644 --- a/src/Window_Xbox.c +++ b/src/Window_Xbox.c @@ -62,7 +62,6 @@ void Window_Init(void) { DisplayInfo.Width = mode.width; DisplayInfo.Height = mode.height; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -120,43 +119,33 @@ void Window_RequestClose(void) { static void HandleButtons(xid_gamepad_in* gp) { int mods = gp->dButtons; - Input_SetNonRepeatable(CCPAD_L, gp->l > 0x7F); - Input_SetNonRepeatable(CCPAD_R, gp->r > 0x7F); - Input_SetNonRepeatable(CCPAD_ZL, gp->white > 0x7F); - Input_SetNonRepeatable(CCPAD_ZR, gp->black > 0x7F); + Gamepad_SetButton(CCPAD_L, gp->l > 0x7F); + Gamepad_SetButton(CCPAD_R, gp->r > 0x7F); + Gamepad_SetButton(CCPAD_ZL, gp->white > 0x7F); + Gamepad_SetButton(CCPAD_ZR, gp->black > 0x7F); - Input_SetNonRepeatable(CCPAD_A, gp->a > 0x7F); - Input_SetNonRepeatable(CCPAD_B, gp->b > 0x7F); - Input_SetNonRepeatable(CCPAD_X, gp->x > 0x7F); - Input_SetNonRepeatable(CCPAD_Y, gp->y > 0x7F); + Gamepad_SetButton(CCPAD_A, gp->a > 0x7F); + Gamepad_SetButton(CCPAD_B, gp->b > 0x7F); + Gamepad_SetButton(CCPAD_X, gp->x > 0x7F); + Gamepad_SetButton(CCPAD_Y, gp->y > 0x7F); - Input_SetNonRepeatable(CCPAD_START, mods & XINPUT_GAMEPAD_START); - Input_SetNonRepeatable(CCPAD_SELECT, mods & XINPUT_GAMEPAD_BACK); - Input_SetNonRepeatable(CCPAD_LSTICK, mods & XINPUT_GAMEPAD_LEFT_THUMB); - Input_SetNonRepeatable(CCPAD_RSTICK, mods & XINPUT_GAMEPAD_RIGHT_THUMB); + Gamepad_SetButton(CCPAD_START, mods & XINPUT_GAMEPAD_START); + Gamepad_SetButton(CCPAD_SELECT, mods & XINPUT_GAMEPAD_BACK); + Gamepad_SetButton(CCPAD_LSTICK, mods & XINPUT_GAMEPAD_LEFT_THUMB); + Gamepad_SetButton(CCPAD_RSTICK, mods & XINPUT_GAMEPAD_RIGHT_THUMB); - Input_SetNonRepeatable(CCPAD_LEFT, mods & XINPUT_GAMEPAD_DPAD_LEFT); - Input_SetNonRepeatable(CCPAD_RIGHT, mods & XINPUT_GAMEPAD_DPAD_RIGHT); - Input_SetNonRepeatable(CCPAD_UP, mods & XINPUT_GAMEPAD_DPAD_UP); - Input_SetNonRepeatable(CCPAD_DOWN, mods & XINPUT_GAMEPAD_DPAD_DOWN); + Gamepad_SetButton(CCPAD_LEFT, mods & XINPUT_GAMEPAD_DPAD_LEFT); + Gamepad_SetButton(CCPAD_RIGHT, mods & XINPUT_GAMEPAD_DPAD_RIGHT); + Gamepad_SetButton(CCPAD_UP, mods & XINPUT_GAMEPAD_DPAD_UP); + Gamepad_SetButton(CCPAD_DOWN, mods & XINPUT_GAMEPAD_DPAD_DOWN); } -static void HandleJoystick_Left(int x, int y) { +#define AXIS_SCALE 8192.0f +static void HandleJoystick(int axis, int x, int y, double delta) { if (Math_AbsI(x) <= 512) x = 0; if (Math_AbsI(y) <= 512) y = 0; - if (x == 0 && y == 0) return; - Input.JoystickMovement = true; - Input.JoystickAngle = Math_Atan2(x, -y); -} - -static void HandleJoystick_Right(int x, int y, double delta) { - float scale = (delta * 60.0) / 8192.0f; - - if (Math_AbsI(x) <= 512) x = 0; - if (Math_AbsI(y) <= 512) y = 0; - - Event_RaiseRawMove(&ControllerEvents.RawMoved, x * scale, -y * scale); + Gamepad_SetAxis(axis, x / AXIS_SCALE, -y / AXIS_SCALE, delta); } void Window_ProcessEvents(double delta) { @@ -165,8 +154,8 @@ void Window_ProcessEvents(double delta) { if (!xid_ctrl) return; HandleButtons(&gp_state); - HandleJoystick_Left( gp_state.leftStickX, gp_state.leftStickY ); - HandleJoystick_Right(gp_state.rightStickX, gp_state.rightStickY, delta); + HandleJoystick(PAD_AXIS_LEFT, gp_state.leftStickX, gp_state.leftStickY, delta); + HandleJoystick(PAD_AXIS_RIGHT, gp_state.rightStickX, gp_state.rightStickY, delta); } void Cursor_SetPosition(int x, int y) { } // Makes no sense for Xbox diff --git a/src/Window_Xbox360.c b/src/Window_Xbox360.c index 64cbf8fbb..9d9ab8592 100644 --- a/src/Window_Xbox360.c +++ b/src/Window_Xbox360.c @@ -33,7 +33,6 @@ void Window_Init(void) { DisplayInfo.Width = ai->width; DisplayInfo.Height = ai->height; - DisplayInfo.Depth = 4; // 32 bit DisplayInfo.ScaleX = 1; DisplayInfo.ScaleY = 1; @@ -87,21 +86,21 @@ struct controller_data_s */ static void HandleButtons(struct controller_data_s* pad) { - Input_SetNonRepeatable(CCPAD_L, pad->lb); - Input_SetNonRepeatable(CCPAD_R, pad->rb); + Gamepad_SetButton(CCPAD_L, pad->lb); + Gamepad_SetButton(CCPAD_R, pad->rb); - Input_SetNonRepeatable(CCPAD_A, pad->a); - Input_SetNonRepeatable(CCPAD_B, pad->b); - Input_SetNonRepeatable(CCPAD_X, pad->x); - Input_SetNonRepeatable(CCPAD_Y, pad->y); + Gamepad_SetButton(CCPAD_A, pad->a); + Gamepad_SetButton(CCPAD_B, pad->b); + Gamepad_SetButton(CCPAD_X, pad->x); + Gamepad_SetButton(CCPAD_Y, pad->y); - Input_SetNonRepeatable(CCPAD_START, pad->start); - Input_SetNonRepeatable(CCPAD_SELECT, pad->back); + Gamepad_SetButton(CCPAD_START, pad->start); + Gamepad_SetButton(CCPAD_SELECT, pad->back); - Input_SetNonRepeatable(CCPAD_LEFT, pad->left); - Input_SetNonRepeatable(CCPAD_RIGHT, pad->right); - Input_SetNonRepeatable(CCPAD_UP, pad->up); - Input_SetNonRepeatable(CCPAD_DOWN, pad->down); + Gamepad_SetButton(CCPAD_LEFT, pad->left); + Gamepad_SetButton(CCPAD_RIGHT, pad->right); + Gamepad_SetButton(CCPAD_UP, pad->up); + Gamepad_SetButton(CCPAD_DOWN, pad->down); } void Window_ProcessEvents(double delta) { diff --git a/src/_GLShared.h b/src/_GLShared.h index c6d7fa10d..ca82a6ace 100644 --- a/src/_GLShared.h +++ b/src/_GLShared.h @@ -232,7 +232,7 @@ void Gfx_CalcPerspectiveMatrix(struct Matrix* matrix, float fov, float aspect, f /*########################################################################################################################* *-----------------------------------------------------------Misc----------------------------------------------------------* *#########################################################################################################################*/ -static BitmapCol* GL_GetRow(struct Bitmap* bmp, int y) { +static BitmapCol* GL_GetRow(struct Bitmap* bmp, int y, void* ctx) { /* OpenGL stores bitmap in bottom-up order, so flip order when saving */ return Bitmap_GetRow(bmp, (bmp->height - 1) - y); } @@ -249,7 +249,7 @@ cc_result Gfx_TakeScreenshot(struct Stream* output) { if (!bmp.scan0) return ERR_OUT_OF_MEMORY; glReadPixels(0, 0, bmp.width, bmp.height, PIXEL_FORMAT, TRANSFER_FORMAT, bmp.scan0); - res = Png_Encode(&bmp, output, GL_GetRow, false); + res = Png_Encode(&bmp, output, GL_GetRow, false, NULL); Mem_Free(bmp.scan0); return res; } diff --git a/src/_GraphicsBase.h b/src/_GraphicsBase.h index 8fd3e41ae..3efd775b6 100644 --- a/src/_GraphicsBase.h +++ b/src/_GraphicsBase.h @@ -136,7 +136,7 @@ void Gfx_RecreateContext(void) { } cc_bool reducedPerformance; -static void TickReducedPerformance(void) { +static CC_INLINE void TickReducedPerformance(void) { Thread_Sleep(100); /* 10 FPS */ if (reducedPerformance) return; @@ -144,7 +144,7 @@ static void TickReducedPerformance(void) { Chat_AddOf(&Gfx_LowPerfMessage, MSG_TYPE_EXTRASTATUS_2); } -static void EndReducedPerformance(void) { +static CC_INLINE void EndReducedPerformance(void) { if (!reducedPerformance) return; reducedPerformance = false; Chat_AddOf(&String_Empty, MSG_TYPE_EXTRASTATUS_2); diff --git a/src/_HttpBase.h b/src/_HttpBase.h index 55a11a15e..d0b0c4bc5 100644 --- a/src/_HttpBase.h +++ b/src/_HttpBase.h @@ -192,7 +192,7 @@ static void Http_FinishRequest(struct HttpRequest* req) { Mutex_Lock(processedMutex); { - req->timeDownloaded = DateTime_CurrentUTC_MS(); + req->timeDownloaded = DateTime_CurrentUTC(); RequestList_Append(&processedReqs, req, false); } Mutex_Unlock(processedMutex); @@ -205,10 +205,10 @@ static void Http_CleanCacheTask(struct ScheduledTask* task) { Mutex_Lock(processedMutex); { - TimeMS now = DateTime_CurrentUTC_MS(); + TimeMS now = DateTime_CurrentUTC(); for (i = processedReqs.count - 1; i >= 0; i--) { item = &processedReqs.entries[i]; - if (item->timeDownloaded + (10 * 1000) >= now) continue; + if (now > item->timeDownloaded + 10) continue; HttpRequest_Free(item); RequestList_RemoveAt(&processedReqs, i); diff --git a/src/_WindowBase.h b/src/_WindowBase.h index 81011c01f..02fcec3ed 100644 --- a/src/_WindowBase.h +++ b/src/_WindowBase.h @@ -43,18 +43,18 @@ static void RegrabMouse(void) { CentreMousePosition(); } -static void DefaultEnableRawMouse(void) { +static CC_INLINE void DefaultEnableRawMouse(void) { Input.RawMode = true; RegrabMouse(); Cursor_SetVisible(false); } -static void DefaultUpdateRawMouse(void) { +static CC_INLINE void DefaultUpdateRawMouse(void) { MoveRawUsingCursorDelta(); CentreMousePosition(); } -static void DefaultDisableRawMouse(void) { +static CC_INLINE void DefaultDisableRawMouse(void) { Input.RawMode = false; RegrabMouse(); Cursor_SetVisible(true); diff --git a/src/interop_ios.m b/src/interop_ios.m index c94410cb9..54faee342 100644 --- a/src/interop_ios.m +++ b/src/interop_ios.m @@ -34,7 +34,7 @@ @interface CCWindow : UIWindow @end -@interface CCViewController : UIViewController +@interface CCViewController : UIViewController @end @interface CCAppDelegate : UIResponder @@ -217,6 +217,12 @@ - (UIRectEdge)preferredScreenEdgesDeferringSystemGestures { // significantly the chance of accidentally triggering this gesture return UIRectEdgeBottom; } + +// == UIAlertViewDelegate == +static int alert_completed; +- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { + alert_completed = true; +} @end @implementation CCAppDelegate @@ -430,20 +436,27 @@ void Window_ProcessEvents(double delta) { void ShowDialogCore(const char* title, const char* msg) { // UIAlertController - iOS 8.0 // UIAlertAction - iOS 8.0 + // UIAlertView - iOS 2.0 Platform_LogConst(title); Platform_LogConst(msg); NSString* _title = [NSString stringWithCString:title encoding:NSASCIIStringEncoding]; NSString* _msg = [NSString stringWithCString:msg encoding:NSASCIIStringEncoding]; - __block int completed = false; + alert_completed = false; +#ifdef TARGET_OS_TV UIAlertController* alert = [UIAlertController alertControllerWithTitle:_title message:_msg preferredStyle:UIAlertControllerStyleAlert]; - UIAlertAction* okBtn = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* act) { completed = true; }]; + UIAlertAction* okBtn = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction* act) { alert_completed = true; }]; [alert addAction:okBtn]; [cc_controller presentViewController:alert animated:YES completion: Nil]; +#else + UIAlertView* alert = [UIAlertView alloc]; + alert = [alert initWithTitle:_title message:_msg delegate:cc_controller cancelButtonTitle:@"OK" otherButtonTitles:nil]; + [alert show]; +#endif // TODO clicking outside message box crashes launcher // loop until alert is closed TODO avoid sleeping - while (!completed) { + while (!alert_completed) { Window_ProcessEvents(0.0); Thread_Sleep(16); }