Add support for supplying exiting CGFX and CWAV files to makebanner, add command for creating SMDH files.
This commit is contained in:
parent
8b9b2f070e
commit
d7e741c9b3
8 changed files with 277 additions and 69 deletions
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "cbmd.h"
|
||||
#include "cwav.h"
|
||||
#include "smdh.h"
|
||||
#include "lz11.h"
|
||||
#include "util.h"
|
||||
|
||||
|
|
|
@ -1,11 +1,53 @@
|
|||
#include "cwav.h"
|
||||
|
||||
#include "../wav.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef struct {
|
||||
char magic[4] = {'C', 'W', 'A', 'V'};
|
||||
u16 endianess = 0xFEFF;
|
||||
u16 structLength = 0x40;
|
||||
u32 unknown0 = 0;
|
||||
u32 fileSize;
|
||||
u32 numChunks = 2;
|
||||
u32 infoChunkFlags = 0x7000;
|
||||
u32 infoChunkOffset;
|
||||
u32 infoChunkLength;
|
||||
u32 dataChunkFlags = 0x7000;
|
||||
u32 dataChunkOffset;
|
||||
u32 dataChunkLength;
|
||||
u8 reserved[0x14] = {0};
|
||||
} Header;
|
||||
|
||||
typedef struct {
|
||||
char magic[4] = {'I', 'N', 'F', 'O'};
|
||||
u32 length = 0xC0;
|
||||
u32 type;
|
||||
u32 sampleRate;
|
||||
u32 unknown1 = 0;
|
||||
u32 totalSamples;
|
||||
u32 unknown2 = 0;
|
||||
u32 totalChannels;
|
||||
} InfoHeader;
|
||||
|
||||
typedef struct {
|
||||
char magic[4] = {'D', 'A', 'T', 'A'};
|
||||
u32 length;
|
||||
} DataHeader;
|
||||
|
||||
typedef struct {
|
||||
u32 flags = 0x7100;
|
||||
u32 offset;
|
||||
} ChannelDataPointer;
|
||||
|
||||
typedef struct {
|
||||
u32 flags = 0x1F00;
|
||||
u32 offset;
|
||||
u32 unknown3 = 0;
|
||||
u32 unknown4 = 0;
|
||||
u32 padding = 0;
|
||||
} ChannelData;
|
||||
|
||||
u8* build_cwav(WAV wav, u32* size) {
|
||||
Header header;
|
||||
u32 offset = sizeof(Header);
|
||||
|
|
|
@ -4,51 +4,6 @@
|
|||
#include "../types.h"
|
||||
#include "../wav.h"
|
||||
|
||||
typedef struct {
|
||||
char magic[4] = {'C', 'W', 'A', 'V'};
|
||||
u16 endianess = 0xFEFF;
|
||||
u16 structLength = 0x40;
|
||||
u32 unknown0 = 0;
|
||||
u32 fileSize;
|
||||
u32 numChunks = 2;
|
||||
u32 infoChunkFlags = 0x7000;
|
||||
u32 infoChunkOffset;
|
||||
u32 infoChunkLength;
|
||||
u32 dataChunkFlags = 0x7000;
|
||||
u32 dataChunkOffset;
|
||||
u32 dataChunkLength;
|
||||
u8 reserved[0x14] = {0};
|
||||
} Header;
|
||||
|
||||
typedef struct {
|
||||
char magic[4] = {'I', 'N', 'F', 'O'};
|
||||
u32 length = 0xC0;
|
||||
u32 type;
|
||||
u32 sampleRate;
|
||||
u32 unknown1 = 0;
|
||||
u32 totalSamples;
|
||||
u32 unknown2 = 0;
|
||||
u32 totalChannels;
|
||||
} InfoHeader;
|
||||
|
||||
typedef struct {
|
||||
char magic[4] = {'D', 'A', 'T', 'A'};
|
||||
u32 length;
|
||||
} DataHeader;
|
||||
|
||||
typedef struct {
|
||||
u32 flags = 0x7100;
|
||||
u32 offset;
|
||||
} ChannelDataPointer;
|
||||
|
||||
typedef struct {
|
||||
u32 flags = 0x1F00;
|
||||
u32 offset;
|
||||
u32 unknown3 = 0;
|
||||
u32 unknown4 = 0;
|
||||
u32 padding = 0;
|
||||
} ChannelData;
|
||||
|
||||
u8* build_cwav(WAV wav, u32* size);
|
||||
|
||||
#endif
|
96
source/3ds/smdh.h
Normal file
96
source/3ds/smdh.h
Normal file
|
@ -0,0 +1,96 @@
|
|||
#ifndef __SMDH_H__
|
||||
#define __SMDH_H__
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
typedef enum {
|
||||
JAPANESE,
|
||||
ENGLISH,
|
||||
FRENCH,
|
||||
GERMAN,
|
||||
ITALIAN,
|
||||
SPANISH,
|
||||
SIMPLIFIED_CHINESE,
|
||||
KOREAN,
|
||||
DUTCH,
|
||||
PORTUGESE,
|
||||
RUSSIAN,
|
||||
TRADITIONAL_CHINESE
|
||||
} SMDHTitleLanguage;
|
||||
|
||||
typedef struct {
|
||||
u16 shortDescription[0x40] = {0};
|
||||
u16 longDescription[0x80] = {0};
|
||||
u16 publisher[0x40] = {0};
|
||||
} SMDHTitle;
|
||||
|
||||
typedef struct {
|
||||
// TODO: values...
|
||||
u8 cero = 0;
|
||||
u8 esrb = 0;
|
||||
u8 reserved0 = 0;
|
||||
u8 usk = 0;
|
||||
u8 pegiGen = 0;
|
||||
u8 reserved1 = 0;
|
||||
u8 pegiPrt = 0;
|
||||
u8 pegiBbfc = 0;
|
||||
u8 cob = 0;
|
||||
u8 grb = 0;
|
||||
u8 cgsrr = 0;
|
||||
u8 reserved2 = 0;
|
||||
u8 reserved3 = 0;
|
||||
u8 reserved4 = 0;
|
||||
u8 reserved5 = 0;
|
||||
u8 reserved6 = 0;
|
||||
} SMDHGameRatings;
|
||||
|
||||
typedef struct _region_lock {
|
||||
_region_lock() : japan(true), northAmerica(true), europe(true), australia(true), china(true), korea(true), taiwan(true) {}
|
||||
|
||||
bool japan : 1;
|
||||
bool northAmerica : 1;
|
||||
bool europe : 1;
|
||||
bool australia : 1;
|
||||
bool china : 1;
|
||||
bool korea : 1;
|
||||
bool taiwan : 1;
|
||||
} SMDHRegionLock;
|
||||
|
||||
typedef struct _flags {
|
||||
_flags() : visible(true), autoBoot(false), allow3d(true), requireEula(false), autoSaveOnExit(false), useExtendedBanner(false), ratingRequired(false), useSaveData(false), recordUsage(true), disableSaveBackups(false) {}
|
||||
|
||||
bool visible : 1;
|
||||
bool autoBoot : 1;
|
||||
bool allow3d : 1;
|
||||
bool requireEula : 1;
|
||||
bool autoSaveOnExit : 1;
|
||||
bool useExtendedBanner : 1;
|
||||
bool ratingRequired : 1;
|
||||
bool useSaveData : 1;
|
||||
bool recordUsage : 1;
|
||||
bool disableSaveBackups : 1;
|
||||
} SMDHFlags;
|
||||
|
||||
typedef struct {
|
||||
SMDHGameRatings gameRatings;
|
||||
SMDHRegionLock regionLock;
|
||||
u8 matchMakerId[0xC] = {0};
|
||||
SMDHFlags flags;
|
||||
u16 eulaVersion = 0;
|
||||
u16 reserved1 = 0;
|
||||
u32 optimalBannerFrame = 0;
|
||||
u32 streetpassId = 0;
|
||||
} SMDHSettings;
|
||||
|
||||
typedef struct {
|
||||
char magic[4] = {'S', 'M', 'D', 'H'};
|
||||
u16 version = 0;
|
||||
u16 reserved0 = 0;
|
||||
SMDHTitle titles[0x10];
|
||||
SMDHSettings settings;
|
||||
u64 reserved2 = 0;
|
||||
u8 smallIcon[0x480] = {0};
|
||||
u8 largeIcon[0x1200] = {0};
|
||||
} SMDH;
|
||||
|
||||
#endif
|
|
@ -7,7 +7,14 @@ u8 TILE_ORDER[64] = { 0, 1, 8, 9, 2, 3, 10, 11, 16, 17, 24, 25, 18, 19, 26
|
|||
32, 33, 40, 41, 34, 35, 42, 43, 48, 49, 56, 57, 50, 51, 58, 59,
|
||||
36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63 };
|
||||
|
||||
u8* image_to_tiles(const char* image, u32 width, u32 height, u32* size) {
|
||||
u16 rgba_to_rgb565(u8 r, u8 g, u8 b, u8 a) {
|
||||
r = (u8) (1.0f * r * a / 255.0f) >> 3;
|
||||
g = (u8) (1.0f * g * a / 255.0f) >> 2;
|
||||
b = (u8) (1.0f * b * a / 255.0f) >> 3;
|
||||
return (r << 11) | (g << 5) | b;
|
||||
}
|
||||
|
||||
u16* image_to_tiles(const char* image, u32 width, u32 height, u32* size) {
|
||||
unsigned char* img;
|
||||
unsigned int imgWidth, imgHeight;
|
||||
if(lodepng_decode32_file(&img, &imgWidth, &imgHeight, image)) {
|
||||
|
@ -28,7 +35,7 @@ u8* image_to_tiles(const char* image, u32 width, u32 height, u32* size) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
u8* converted = (u8*) malloc(width * height * 2);
|
||||
u16* converted = (u16*) malloc(width * height * sizeof(u16));
|
||||
u32 n = 0;
|
||||
for(int y = 0; y < height; y += 8) {
|
||||
for(int x = 0; x < width; x += 8) {
|
||||
|
@ -37,15 +44,26 @@ u8* image_to_tiles(const char* image, u32 width, u32 height, u32* size) {
|
|||
u32 yy = (u32) (TILE_ORDER[k] >> 3);
|
||||
|
||||
u8* pixel = img + (((y + yy) * width + (x + xx)) * 4);
|
||||
converted[n++] = ((pixel[2] >> 4) << 4) | (pixel[3] >> 4);
|
||||
converted[n++] = ((pixel[0] >> 4) << 4) | (pixel[1] >> 4);
|
||||
converted[n++] = rgba_to_rgb565(pixel[0], pixel[1], pixel[2], pixel[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(size != NULL) {
|
||||
*size = width * height * 2;
|
||||
*size = width * height * (u32) sizeof(u16);
|
||||
}
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
void utf8_to_utf16(u16* dst, const char* src, size_t max_len) {
|
||||
u8* u8dst = (u8*) dst;
|
||||
size_t n = 0;
|
||||
while(src[n]) {
|
||||
u8dst[n * 2] = (u8) src[n];
|
||||
u8dst[n * 2 + 1] = 0;
|
||||
if(n++ >= max_len) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "../types.h"
|
||||
|
||||
u8* image_to_tiles(const char* image, u32 width, u32 height, u32* size);
|
||||
u16 rgba_to_rgb565(u8 r, u8 g, u8 b, u8 a);
|
||||
u16* image_to_tiles(const char* image, u32 width, u32 height, u32* size);
|
||||
void utf8_to_utf16(u16* dst, const char* src, size_t max_len);
|
||||
|
||||
#endif
|
|
@ -39,9 +39,18 @@ void cmd_print_usage(const char* executedFrom) {
|
|||
void cmd_print_info(const char* command) {
|
||||
if(strcmp(command, "makebanner") == 0) {
|
||||
printf("makebanner - Creates a .bnr file.\n");
|
||||
printf(" -i/--image: PNG file to use as the banner image.\n");
|
||||
printf(" -a/--audio: WAV file to use as the banner's tune.\n");
|
||||
printf(" -i/--image: PNG file to use as the banner's image. Interchangeable with -ci.\n");
|
||||
printf(" -a/--audio: WAV file to use as the banner's tune. Interchangeable with -ca.\n");
|
||||
printf(" -ci/--cgfximage: CGFX file to use as the banner's image. Interchangeable with -i.\n");
|
||||
printf(" -ca/--cwavaudio: CWAV file to use as the banner's tune. Interchangeable with -a.\n");
|
||||
printf(" -o/--output: File to output the created banner to.\n");
|
||||
} else if(strcmp(command, "makesmdh") == 0) {
|
||||
printf("makesmdh - Creates a .smdh/.icn file.\n");
|
||||
printf(" -s/--shortdescription: Short description of the application.\n");
|
||||
printf(" -l/--longdescription: Long description of the application.\n");
|
||||
printf(" -p/--publisher: Publisher of the application.\n");
|
||||
printf(" -i/--icon: PNG file to use as an icon.\n");
|
||||
printf(" -o/--output: File to output the created SMDH/ICN to.\n");
|
||||
} else if(strcmp(command, "makecwav") == 0) {
|
||||
printf("makecwav - Creates a CWAV file from a WAV.\n");
|
||||
printf(" -i/--input: WAV file to convert.\n");
|
||||
|
@ -56,6 +65,7 @@ void cmd_print_info(const char* command) {
|
|||
void cmd_print_commands() {
|
||||
printf("Available commands:\n");
|
||||
cmd_print_info("makebanner");
|
||||
cmd_print_info("makesmdh");
|
||||
cmd_print_info("makecwav");
|
||||
cmd_print_info("lz11");
|
||||
}
|
||||
|
|
102
source/main.cpp
102
source/main.cpp
|
@ -3,6 +3,7 @@
|
|||
#include "data.h"
|
||||
#include "types.h"
|
||||
#include "wav.h"
|
||||
#include "3ds/smdh.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <malloc.h>
|
||||
|
@ -10,7 +11,7 @@
|
|||
|
||||
u8* convert_to_cgfx(const char* image, u32 width, u32 height, u32* size) {
|
||||
u32 convertedSize = 0;
|
||||
u8* converted = image_to_tiles(image, width, height, &convertedSize);
|
||||
u16* converted = image_to_tiles(image, width, height, &convertedSize);
|
||||
if(converted == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -37,18 +38,50 @@ u8* convert_to_cwav(const char* file, u32* size) {
|
|||
return cwav;
|
||||
}
|
||||
|
||||
int make_banner(const char* image, const char* audio, const char* output) {
|
||||
int make_banner(const char* image, const char* audio, char* cgfxFile, char* cwavFile, const char* output) {
|
||||
u32 cgfxSize = 0;
|
||||
u8* cgfx = convert_to_cgfx(image, 256, 128, &cgfxSize);
|
||||
u8* cgfx = NULL;
|
||||
if(cgfxFile != NULL) {
|
||||
FILE* fd = fopen(cgfxFile, "r");
|
||||
if(!fd) {
|
||||
printf("ERROR: Could not open CGFX file: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
fseek(fd, 0, SEEK_END);
|
||||
cgfxSize = (u32) ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
cgfx = (u8*) malloc(cgfxSize);
|
||||
fread(cgfx, 1, cgfxSize, fd);
|
||||
fclose(fd);
|
||||
} else {
|
||||
cgfx = convert_to_cgfx(image, 256, 128, &cgfxSize);
|
||||
if(!cgfx) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
u32 cwavSize = 0;
|
||||
u8* cwav = convert_to_cwav(audio, &cwavSize);
|
||||
u8* cwav = NULL;
|
||||
if(cwavFile != NULL) {
|
||||
FILE* fd = fopen(cwavFile, "r");
|
||||
if(!fd) {
|
||||
printf("ERROR: Could not open CWAV file: %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
fseek(fd, 0, SEEK_END);
|
||||
cwavSize = (u32) ftell(fd);
|
||||
fseek(fd, 0, SEEK_SET);
|
||||
|
||||
cwav = (u8*) malloc(cwavSize);
|
||||
fread(cwav, 1, cwavSize, fd);
|
||||
fclose(fd);
|
||||
} else {
|
||||
cwav = convert_to_cwav(audio, &cwavSize);
|
||||
if(!cwav) {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
CBMD cbmd;
|
||||
cbmd.cgfxs[CGFX_COMMON] = cgfx;
|
||||
|
@ -76,6 +109,43 @@ int make_banner(const char* image, const char* audio, const char* output) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int make_smdh(char* shortDescription, char* longDescription, char* publisher, char* icon, char* output) {
|
||||
u16* icon48 = image_to_tiles(icon, 48, 48, NULL);
|
||||
if(icon48 == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
u16 icon24[24 * 24];
|
||||
for(int y = 0; y < 24; y++) {
|
||||
for(int x = 0; x < 24; x++) {
|
||||
icon24[y * 24 + x] = icon48[y * 48 + x];
|
||||
}
|
||||
}
|
||||
|
||||
SMDH smdh;
|
||||
for(int i = 0; i < 0x10; i++) {
|
||||
utf8_to_utf16(smdh.titles[i].shortDescription, shortDescription, 0x40);
|
||||
utf8_to_utf16(smdh.titles[i].longDescription, longDescription, 0x80);
|
||||
utf8_to_utf16(smdh.titles[i].publisher, publisher, 0x40);
|
||||
}
|
||||
|
||||
memcpy(smdh.largeIcon, icon48, 0x1200);
|
||||
memcpy(smdh.smallIcon, icon24, 0x480);
|
||||
free(icon48);
|
||||
|
||||
FILE* fd = fopen(output, "wb");
|
||||
if(!fd) {
|
||||
printf("ERROR: Could not open output file: %s\n", strerror(errno));
|
||||
return 2;
|
||||
}
|
||||
|
||||
fwrite(&smdh, 1, sizeof(SMDH), fd);
|
||||
fclose(fd);
|
||||
|
||||
printf("Created SMDH \"%s\".\n", output);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int make_cwav(char* input, char* output) {
|
||||
u32 cwavSize = 0;
|
||||
u8* cwav = convert_to_cwav(input, &cwavSize);
|
||||
|
@ -143,15 +213,29 @@ int main(int argc, char* argv[]) {
|
|||
char* command = argv[1];
|
||||
std::map<char*, char*, compare_strings> args = cmd_get_args(argc, argv);
|
||||
if(strcmp(command, "makebanner") == 0) {
|
||||
char* banner = cmd_find_arg(args, "i", "image");
|
||||
char* audio = cmd_find_arg(args, "a", "audio");
|
||||
char* output = cmd_find_arg(args, "o", "output");
|
||||
if(!banner || !audio || !output) {
|
||||
char *banner = cmd_find_arg(args, "i", "image");
|
||||
char *audio = cmd_find_arg(args, "a", "audio");
|
||||
char *cgfxFile = cmd_find_arg(args, "ci", "cgfximage");
|
||||
char *cwavFile = cmd_find_arg(args, "ca", "cwavaudio");
|
||||
char *output = cmd_find_arg(args, "o", "output");
|
||||
if(!(banner || cgfxFile) || !(audio || cwavFile) || !output) {
|
||||
cmd_missing_args(command);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return make_banner(banner, audio, output);
|
||||
return make_banner(banner, audio, cgfxFile, cwavFile, output);
|
||||
} else if(strcmp(command, "makesmdh") == 0) {
|
||||
char* shortDescription = cmd_find_arg(args, "s", "shortdescription");
|
||||
char* longDescription = cmd_find_arg(args, "l", "longdescription");
|
||||
char* publisher = cmd_find_arg(args, "p", "publisher");
|
||||
char* icon = cmd_find_arg(args, "i", "icon");
|
||||
char* output = cmd_find_arg(args, "o", "output");
|
||||
if(!shortDescription || !longDescription || !publisher || !icon || !output) {
|
||||
cmd_missing_args(command);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return make_smdh(shortDescription, longDescription, publisher, icon, output);
|
||||
} else if(strcmp(command, "makecwav") == 0) {
|
||||
char* input = cmd_find_arg(args, "i", "input");
|
||||
char* output = cmd_find_arg(args, "o", "output");
|
||||
|
|
Loading…
Reference in a new issue