13 Commits

Author SHA1 Message Date
b337dfe3bc Add Automated release action
All checks were successful
Build bannertool / build-linux (push) Successful in 1m44s
Build bannertool / release (push) Successful in 8s
/ build-linux (push) Successful in 1m41s
/ build-macos (push) Successful in 8s
/ release (push) Successful in 6s
2025-11-02 18:03:21 +01:00
571b674c3d fix typo
All checks were successful
Build bannertool / build-linux (push) Successful in 1m33s
Build bannertool / release (push) Successful in 6s
2025-11-02 17:45:02 +01:00
56ba11f79f Changes
Some checks failed
Build bannertool / build-linux (push) Failing after 2m18s
Build bannertool / release (push) Has been skipped
Update README
Add windows 32 bit support
Remove macos building (cause dont want my macbook run all the day)
2025-11-02 17:41:40 +01:00
6f91e0f0d0 Update CmakeScript and CI
All checks were successful
Build bannertool / build-macos (push) Successful in 7s
Build bannertool / build-linux (push) Successful in 1m27s
Build bannertool / release (push) Successful in 6s
2025-10-31 21:47:06 +01:00
7d7d8b6291 Add CMake file 2024-03-08 16:52:49 +01:00
ba50d482c0 patch for msvc compile error 2024-03-08 16:40:05 +01:00
be0443e468 # Removed #
- submodule
- Makefile
- Dockerfile
- Updated .gitignore for cmake support
2024-03-08 16:39:04 +01:00
Steveice10
16d8c5a0ce Fix submodule URL. 2022-11-11 17:27:11 -08:00
Steveice10
aeadb2285f Merge pull request #20 from ShockwaveNN/feature/dockerfile
Add Dockerfile and build instruction
2020-02-22 10:15:44 -08:00
Pavel Lobashov
6168450fd7 Add Dockerfile 2020-02-22 15:12:56 +03:00
Steveice10
7e1f433b1d Implement proper UTF-16 conversion. 2019-05-03 22:53:56 -07:00
Steveice10
703d33110f Fix strncpy warning. 2019-03-02 17:17:35 -08:00
Steveice10
82b49e9102 Split and fix SMDH match maker IDs. 2019-03-02 17:09:06 -08:00
13 changed files with 289 additions and 80 deletions

View File

@@ -0,0 +1,94 @@
name: Build bannertool
on:
push:
branches:
- '*'
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Tools
run: |
sudo apt update
sudo apt install -y build-essential make cmake zip \
gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
gcc-x86-64-linux-gnu g++-x86-64-linux-gnu \
gcc-i686-linux-gnu g++-i686-linux-gnu \
gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 \
gcc-mingw-w64-i686 g++-mingw-w64-i686
- name: Download Cross-Build
run: git clone https://dev.npid7.de/tobid7/cross-build
- name: Build Targets
run: |
mkdir -p build_artifacts
declare -A TARGETS=(
["linux-i386"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-i386.cmake"
["linux-x86_64"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-x86_64.cmake"
["linux-armhf"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-armhf.cmake"
["linux-aarch64"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-aarch64.cmake"
["windows-x86_64"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/windows-mingw-x86_64.cmake"
["windows-x86"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/windows-mingw-i386.cmake"
)
for name in "${!TARGETS[@]}"; do
cmake ${TARGETS[$name]} -DCMAKE_INSTALL_PREFIX=bin-$name -B build-$name .
cmake --build build-$name --parallel
cmake --install build-$name
zip -r build_artifacts/${name}.zip bin-$name
done
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: linux-builds
path: build_artifacts/*.zip
# build-macos:
# runs-on: macos-latest
# steps:
# - name: Checkout
# uses: actions/checkout@v4
# - name: Build
# run: |
# mkdir -p build_artifacts
# cmake -DCMAKE_INSTALL_PREFIX=bin-macos-universal -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -B build .
# cmake --build build
# cmake --install build
# mkdir -p artifacts/macos-universal
# cp -r bin-macos-universal/* artifacts/macos-universal/
# zip -r build_artifacts/macos-universal.zip bin-macos-universal
# - name: Upload Artifact
# uses: actions/upload-artifact@v3
# with:
# name: macos-build
# path: build_artifacts/*.zip
release:
runs-on: ubuntu-latest
needs: [build-linux] #, build-macos]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download Artifacts
uses: actions/download-artifact@v3
with:
path: release_files
- name: Get Date
run: echo "TAG_NAME=v$(date -u +'%Y%m%d-%H%M%S')" >> $GITHUB_ENV
- name: Upload to Gitea
uses: akkuman/gitea-release-action@v1
with:
token: ${{ secrets.TOKEN }}
prerelease: true
name: "Automated Build ${{ env.TAG_NAME }}"
tag_name: ${{ env.TAG_NAME }}
body: |
**Automated prerelease build**
Commit: ${{ github.sha }}
Triggered by: ${{ github.actor }}
files: release_files/**/*.zip

View File

@@ -0,0 +1,98 @@
on:
push:
tags:
- 'v*.*.*'
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Tools
run: |
sudo apt update
sudo apt install -y build-essential make cmake zip \
gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
gcc-x86-64-linux-gnu g++-x86-64-linux-gnu \
gcc-i686-linux-gnu g++-i686-linux-gnu \
gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 \
gcc-mingw-w64-i686 g++-mingw-w64-i686
- name: Download Cross-Build
run: git clone https://dev.npid7.de/tobid7/cross-build
- name: Build Targets
run: |
mkdir -p build_artifacts
declare -A TARGETS=(
["linux-i386"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-i386.cmake"
["linux-x86_64"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-x86_64.cmake"
["linux-armhf"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-armhf.cmake"
["linux-aarch64"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/linux-aarch64.cmake"
["windows-x86_64"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/windows-mingw-x86_64.cmake"
["windows-x86"]="-DCMAKE_TOOLCHAIN_FILE=cross-build/cmake/windows-mingw-i386.cmake"
)
for name in "${!TARGETS[@]}"; do
cmake ${TARGETS[$name]} -DCMAKE_INSTALL_PREFIX=bin-$name -B build-$name .
cmake --build build-$name --parallel
cmake --install build-$name
zip -r build_artifacts/${name}.zip bin-$name
done
- name: Upload Artifacts
uses: actions/upload-artifact@v3
with:
name: linux-builds
path: build_artifacts/*.zip
build-macos:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: |
mkdir -p build_artifacts
cmake -DCMAKE_INSTALL_PREFIX=bin-macos-universal -DCMAKE_OSX_ARCHITECTURES="x86_64;arm64" -B build .
cmake --build build
cmake --install build
mkdir -p artifacts/macos-universal
cp -r bin-macos-universal/* artifacts/macos-universal/
zip -r build_artifacts/macos-universal.zip bin-macos-universal
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: macos-build
path: build_artifacts/*.zip
release:
runs-on: ubuntu-latest
needs: [build-linux, build-macos]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Download Artifacts
uses: actions/download-artifact@v3
with:
path: release_files
- name: Upload to Gitea
uses: akkuman/gitea-release-action@v1
with:
token: ${{ secrets.TOKEN }}
prerelease: false
name: "Release ${{ github.ref_name }}"
tag_name: ${{ github.ref_name }}
body: |
**Bannertool Release ${{ github.ref_name }}**
Commit: ${{ github.sha }}
Triggered by: ${{ github.actor }}
Supported Systems:
- Windows x64
- Windows x86 (32 Bit)
- Linux x86_64
- Linux x86 (32 Bit)
- Linux armhf (32 Bit)
- Linux aarch64 (arm64)
files: release_files/**/*.zip

6
.gitignore vendored
View File

@@ -1,6 +1,2 @@
.idea
cmake-build-debug
CMakeLists.txt
build
output
cmake-build-debug

4
.gitmodules vendored
View File

@@ -1,4 +0,0 @@
[submodule "buildtools"]
path = buildtools
url = git://github.com/Steveice10/buildtools
shallow = true

42
CMakeLists.txt Normal file
View File

@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.22)
project(bannertool)
# add definition for alloca
# otherwise stb_vorbis fails
if(MSVC)
add_compile_definitions(alloca=_alloca)
endif()
# define version
set(VERSION_MAJOR "1")
set(VERSION_MINOR "2")
set(VERSION_MICRO "0")
# Add executable
add_executable(bannertool
source/cmd.cpp
source/main.cpp
source/pc/stb_image.c
source/pc/stb_vorbis.c
source/pc/wav.cpp
source/3ds/cbmd.cpp
source/3ds/cwav.cpp
source/3ds/lz11.cpp
)
# set include dirs
target_include_directories(bannertool PUBLIC
source
source/3ds
source/pc
)
# define VERSION
target_compile_definitions(bannertool PUBLIC
-DVERSION_MAJOR=${VERSION_MAJOR}
-DVERSION_MINOR=${VERSION_MINOR}
-DVERSION_MICRO=${VERSION_MICRO}
)
install(TARGETS bannertool DESTINATION bin)

View File

@@ -1,3 +1,4 @@
Copyright (C) 2024-2025 tobid7
Copyright (C) 2015-2017 Steveice10
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,39 +0,0 @@
# TARGET #
TARGET := NATIVE
LIBRARY := 0
ifeq ($(TARGET),$(filter $(TARGET),3DS WIIU))
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPro")
endif
endif
# COMMON CONFIGURATION #
NAME := bannertool
BUILD_DIR := build
OUTPUT_DIR := output
INCLUDE_DIRS := include
SOURCE_DIRS := source
EXTRA_OUTPUT_FILES :=
LIBRARY_DIRS :=
LIBRARIES :=
BUILD_FLAGS := -Wno-unused-function
BUILD_FLAGS_CC :=
BUILD_FLAGS_CXX :=
RUN_FLAGS :=
VERSION_PARTS := $(subst ., ,$(shell git describe --tags --abbrev=0))
VERSION_MAJOR := $(word 1, $(VERSION_PARTS))
VERSION_MINOR := $(word 2, $(VERSION_PARTS))
VERSION_MICRO := $(word 3, $(VERSION_PARTS))
# INTERNAL #
include buildtools/make_base

View File

@@ -1,3 +1,15 @@
# bannertool
A tool for creating 3DS banners.
## Building
```bash
cmake -B build .
cmake --build build
```
## Credits
- Stevice10: Creating bannertool
- tobid7: Providing binaries for more systems

Submodule buildtools deleted from 4524b3a324

View File

@@ -113,11 +113,14 @@ void* lz11_compress(u32* size, void* input, u32 inputSize) {
if(compressedLength % 4 != 0) {
u32 padLength = 4 - (compressedLength % 4);
u8 pad[padLength];
// Small patch to prevent msvc error
u8* pad = new u8[padLength];
memset(pad, 0, (size_t) padLength);
ss.write((char*) pad, padLength);
compressedLength += padLength;
// Need to delete as not dynamic
delete[] pad;
}
void* buf = malloc((size_t) compressedLength);

View File

@@ -76,7 +76,8 @@ typedef struct {
typedef struct {
u8 gameRatings[SMDH_NUM_RATING_SLOTS];
u32 regionLock;
u8 matchMakerId[0xC];
u32 matchMakerId;
u64 matchMakerBitId;
u32 flags;
u16 eulaVersion;
u16 reserved1;
@@ -95,4 +96,4 @@ typedef struct {
u16 largeIcon[SMDH_LARGE_ICON_SIZE * SMDH_LARGE_ICON_SIZE];
} SMDH;
#endif
#endif

View File

@@ -13,6 +13,8 @@
#include <string.h>
#include <algorithm>
#include <codecvt>
#include <locale>
#include <map>
#include <vector>
@@ -21,18 +23,20 @@ typedef enum {
RGBA4444
} PixelFormat;
static void utf8_to_utf16(u16* dst, const char* src, size_t maxLen) {
static void utf8_to_utf16(u16* dst, const std::string& src, size_t maxLen) {
if(maxLen == 0) {
return;
}
size_t n = 0;
while(src[n]) {
dst[n] = (u16) src[n];
if(n++ >= maxLen) {
return;
}
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> converter;
std::u16string str16 = converter.from_bytes(src.data());
size_t copyLen = str16.length() * sizeof(char16_t);
if(copyLen > maxLen) {
copyLen = maxLen;
}
memcpy(dst, str16.data(), copyLen);
}
static void* read_file(u32* size, const std::string& file) {
@@ -504,6 +508,7 @@ static void cmd_print_info(const std::string& command) {
printf(" -f/--flags: Optional. Flags to apply to the SMDH file.\n");
printf(" Valid flags: visible, autoboot, allow3d, requireeula, autosave, extendedbanner, ratingrequired, savedata, recordusage, nosavebackups, new3ds.\n");
printf(" -mmid/--matchmakerid: Optional. Match maker ID of the SMDH.\n");
printf(" -mmbid/--matchmakerbitid: Optional. Match maker BIT ID of the SMDH.\n");
printf(" -ev/--eulaversion: Optional. Version of the EULA required to be accepted before launching.\n");
printf(" -obf/--optimalbannerframe: Optional. Optimal frame of the accompanying banner.\n");
printf(" -spid/--streetpassid: Optional. Streetpass ID of the SMDH.\n");
@@ -642,9 +647,9 @@ int cmd_process_command(int argc, char* argv[]) {
longTitleFound = longTitleFound || !currLongTitle.empty();
publisherFound = publisherFound || !currPublisher.empty();
utf8_to_utf16(smdh.titles[i].shortTitle, currShortTitle.c_str(), sizeof(smdh.titles[i].shortTitle) / 2);
utf8_to_utf16(smdh.titles[i].longTitle, currLongTitle.c_str(), sizeof(smdh.titles[i].longTitle) / 2);
utf8_to_utf16(smdh.titles[i].publisher, currPublisher.c_str(), sizeof(smdh.titles[i].publisher) / 2);
utf8_to_utf16(smdh.titles[i].shortTitle, currShortTitle, sizeof(smdh.titles[i].shortTitle) / 2);
utf8_to_utf16(smdh.titles[i].longTitle, currLongTitle, sizeof(smdh.titles[i].longTitle) / 2);
utf8_to_utf16(smdh.titles[i].publisher, currPublisher, sizeof(smdh.titles[i].publisher) / 2);
}
if(!shortTitleFound || !longTitleFound || !publisherFound) {
@@ -709,12 +714,12 @@ int cmd_process_command(int argc, char* argv[]) {
}
}
u64 matchMakerId = (u64) atoll(cmd_find_arg(args, "mmid", "matchmakerid", "0").c_str());
memcpy(smdh.settings.matchMakerId, &matchMakerId, 0xC);
smdh.settings.matchMakerId = (u32) atoi(cmd_find_arg(args, "mmid", "matchmakerid", "0").c_str());
smdh.settings.matchMakerBitId = (u64) atoll(cmd_find_arg(args, "mmbid", "matchmakerbitid", "0").c_str());
smdh.settings.eulaVersion = (u16) atoi(cmd_find_arg(args, "ev", "eulaversion", "0").c_str());
smdh.settings.optimalBannerFrame = (u32) atoll(cmd_find_arg(args, "obf", "optimalbannerframe", "0").c_str());
smdh.settings.streetpassId = (u32) atoll(cmd_find_arg(args, "spid", "streetpassid", "0").c_str());
smdh.settings.optimalBannerFrame = (u32) atoi(cmd_find_arg(args, "obf", "optimalbannerframe", "0").c_str());
smdh.settings.streetpassId = (u32) atoi(cmd_find_arg(args, "spid", "streetpassid", "0").c_str());
smdh.settings.gameRatings[SMDH_RATING_CERO] = (u8) atoi(cmd_find_arg(args, "cer", "cero", "0").c_str());
smdh.settings.gameRatings[SMDH_RATING_ESRB] = (u8) atoi(cmd_find_arg(args, "er", "esrb", "0").c_str());
@@ -731,8 +736,8 @@ int cmd_process_command(int argc, char* argv[]) {
const std::string input = cmd_find_arg(args, "i", "input", "");
const std::string output = cmd_find_arg(args, "o", "output", "");
std::string loop = cmd_find_arg(args, "l", "loop", "false");
u32 loopStartFrame = (u32) atol(cmd_find_arg(args, "s", "loopstartframe", "0").c_str());
u32 loopEndFrame = (u32) atol(cmd_find_arg(args, "e", "loopendframe", "0").c_str());
u32 loopStartFrame = (u32) atoi(cmd_find_arg(args, "s", "loopstartframe", "0").c_str());
u32 loopEndFrame = (u32) atoi(cmd_find_arg(args, "e", "loopendframe", "0").c_str());
if(input.empty() || output.empty()) {
cmd_missing_args(command);
return -1;

View File

@@ -1,6 +1,7 @@
#include "wav.h"
#include <sstream>
#include <string>
#include <errno.h>
#include <stdlib.h>
@@ -45,7 +46,7 @@ WAV* wav_read(FILE* fd) {
return NULL;
}
char error[128] = {'\0'};
std::string error = "";
bool riff = false;
bool fmt = false;
bool data = false;
@@ -54,23 +55,23 @@ WAV* wav_read(FILE* fd) {
memset(&context, 0, sizeof(context));
context.fd = fd;
while(strlen(error) == 0 && (!riff || !fmt || !data) && wav_next_chunk(&context)) {
while(error.empty() && (!riff || !fmt || !data) && wav_next_chunk(&context)) {
if(memcmp(context.currChunk.chunkId, "RIFF", 4) == 0) {
riff = true;
char format[4];
if(wav_read_chunk_data(&context, format, sizeof(format))) {
if(memcmp(format, "WAVE", sizeof(format)) != 0) {
strncpy(error, "ERROR: RIFF file is not of WAVE format", sizeof(error));
error = "ERROR: RIFF file is not of WAVE format";
}
} else {
strncpy(error, "ERROR: Failed to read RIFF chunk data", sizeof(error));
error = "ERROR: Failed to read RIFF chunk data";
}
} else if(memcmp(context.currChunk.chunkId, "fmt ", 4) == 0) {
fmt = true;
if(!wav_read_chunk_data(&context, &wav->format, sizeof(WavFormatChunk))) {
strncpy(error, "ERROR: Failed to read fmt chunk data", sizeof(error));
error = "ERROR: Failed to read fmt chunk data";
}
} else if(memcmp(context.currChunk.chunkId, "data", 4) == 0) {
data = true;
@@ -79,15 +80,15 @@ WAV* wav_read(FILE* fd) {
wav->data.data = (u8*) malloc(wav->data.size);
if(wav->data.data != NULL) {
if(!wav_read_chunk_data(&context, wav->data.data, wav->data.size)) {
strncpy(error, "ERROR: Failed to read data chunk data", sizeof(error));
error = "ERROR: Failed to read data chunk data";
}
} else {
strncpy(error, "ERROR: Could not allocate memory for WAV samples", sizeof(error));
error = "ERROR: Could not allocate memory for WAV samples";
}
}
}
if(strlen(error) == 0 && (!riff || !fmt || !data)) {
if(error.empty() && (!riff || !fmt || !data)) {
std::stringstream stream;
stream << "ERROR: Missing one or more WAV chunks: ";
@@ -111,16 +112,16 @@ WAV* wav_read(FILE* fd) {
stream << "data";
}
strncpy(error, stream.str().c_str(), sizeof(error));
error = stream.str();
}
if(strlen(error) > 0) {
if(!error.empty()) {
wav_free(wav);
if(errno != 0) {
perror(error);
perror(error.c_str());
} else {
printf("%s.\n", error);
printf("%s.\n", error.c_str());
}
return NULL;