diff --git a/Dockerfile b/Dockerfile index d749b567..0c67b544 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ -FROM --platform=amd64 archlinux:latest +ARG BUILDPLATFORM +FROM --platform=${BUILDPLATFORM} archlinux:latest RUN pacman -Syyu base-devel --noconfirm RUN pacman -Syyu arm-none-eabi-gcc --noconfirm RUN pacman -Syyu arm-none-eabi-newlib --noconfirm diff --git a/Makefile b/Makefile index d55d9db3..66380bbc 100644 --- a/Makefile +++ b/Makefile @@ -41,6 +41,7 @@ ENABLE_BYP_RAW_DEMODULATORS ?= 0 ENABLE_BLMIN_TMP_OFF ?= 0 ENABLE_SCAN_RANGES ?= 1 ENABLE_FEAT_F4HWN ?= 1 +ENABLE_FEAT_F4HWN_GAME ?= 0 ENABLE_FEAT_F4HWN_SCREENSHOT ?= 0 ENABLE_FEAT_F4HWN_SPECTRUM ?= 1 ENABLE_FEAT_F4HWN_RX_TX_TIMER ?= 1 @@ -153,6 +154,9 @@ OBJS += app/menu.o ifeq ($(ENABLE_SPECTRUM), 1) OBJS += app/spectrum.o endif +ifeq ($(ENABLE_FEAT_F4HWN_GAME), 1) +OBJS += app/breakout.o +endif OBJS += app/scanner.o ifeq ($(ENABLE_UART),1) OBJS += app/uart.o @@ -230,7 +234,7 @@ ifeq ($(ENABLE_FEAT_F4HWN),1) VERSION_STRING_1 ?= v0.22 AUTHOR_STRING_2 ?= F4HWN - VERSION_STRING_2 ?= v4.0 + VERSION_STRING_2 ?= v4.1 EDITION_STRING ?= Custom @@ -430,6 +434,9 @@ ifeq ($(ENABLE_FEAT_F4HWN),1) else CFLAGS += -DSQL_TONE=550 endif +ifeq ($(ENABLE_FEAT_F4HWN_GAME),1) + CFLAGS += -DENABLE_FEAT_F4HWN_GAME +endif ifeq ($(ENABLE_FEAT_F4HWN_SCREENSHOT),1) CFLAGS += -DENABLE_FEAT_F4HWN_SCREENSHOT endif diff --git a/README.md b/README.md index fde77c08..ae934209 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Anyway, have fun. # Donations -Special thanks to Jean-Cyrille F6IWW (2 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW, Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (3 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF, Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Meinhard Frank Günther, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB, Ian KE2CHJ and Daryl VK3AWA for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻 +Special thanks to Jean-Cyrille F6IWW (2 times), Fabrice 14RC123, David F4BPP, Olivier 14RC206, Frédéric F4ESO, Stéphane F5LGW, Jorge Ornelas (4 times), Laurent F4AXK, Christophe Morel, Clayton W0LED, Pierre Antoine F6FWB, Jean-Claude 14FRS3306, Thierry F4GVO, Eric F1NOU, PricelessToolkit, Ady M6NYJ, Tom McGovern (3 times), Joseph Roth, Pierre-Yves Colin, Frank DJ7FG, Marcel Testaz, Brian Frobisher, Yannick F4JFO, Paolo Bussola, Dirk DL8DF, Levente Szőke (2 times), Bernard-Michel Herrera, Jérôme Saintespes, Paul Davies, RS (3 times), Johan F4WAT, Robert Wörle, Rafael Sundorf, Paul Harker, Peter Fintl, Pascal F4ICR (2 times), Mike DL2MF, Eric KI1C (2 times), Phil G0ELM, Jérôme Lambert, Meinhard Frank Günther, Eliot Vedel, Alfonso EA7KDF, Jean-François F1EVM, Robert DC1RDB, Ian KE2CHJ, Daryl VK3AWA, Roberto Brunelli, Robert Boardman and Stephen Oliver for their [donations](https://www.paypal.com/paypalme/F4HWN). That’s so kind of them. Thanks so much 🙏🏻 ## Table of Contents diff --git a/app/breakout.c b/app/breakout.c new file mode 100644 index 00000000..ddeabd49 --- /dev/null +++ b/app/breakout.c @@ -0,0 +1,504 @@ +/* Copyright 2025 Armel F4HWN + * https://github.com/armel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app/breakout.h" + +static uint32_t randSeed = 1; +static uint8_t blockAnim = 0; + +bool isInitialized = false; +bool isPaused = false; +bool isBeep = false; + +uint8_t levelCountBreackout = 1; + +uint16_t tone = 0; +uint16_t score = 0; + +int16_t ballCount = BALL_NUMBER; + +char str[12]; + +KeyboardState kbd = {KEY_INVALID, KEY_INVALID, 0}; + +Brick brick[BRICK_NUMBER]; +Racket racket; +Ball ball; + +const uint8_t BITMAP_block[4][15] = +{ + { + 0b00011110, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00011110, + }, + { + 0b00011110, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00011110, + }, + { + 0b00011110, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00011110, + }, + { + 0b00011110, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00110001, + 0b00101001, + 0b00100101, + 0b00100011, + 0b00011110, + } +}; + +const uint8_t BITMAP_blockOn[15] = +{ + 0b00011110, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00111111, + 0b00011110, +}; + +const uint8_t BITMAP_blockEmpty[15] = +{ + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, +}; + +// Initialise seed +void srand_custom(uint32_t seed) { + randSeed = seed; +} + +// Return pseudo-random from 0 to RAND_MAX (here 32767) +int rand_custom(void) { + randSeed = randSeed * 1103515245 + 12345; + return (randSeed >> 16) & 0x7FFF; // 15 bits +} + +// Return integer from min to max include +int randInt(int min, int max) { + return min + (rand_custom() % (max - min + 1)); +} + +// Reset +void reset(void) +{ + ballCount = BALL_NUMBER; + levelCountBreackout = 1; + score = 0; +} + +// PlayBeep +void playBeep(uint16_t tone) +{ + BK4819_PlayTone(tone, true); // 500 Hz ON + AUDIO_AudioPathOn(); + BK4819_ExitTxMute(); + SYSTEM_DelayMs(100); + BK4819_EnterTxMute(); + AUDIO_AudioPathOff(); +} + +// Draw score +void drawScore() +{ + // Clean status line + memset(gStatusLine, 0, sizeof(gStatusLine)); + + // Level + sprintf(str, "Level %02u", levelCountBreackout); + GUI_DisplaySmallest(str, 0, 1, true, true); + + // Ball + sprintf(str, "Ball %02u", (ballCount < 0) ? 0 : ballCount); + GUI_DisplaySmallest(str, 45, 1, true, true); + + // Score + sprintf(str, "Score %04u", score); + GUI_DisplaySmallest(str, 88, 1, true, true); +} + +// Init ball +void initBall() { + ball.x = 62; + ball.y = 30; + ball.w = 3; + ball.h = 3; + ball.dx = 0; + ball.dy = 1; + + UI_DrawRectangleBuffer(gFrameBuffer, ball.x, ball.y, ball.x + ball.w -1, ball.y + ball.h - 1, true); + UI_DrawLineBuffer(gFrameBuffer, ball.x - 1, ball.y + 1, ball.x + ball.w, ball.y + 1, true); +} + +// Draw ball +void drawBall() { + UI_DrawRectangleBuffer(gFrameBuffer, ball.x, ball.y, ball.x + ball.w -1, ball.y + ball.h -1, false); + UI_DrawLineBuffer(gFrameBuffer, ball.x - 1, ball.y + 1, ball.x + ball.w, ball.y + 1, false); + + ball.x += ball.dx; + ball.y += ball.dy; + + if (ball.y <= 0) // Up + { + ball.dx = map(randInt(0, 7), 0, 7, 3, -3); + ball.dy = 1; + } + else if (ball.x <= 2) // Left + { + ball.dx = abs(ball.dx); + } + else if (ball.x >= 124) // Right + { + ball.dx = -abs(ball.dx); + } + // And now Down... + if (ball.y == 47) { + if (ball.x + 1 >= racket.x && ball.x - 1 <= racket.x + racket.w) { + ball.dx = map(racket.x + racket.w - ball.x, 0, racket.w, 3, -3); + ball.dy *= -1; + isBeep = true; + tone = 400; + } + } + else if(ball.y > 49) { + ballCount--; + UI_DisplayClear(); + drawScore(); + isBeep = true; + tone = 800; + if (ballCount < 0) { + reset(); + initWall(); + drawWall(); + + isPaused = true; + UI_PrintStringSmallBold("GAME", 32, 0, 4); + UI_PrintStringSmallBold("OVER", 66, 0, 4); + } + initRacket(); + initBall(); + } + + UI_DrawRectangleBuffer(gFrameBuffer, ball.x, ball.y, ball.x + ball.w -1, ball.y + ball.h - 1, true); + UI_DrawLineBuffer(gFrameBuffer, ball.x - 1, ball.y + 1, ball.x + ball.w, ball.y + 1, true); +} + +// Init wall +void initWall() { + uint8_t offset = 6; + uint8_t i = 0; + uint8_t j = 0; + uint8_t k = 0; + + for (i = 0; i < BRICK_NUMBER; i++) { + if (i % 6 == 0) { + j = 0; + k++; + } + + brick[i].x = offset + (20 * j); + brick[i].y = -8 + 8 * k; + brick[i].w = 14; + brick[i].h = 5; + brick[i].s = randInt(0, 3); + brick[i].destroy = false; + + j++; + } +} + +// Draw wall +void drawWall() { + uint8_t i = 0; + + for (i = 0; i < BRICK_NUMBER; i++) { + if (brick[i].destroy == false) { + if ((ball.x + 1 >= brick[i].x && + ball.x - 1 <= brick[i].x + brick[i].w) && + ((ball.y + 1 >= brick[i].y && + ball.y - 1 <= brick[i].y + brick[i].h))) { + brick[i].destroy = true; + score++; + + ball.dx = map(brick[i].x + brick[i].w - ball.x, 0, brick[i].w, 2, -2); + ball.dy *= -1; + + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, true); + memcpy(gFrameBuffer[brick[i].y / 8] + brick[i].x, BITMAP_blockOn, sizeof(BITMAP_blockOn)); + ST7565_BlitLine(brick[i].y / 8); + playBeep(600); + memcpy(gFrameBuffer[brick[i].y / 8] + brick[i].x, BITMAP_blockEmpty, sizeof(BITMAP_blockEmpty)); + ST7565_BlitLine(brick[i].y / 8); + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false); + + if (score % BRICK_NUMBER == 0) { + levelCountBreackout++; + ballCount++; + initWall(); + return; + } + } + + if (brick[i].destroy == false) { + memcpy(gFrameBuffer[brick[i].y / 8] + brick[i].x, BITMAP_block[(brick[i].s + blockAnim) % 4], sizeof(BITMAP_block[(brick[i].s + blockAnim) % 4])); + } + } + } +} + +// Init racket +void initRacket() { + racket.w = 24; + racket.x = (64) - (racket.w / 2); + racket.y = 50; + racket.h = 2; + racket.p = racket.x; + + UI_DrawRectangleBuffer(gFrameBuffer, racket.x + 1, racket.y, racket.x + racket.w - 2, racket.y + racket.h, true); + UI_DrawLineBuffer(gFrameBuffer, racket.x, racket.y + 1, racket.x + racket.w - 1, racket.y + 1, true); +} + +// Draw racket +void drawRacket() { + if (racket.p != racket.x) { + UI_DrawRectangleBuffer(gFrameBuffer, racket.p + 1, racket.y, racket.p + racket.w - 2, racket.y + racket.h, false); + UI_DrawLineBuffer(gFrameBuffer, racket.p, racket.y + 1, racket.p + racket.w - 1, racket.y + 1, false); + racket.p = racket.x; + UI_DrawRectangleBuffer(gFrameBuffer, racket.x + 1, racket.y, racket.x + racket.w - 2, racket.y + racket.h, true); + UI_DrawLineBuffer(gFrameBuffer, racket.x, racket.y + 1, racket.x + racket.w - 1, racket.y + 1, true); + } +} + +// OnKeyDown +static void OnKeyDown(uint8_t key) +{ + bool wasPaused = isPaused; + + switch (key) + { + case KEY_4: + case KEY_UP: + if(!isPaused && racket.x > 0) + racket.x -= 2; + isPaused = false; + break; + case KEY_0: + case KEY_DOWN: + if(!isPaused && racket.x < 102) + racket.x += 2; + isPaused = false; + break; + case KEY_MENU: + isPaused = !isPaused; + kbd.counter = 0; + if(isPaused) + { + UI_PrintStringSmallBold("PAUSE", 0, 128, 4); + } + break; + case KEY_EXIT: + isPaused = false; + isInitialized = false; + break; + } + + if(wasPaused == true && isPaused == false) + { + // Clear the pause text + for(uint8_t i = 0; i < 8; i++) + { + UI_DrawLineBuffer(gFrameBuffer, 32, 32 + i, 96, 32 + i, false); + } + } +} + + +// Key +KEY_Code_t GetKey() +{ + KEY_Code_t btn = KEYBOARD_Poll(); + if (btn == KEY_INVALID && !GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT)) + { + btn = KEY_PTT; + } + return btn; +} + +// HandleUserInput +bool HandleUserInput() +{ + // Store previous key state + kbd.prev = kbd.current; + + // Get the current key + kbd.current = GetKey(); + + // Detect valid key press continuation (same key still pressed) + if (kbd.current != KEY_INVALID && kbd.current == kbd.prev) + { + kbd.counter = 1; + } + else + { + kbd.counter = 0; + } + + // Process the key if counter indicates it should be handled + if (kbd.counter == 1) + { + OnKeyDown(kbd.current); + + // Special handling for MENU key + if(kbd.current == KEY_MENU) + { + kbd.counter = 0; + SYSTEM_DelayMs(250); + } + } + + return true; +} + +// Tick +static void Tick() +{ + HandleUserInput(); + HandleUserInput(); +} + +// APP_RunBreakout +void APP_RunBreakout(void) { + static uint8_t swap = 0; + + // Init seed + srand_custom(BK4819_ReadRegister(BK4819_REG_67) & 0x01FF * gBatteryVoltageAverage * gEeprom.VfoInfo[0].pRX->Frequency); + + // Init led + BK4819_ToggleGpioOut(BK4819_GPIO6_PIN2_GREEN, false); + + // Init game + UI_DisplayClear(); + reset(); + initWall(); + initRacket(); + initBall(); + memset(gStatusLine, 0, sizeof(gStatusLine)); + isInitialized = true; + + while(isInitialized) + { + Tick(); + if(!isPaused) + { + if(swap == 0) + { + blockAnim = (blockAnim + 1) % 4; + } + + swap = (swap + 1) % 4; + + drawScore(); + drawWall(); + drawRacket(); + drawBall(); + + if(isBeep) + { + playBeep(tone); + isBeep = false; + } + else + { + SYSTEM_DelayMs(40 - MIN(levelCountBreackout - 1, 20)); // Add more fun... + } + } + + ST7565_BlitStatusLine(); // Blank status line + ST7565_BlitFullScreen(); + } +} \ No newline at end of file diff --git a/app/breakout.h b/app/breakout.h new file mode 100644 index 00000000..eb2c546a --- /dev/null +++ b/app/breakout.h @@ -0,0 +1,80 @@ +/* Copyright 2025 Armel F4HWN + * https://github.com/armel + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../bitmaps.h" +#include "../board.h" +#include "../bsp/dp32g030/gpio.h" +#include "../driver/bk4819-regs.h" +#include "../driver/bk4819.h" +#include "../driver/gpio.h" +#include "../driver/keyboard.h" +#include "../driver/st7565.h" +#include "../driver/system.h" +#include "../driver/systick.h" +#include "../external/printf/printf.h" +#include "../font.h" +#include "../helper/battery.h" +#include "../misc.h" +#include "../radio.h" +#include "../settings.h" +#include "../ui/helper.h" +#include "../audio.h" +#include +#include +#include +#include + +#define BRICK_NUMBER 18 +#define BALL_NUMBER 5 + +typedef struct { + uint8_t x; // x + uint8_t y; // y + uint8_t w; // width + uint8_t h; // height + uint8_t s; // style + bool destroy; // active, if true, check this button, else bypass +} Brick; + +typedef struct { + int8_t x; // x + uint8_t y; // y + uint8_t w; // width + uint8_t h; // height + uint8_t p; // previous x +} Racket; + +typedef struct { + int16_t x; // x + int8_t y; // y + uint8_t w; // width + uint8_t h; // height + int8_t dx; // move x + int8_t dy; // move y +} Ball; + +typedef struct KeyboardState +{ + KEY_Code_t current; + KEY_Code_t prev; + uint8_t counter; +} KeyboardState; + +void initWall(void); +void drawWall(void); +void initRacket(void); +void drawRacket(void); +void APP_RunBreakout(void); diff --git a/app/main.c b/app/main.c index 2de8caa6..8c5619ca 100644 --- a/app/main.c +++ b/app/main.c @@ -31,6 +31,10 @@ #include "app/spectrum.h" #endif +#ifdef ENABLE_FEAT_F4HWN_GAME +#include "app/breakout.h" +#endif + #include "audio.h" #include "board.h" #include "driver/bk4819.h" @@ -582,6 +586,13 @@ static void MAIN_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld) ACTION_BackLight(); return; } + #ifdef ENABLE_FEAT_F4HWN_GAME + else if(Key == 7) + { + APP_RunBreakout(); + return; + } + #endif processFKeyFunction(Key, true); } @@ -660,7 +671,7 @@ static void MAIN_Key_MENU(bool bKeyPressed, bool bKeyHeld) // Exclude work with list 1, 2, 3 or all list if(gScanStateDir != SCAN_OFF) { - if(FUNCTION_IsRx()) + if(FUNCTION_IsRx() || gScanPauseDelayIn_10ms > 9) { gMR_ChannelExclude[gTxVfo->CHANNEL_SAVE] = true; diff --git a/app/spectrum.c b/app/spectrum.c index 418b73c3..891e8387 100644 --- a/app/spectrum.c +++ b/app/spectrum.c @@ -103,11 +103,18 @@ RegisterSpec registerSpecs[] = { {}, {"LNAs", BK4819_REG_13, 8, 0b11, 1}, {"LNA", BK4819_REG_13, 5, 0b111, 1}, - {"PGA", BK4819_REG_13, 0, 0b111, 1}, - {"IF", BK4819_REG_3D, 0, 0xFFFF, 0x2aaa}, + {"VGA", BK4819_REG_13, 0, 0b111, 1}, + {"BPF", BK4819_REG_3D, 0, 0xFFFF, 0x2aaa}, // {"MIX", 0x13, 3, 0b11, 1}, // TODO: hidden }; +#ifdef ENABLE_FEAT_F4HWN_SPECTRUM +const int8_t LNAsOptions[] = {-19, -16, -11, 0}; +const int8_t LNAOptions[] = {-24, -19, -14, -9, -6, -4, -2, 0}; +const int8_t VGAOptions[] = {-33, -27, -21, -15, -9, -6, -3, 0}; +const char *BPFOptions[] = {"8.46", "7.25", "6.35", "5.64", "5.08", "4.62", "4.23"}; +#endif + uint16_t statuslineUpdateTimer = 0; #ifdef ENABLE_FEAT_F4HWN_SPECTRUM @@ -1387,7 +1394,27 @@ static void RenderStill() sprintf(String, "%s", registerSpecs[idx].name); GUI_DisplaySmallest(String, offset + 2, row * 8 + 2, false, menuState != idx); + +#ifdef ENABLE_FEAT_F4HWN_SPECTRUM + if(idx == 1) + { + sprintf(String, "%ddB", LNAsOptions[GetRegMenuValue(idx)]); + } + else if(idx == 2) + { + sprintf(String, "%ddB", LNAOptions[GetRegMenuValue(idx)]); + } + else if(idx == 3) + { + sprintf(String, "%ddB", VGAOptions[GetRegMenuValue(idx)]); + } + else if(idx == 4) + { + sprintf(String, "%skHz", BPFOptions[(GetRegMenuValue(idx) / 0x2aaa)]); + } +#else sprintf(String, "%u", GetRegMenuValue(idx)); +#endif GUI_DisplaySmallest(String, offset + 2, (row + 1) * 8 + 1, false, menuState != idx); } diff --git a/archive/f4hwn.bandscope.packed.v4.1.bin b/archive/f4hwn.bandscope.packed.v4.1.bin new file mode 100644 index 00000000..dea7b4bb Binary files /dev/null and b/archive/f4hwn.bandscope.packed.v4.1.bin differ diff --git a/archive/f4hwn.bandscope.packed.v4.1.sa818.bin b/archive/f4hwn.bandscope.packed.v4.1.sa818.bin new file mode 100644 index 00000000..dea7b4bb Binary files /dev/null and b/archive/f4hwn.bandscope.packed.v4.1.sa818.bin differ diff --git a/archive/f4hwn.basic.packed.v4.1.bin b/archive/f4hwn.basic.packed.v4.1.bin new file mode 100644 index 00000000..7b493b65 Binary files /dev/null and b/archive/f4hwn.basic.packed.v4.1.bin differ diff --git a/archive/f4hwn.basic.packed.v4.1.sa818.bin b/archive/f4hwn.basic.packed.v4.1.sa818.bin new file mode 100644 index 00000000..7b493b65 Binary files /dev/null and b/archive/f4hwn.basic.packed.v4.1.sa818.bin differ diff --git a/archive/f4hwn.broadcast.packed.v4.1.bin b/archive/f4hwn.broadcast.packed.v4.1.bin new file mode 100644 index 00000000..a46facc1 Binary files /dev/null and b/archive/f4hwn.broadcast.packed.v4.1.bin differ diff --git a/archive/f4hwn.broadcast.packed.v4.1.sa818.bin b/archive/f4hwn.broadcast.packed.v4.1.sa818.bin new file mode 100644 index 00000000..a46facc1 Binary files /dev/null and b/archive/f4hwn.broadcast.packed.v4.1.sa818.bin differ diff --git a/archive/f4hwn.game.packed.v4.1.bin b/archive/f4hwn.game.packed.v4.1.bin new file mode 100644 index 00000000..95acdf8e Binary files /dev/null and b/archive/f4hwn.game.packed.v4.1.bin differ diff --git a/archive/f4hwn.game.packed.v4.1.sa818.bin b/archive/f4hwn.game.packed.v4.1.sa818.bin new file mode 100644 index 00000000..95acdf8e Binary files /dev/null and b/archive/f4hwn.game.packed.v4.1.sa818.bin differ diff --git a/archive/f4hwn.rescueops.packed.v4.1.bin b/archive/f4hwn.rescueops.packed.v4.1.bin new file mode 100644 index 00000000..7e7685d0 Binary files /dev/null and b/archive/f4hwn.rescueops.packed.v4.1.bin differ diff --git a/archive/f4hwn.rescueops.packed.v4.1.sa818.bin b/archive/f4hwn.rescueops.packed.v4.1.sa818.bin new file mode 100644 index 00000000..7e7685d0 Binary files /dev/null and b/archive/f4hwn.rescueops.packed.v4.1.sa818.bin differ diff --git a/compile-with-docker.sh b/compile-with-docker.sh index 0b6ebbf0..9135af11 100755 --- a/compile-with-docker.sh +++ b/compile-with-docker.sh @@ -1,26 +1,38 @@ #!/bin/sh -#export DOCKER_DEFAULT_PLATFORM=linux/amd64 -#export DOCKER_NETWORK="--network=host" + IMAGE_NAME="uvk5" -rm "${PWD}/compiled-firmware/*" -echo "Building docker image $IMAGE_NAME" -if ! docker build -t $DOCKER_NETWORK $IMAGE_NAME . -then - echo "Failed to build docker image" - exit 1 +FIRMWARE_DIR="${PWD}/compiled-firmware" + +# Create firmware output directory if it doesn't exist +mkdir -p "$FIRMWARE_DIR" + +# Clean previously compiled firmware files +rm -f "$FIRMWARE_DIR"/* + +# Build image only if it doesn't already exist +if ! docker image inspect "$IMAGE_NAME" >/dev/null 2>&1; then + echo "Docker image '$IMAGE_NAME' not found, building..." + if ! docker build --build-arg BUILDPLATFORM=linux/amd64 -t "$IMAGE_NAME" .; then + echo "❌ Failed to build docker image" + exit 1 + fi fi +# ------------------ BUILD VARIANTS ------------------ + custom() { - echo "Custom compilation..." - docker run --rm -v "${PWD}/compiled-firmware/:/app/compiled-firmware" $IMAGE_NAME /bin/bash -c "rm ./compiled-firmware/*; cd /app && make -s \ + echo "🔧 Custom compilation..." + docker run --rm -v "$FIRMWARE_DIR:/app/compiled-firmware" "$IMAGE_NAME" /bin/bash -c "\ + rm -f ./compiled-firmware/* && cd /app && make -s \ EDITION_STRING=Custom \ TARGET=f4hwn.custom \ && cp f4hwn.custom* compiled-firmware/" } standard() { - echo "Standard compilation..." - docker run --rm -v "${PWD}/compiled-firmware:/app/compiled-firmware" $IMAGE_NAME /bin/bash -c "rm ./compiled-firmware/*; cd /app && make -s \ + echo "📦 Standard compilation..." + docker run --rm -v "$FIRMWARE_DIR:/app/compiled-firmware" "$IMAGE_NAME" /bin/bash -c "\ + rm -f ./compiled-firmware/* && cd /app && make -s \ ENABLE_SPECTRUM=0 \ ENABLE_FMRADIO=0 \ ENABLE_AIRCOPY=0 \ @@ -31,11 +43,14 @@ standard() { } bandscope() { - echo "Bandscope compilation..." - docker run --rm -v "${PWD}/compiled-firmware/:/app/compiled-firmware" $IMAGE_NAME /bin/bash -c "rm ./compiled-firmware/*; cd /app && make -s \ + echo "📺 Bandscope compilation..." + docker run --rm -v "$FIRMWARE_DIR:/app/compiled-firmware" "$IMAGE_NAME" /bin/bash -c "\ + rm -f ./compiled-firmware/* && cd /app && make -s \ ENABLE_SPECTRUM=1 \ ENABLE_FMRADIO=0 \ + ENABLE_VOX=0 \ ENABLE_AIRCOPY=1 \ + ENABLE_FEAT_F4HWN_GAME=0 \ ENABLE_FEAT_F4HWN_PMR=1 \ ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \ ENABLE_NOAA=0 \ @@ -46,32 +61,37 @@ bandscope() { } broadcast() { - echo "Broadcast compilation..." - docker run --rm -v "${PWD}/compiled-firmware:/app/compiled-firmware" $IMAGE_NAME /bin/bash -c "cd /app && make -s \ + echo "📻 Broadcast compilation..." + docker run --rm -v "$FIRMWARE_DIR:/app/compiled-firmware" "$IMAGE_NAME" /bin/bash -c "\ + cd /app && make -s \ ENABLE_SPECTRUM=0 \ ENABLE_FMRADIO=1 \ + ENABLE_VOX=1 \ ENABLE_AIRCOPY=1 \ + ENABLE_FEAT_F4HWN_GAME=0 \ ENABLE_FEAT_F4HWN_PMR=1 \ ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \ ENABLE_NOAA=0 \ - EDITION_STRING=Broadcast \ ENABLE_FEAT_F4HWN_RESCUE_OPS=0 \ + EDITION_STRING=Broadcast \ TARGET=f4hwn.broadcast \ && cp f4hwn.broadcast* compiled-firmware/" } basic() { - echo "Basic compilation..." - docker run --rm -v "${PWD}/compiled-firmware:/app/compiled-firmware" $IMAGE_NAME /bin/bash -c "cd /app && make -s \ + echo "☘️ Basic compilation..." + docker run --rm -v "$FIRMWARE_DIR:/app/compiled-firmware" "$IMAGE_NAME" /bin/bash -c "\ + cd /app && make -s \ ENABLE_SPECTRUM=1 \ ENABLE_FMRADIO=1 \ ENABLE_VOX=0 \ ENABLE_AIRCOPY=0 \ - ENABLE_AUDIO_BAR=0 \ + ENABLE_FEAT_F4HWN_GAME=0 \ ENABLE_FEAT_F4HWN_SPECTRUM=0 \ ENABLE_FEAT_F4HWN_PMR=1 \ ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \ ENABLE_NOAA=0 \ + ENABLE_AUDIO_BAR=0 \ ENABLE_FEAT_F4HWN_RESUME_STATE=0 \ ENABLE_FEAT_F4HWN_CHARGING_C=0 \ ENABLE_FEAT_F4HWN_INV=1 \ @@ -84,11 +104,14 @@ basic() { } rescueops() { - echo "RescueOps compilation..." - docker run --rm -v "${PWD}/compiled-firmware:/app/compiled-firmware" $IMAGE_NAME /bin/bash -c "cd /app && make -s \ + echo "🚨 RescueOps compilation..." + docker run --rm -v "$FIRMWARE_DIR:/app/compiled-firmware" "$IMAGE_NAME" /bin/bash -c "\ + cd /app && make -s \ ENABLE_SPECTRUM=0 \ ENABLE_FMRADIO=0 \ + ENABLE_VOX=1 \ ENABLE_AIRCOPY=1 \ + ENABLE_FEAT_F4HWN_GAME=0 \ ENABLE_FEAT_F4HWN_PMR=1 \ ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \ ENABLE_NOAA=1 \ @@ -98,33 +121,43 @@ rescueops() { && cp f4hwn.rescueops* compiled-firmware/" } +game() { + echo "🎮 Game compilation..." + docker run --rm -v "$FIRMWARE_DIR:/app/compiled-firmware" "$IMAGE_NAME" /bin/bash -c "\ + cd /app && make -s \ + ENABLE_SPECTRUM=0 \ + ENABLE_FMRADIO=1 \ + ENABLE_VOX=0 \ + ENABLE_AIRCOPY=1 \ + ENABLE_FEAT_F4HWN_GAME=1 \ + ENABLE_FEAT_F4HWN_PMR=1 \ + ENABLE_FEAT_F4HWN_GMRS_FRS_MURS=1 \ + ENABLE_NOAA=0 \ + ENABLE_FEAT_F4HWN_RESCUE_OPS=0 \ + EDITION_STRING=Game \ + TARGET=f4hwn.game \ + && cp f4hwn.game* compiled-firmware/" +} + +# ------------------ MENU ------------------ + case "$1" in - custom) - custom - ;; - standard) - standard - ;; - bandscope) - bandscope - ;; - broadcast) - broadcast - ;; - basic) - basic - ;; - rescueops) - rescueops - ;; + custom) custom ;; + standard) standard ;; + bandscope) bandscope ;; + broadcast) broadcast ;; + basic) basic ;; + rescueops) rescueops ;; + game) game ;; all) bandscope broadcast - rescueops basic + rescueops + game ;; *) - echo "Usage: $0 {custom|bandscope|broadcast|basic|standard|all}" + echo "Usage: $0 {custom|standard|bandscope|broadcast|basic|rescueops|game|all} [--rebuild]" exit 1 ;; -esac \ No newline at end of file +esac diff --git a/settings.c b/settings.c index c79fcb32..6741b136 100644 --- a/settings.c +++ b/settings.c @@ -1004,7 +1004,7 @@ State[1] = 0 #ifdef ENABLE_BYP_RAW_DEMODULATORS | (1 << 2) #endif -#ifdef ENABLE_BLMIN_TMP_OFF +#ifdef ENABLE_FEAT_F4HWN_GAME | (1 << 3) #endif #ifdef ENABLE_AM_FIX diff --git a/ui/menu.c b/ui/menu.c index 0c5d0796..a4ad6d9d 100644 --- a/ui/menu.c +++ b/ui/menu.c @@ -1227,6 +1227,8 @@ void UI_DisplayMenu(void) #if defined(ENABLE_SPECTRUM) && defined(ENABLE_FMRADIO) UI_PrintStringSmallNormal("Basic", 54, 127, 6); + #elif defined(ENABLE_FEAT_F4HWN_GAME) + UI_PrintStringSmallNormal("Game", 54, 127, 6); #elif defined(ENABLE_SPECTRUM) UI_PrintStringSmallNormal("Bandscope", 54, 127, 6); #elif defined(ENABLE_FMRADIO)