Organize code structure, generate CBMD files without premade headers.
This commit is contained in:
parent
2e75e2c9cc
commit
239547097f
13 changed files with 245 additions and 103 deletions
|
@ -3,5 +3,5 @@ project(bannertool)
|
||||||
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||||
|
|
||||||
set(SOURCE_FILES source/main.cpp source/lz11.cpp source/lodepng.cpp)
|
set(SOURCE_FILES source/main.cpp source/3ds/cbmd.cpp source/3ds/lz11.cpp source/3ds/util.cpp source/lodepng/lodepng.cpp)
|
||||||
add_executable(bannertool ${SOURCE_FILES})
|
add_executable(bannertool ${SOURCE_FILES})
|
9
source/3ds/3ds.h
Normal file
9
source/3ds/3ds.h
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
#ifndef __3DS_H__
|
||||||
|
#define __3DS_H__
|
||||||
|
|
||||||
|
#include "cbmd.h"
|
||||||
|
#include "lz11.h"
|
||||||
|
#include "types.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#endif
|
81
source/3ds/cbmd.cpp
Normal file
81
source/3ds/cbmd.cpp
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
#include "cbmd.h"
|
||||||
|
|
||||||
|
#include "lz11.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char magic[4] = {'C', 'B', 'M', 'D'};
|
||||||
|
u32 zero = 0;
|
||||||
|
u32 cgfxOffsets[14] = {0};
|
||||||
|
u8 padding[0x44] = {0};
|
||||||
|
u32 cwavOffset = 0;
|
||||||
|
} CBMDHeader;
|
||||||
|
|
||||||
|
u8* build_cbmd_data(CBMD cbmd, u32* size, bool bnr) {
|
||||||
|
u32 headerSize = sizeof(CBMDHeader);
|
||||||
|
CBMDHeader header;
|
||||||
|
|
||||||
|
u8* compressedCGFXs[14] = {0};
|
||||||
|
u32 compressedCGFXSizes[14] = {0};
|
||||||
|
|
||||||
|
u32 offset = headerSize;
|
||||||
|
for(int i = 0; i < 14; i++) {
|
||||||
|
if(cbmd.cgfxs[i] != NULL) {
|
||||||
|
compressedCGFXs[i] = lz11_compress(cbmd.cgfxs[i], cbmd.cgfxSizes[i], &compressedCGFXSizes[i]);
|
||||||
|
header.cgfxOffsets[i] = offset;
|
||||||
|
offset += compressedCGFXSizes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 pad = 0;
|
||||||
|
if(bnr) {
|
||||||
|
pad = 16 - (offset % 16);
|
||||||
|
offset += pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cbmd.cwav != NULL) {
|
||||||
|
header.cwavOffset = offset;
|
||||||
|
offset += cbmd.cwavSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* output = (u8*) malloc(offset);
|
||||||
|
u32 pos = 0;
|
||||||
|
|
||||||
|
memcpy(output + pos, &header, headerSize);
|
||||||
|
pos += headerSize;
|
||||||
|
|
||||||
|
for(int i = 0; i < 14; i++) {
|
||||||
|
if(compressedCGFXs[i] != NULL) {
|
||||||
|
memcpy(output + pos, compressedCGFXs[i], compressedCGFXSizes[i]);
|
||||||
|
free(compressedCGFXs[i]);
|
||||||
|
compressedCGFXs[i] = NULL;
|
||||||
|
pos += compressedCGFXSizes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bnr) {
|
||||||
|
memset(output + pos, 0, pad);
|
||||||
|
pos += pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(cbmd.cwav != NULL) {
|
||||||
|
memcpy(output + pos, cbmd.cwav, cbmd.cwavSize);
|
||||||
|
pos += cbmd.cwavSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(size != NULL) {
|
||||||
|
*size = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* build_cbmd(CBMD cbmd, u32* size) {
|
||||||
|
return build_cbmd_data(cbmd, size, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* build_bnr(CBMD cbmd, u32* size) {
|
||||||
|
return build_cbmd_data(cbmd, size, true);
|
||||||
|
}
|
33
source/3ds/cbmd.h
Normal file
33
source/3ds/cbmd.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef __CBMD_H__
|
||||||
|
#define __CBMD_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CGFX_COMMON,
|
||||||
|
CGFX_EUR_ENGLISH,
|
||||||
|
CGFX_EUR_FRENCH,
|
||||||
|
CGFX_EUR_GERMAN,
|
||||||
|
CGFX_EUR_ITALIAN,
|
||||||
|
CGFX_EUR_SPANISH,
|
||||||
|
CGFX_EUR_DUTCH,
|
||||||
|
CGFX_EUR_PORTUGESE,
|
||||||
|
CGFX_EUR_RUSSIAN,
|
||||||
|
CGFX_JPN_JAPANESE,
|
||||||
|
CGFX_USA_ENGLISH,
|
||||||
|
CGFX_USA_FRENCH,
|
||||||
|
CGFX_USA_SPANISH,
|
||||||
|
CGFX_USA_PORTUGESE
|
||||||
|
} CBMDCGFX;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u8* cgfxs[14] = {NULL};
|
||||||
|
u32 cgfxSizes[14] = {0};
|
||||||
|
u8* cwav = NULL;
|
||||||
|
u32 cwavSize = 0;
|
||||||
|
} CBMD;
|
||||||
|
|
||||||
|
u8* build_cbmd(CBMD cbmd, u32* size);
|
||||||
|
u8* build_bnr(CBMD cbmd, u32* size);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,16 +1,15 @@
|
||||||
#include "lz11.h"
|
#include "lz11.h"
|
||||||
|
|
||||||
#include <time.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#define MIN(a,b) (((a)<(b))?(a):(b))
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
||||||
|
|
||||||
// Ported from: https://github.com/svn2github/3DS-Explorer/blob/master/3DSExplorer/DSDecmp/Formats/Nitro/LZ11.cs
|
// Ported from: https://github.com/svn2github/3DS-Explorer/blob/master/3DSExplorer/DSDecmp/Formats/Nitro/LZ11.cs
|
||||||
|
|
||||||
int get_occurence_length(u8* newPtr, int newLength, u8* oldPtr, int oldLength, int* disp) {
|
int lz11_get_occurence_length(u8* newPtr, int newLength, u8* oldPtr, int oldLength, int* disp) {
|
||||||
if(disp != NULL) {
|
if(disp != NULL) {
|
||||||
*disp = 0;
|
*disp = 0;
|
||||||
}
|
}
|
||||||
|
@ -46,7 +45,7 @@ int get_occurence_length(u8* newPtr, int newLength, u8* oldPtr, int oldLength, i
|
||||||
return maxLength;
|
return maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* compress_lz11(u8* input, u32 inputSize, u32* size) {
|
u8* lz11_compress(u8* input, u32 inputSize, u32* size) {
|
||||||
if (inputSize > 0xFFFFFF) {
|
if (inputSize > 0xFFFFFF) {
|
||||||
printf("ERROR: LZ11 input is too large.");
|
printf("ERROR: LZ11 input is too large.");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -74,7 +73,7 @@ u8* compress_lz11(u8* input, u32 inputSize, u32* size) {
|
||||||
|
|
||||||
int disp = 0;
|
int disp = 0;
|
||||||
int oldLength = MIN(readBytes, 0x1000);
|
int oldLength = MIN(readBytes, 0x1000);
|
||||||
int length = get_occurence_length(input + readBytes, MIN(inputSize - readBytes, 0x10110), input + readBytes - oldLength, oldLength, &disp);
|
int length = lz11_get_occurence_length(input + readBytes, MIN(inputSize - readBytes, 0x10110), input + readBytes - oldLength, oldLength, &disp);
|
||||||
if(length < 3) {
|
if(length < 3) {
|
||||||
outbuffer[bufferlength++] = *(input + (readBytes++));
|
outbuffer[bufferlength++] = *(input + (readBytes++));
|
||||||
} else {
|
} else {
|
||||||
|
@ -110,11 +109,13 @@ u8* compress_lz11(u8* input, u32 inputSize, u32* size) {
|
||||||
compressedLength += bufferlength;
|
compressedLength += bufferlength;
|
||||||
}
|
}
|
||||||
|
|
||||||
int padLength = 4 - (compressedLength % 4);
|
if(compressedLength % 4 != 0) {
|
||||||
if(padLength > 0) {
|
int padLength = 4 - (compressedLength % 4);
|
||||||
u8 pad[padLength];
|
u8 pad[padLength];
|
||||||
memset(pad, 0, (size_t) padLength);
|
memset(pad, 0, (size_t) padLength);
|
||||||
|
|
||||||
ss.write((char*) pad, padLength);
|
ss.write((char*) pad, padLength);
|
||||||
|
compressedLength += padLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8* buf = (u8*) malloc((size_t) compressedLength);
|
u8* buf = (u8*) malloc((size_t) compressedLength);
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
u8* compress_lz11(u8* input, u32 inputSize, u32* size);
|
u8* lz11_compress(u8* input, u32 inputSize, u32* size);
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,7 +1,9 @@
|
||||||
#ifndef __TYPES_H__
|
#ifndef __TYPES_H__
|
||||||
#define __TYPES_H__
|
#define __TYPES_H__
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
typedef uint8_t u8;
|
typedef uint8_t u8;
|
||||||
typedef uint16_t u16;
|
typedef uint16_t u16;
|
51
source/3ds/util.cpp
Normal file
51
source/3ds/util.cpp
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include "../lodepng/lodepng.h"
|
||||||
|
|
||||||
|
u8 TILE_ORDER[64] = { 0, 1, 8, 9, 2, 3, 10, 11, 16, 17, 24, 25, 18, 19, 26, 27,
|
||||||
|
4, 5, 12, 13, 6, 7, 14, 15, 20, 21, 28, 29, 22, 23, 30, 31,
|
||||||
|
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) {
|
||||||
|
unsigned char* img;
|
||||||
|
unsigned int imgWidth, imgHeight;
|
||||||
|
if(lodepng_decode32_file(&img, &imgWidth, &imgHeight, image)) {
|
||||||
|
printf("ERROR: Could not load png file.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(width == 0) {
|
||||||
|
width = imgWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(height == 0) {
|
||||||
|
height = imgHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(imgWidth != width || imgHeight != height) {
|
||||||
|
printf("ERROR: Image must be exactly %d x %d in size.\n", width, height);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* converted = (u8*) malloc(width * height * 2);
|
||||||
|
u32 n = 0;
|
||||||
|
for(int y = 0; y < height; y += 8) {
|
||||||
|
for(int x = 0; x < width; x += 8) {
|
||||||
|
for(int k = 0; k < 8 * 8; k++) {
|
||||||
|
u32 xx = (u32) (TILE_ORDER[k] & 0x7);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(size != NULL) {
|
||||||
|
*size = width * height * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return converted;
|
||||||
|
}
|
8
source/3ds/util.h
Normal file
8
source/3ds/util.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __UTIL_H__
|
||||||
|
#define __UTIL_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
u8* image_to_tiles(const char* image, u32 width, u32 height, u32* size);
|
||||||
|
|
||||||
|
#endif
|
File diff suppressed because one or more lines are too long
132
source/main.cpp
132
source/main.cpp
|
@ -1,73 +1,22 @@
|
||||||
|
#include "data.h"
|
||||||
|
#include "3ds/3ds.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "data.h"
|
u8* convert_to_cgfx(const char* image, u32 width, u32 height, u32* size) {
|
||||||
#include "lz11.h"
|
u32 convertedSize = 0;
|
||||||
#include "types.h"
|
u8* converted = image_to_tiles(image, width, height, &convertedSize);
|
||||||
#include "lodepng.h"
|
if(converted == NULL) {
|
||||||
|
|
||||||
u8* convert_to_cgfx(const char* file, u32 width, u32 height, u32* size) {
|
|
||||||
unsigned char* img;
|
|
||||||
unsigned int imgWidth, imgHeight;
|
|
||||||
if(lodepng_decode32_file(&img, &imgWidth, &imgHeight, file)) {
|
|
||||||
printf("ERROR: Could not load png file.\n");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(imgWidth != width || imgHeight != height) {
|
u8* ret = (u8*) malloc(BANNER_CGFX_HEADER_LENGTH + convertedSize);
|
||||||
printf("ERROR: Image must be exactly %d x %d in size.\n", width, height);
|
memcpy(ret, BANNER_CGFX_HEADER, BANNER_CGFX_HEADER_LENGTH);
|
||||||
return NULL;
|
memcpy(ret + BANNER_CGFX_HEADER_LENGTH, converted, convertedSize);
|
||||||
}
|
|
||||||
|
|
||||||
u8 converted[width * height * 2];
|
*size = BANNER_CGFX_HEADER_LENGTH + convertedSize;
|
||||||
u32 n = 0;
|
|
||||||
for(int y = 0; y < height; y += 8) {
|
|
||||||
for(int x = 0; x < width; x += 8) {
|
|
||||||
for(int k = 0; k < 8 * 8; k++) {
|
|
||||||
u32 xx = (u32) (TILE_ORDER[k] & 0x7);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* ret = (u8*) malloc(CGFX_HEADER_LENGTH + (width * height * 2));
|
|
||||||
memcpy(ret, CGFX_HEADER, CGFX_HEADER_LENGTH);
|
|
||||||
memcpy(ret + CGFX_HEADER_LENGTH, converted, width * height * 2);
|
|
||||||
|
|
||||||
*size = CGFX_HEADER_LENGTH + (width * height * 2);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* make_banner(const char* file, u32* size) {
|
|
||||||
u32 originalSize = 0;
|
|
||||||
u8* cgfx = convert_to_cgfx(file, 256, 128, &originalSize);
|
|
||||||
if(!cgfx) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 compressedSize = 0;
|
|
||||||
u8* compressed = compress_lz11(cgfx, originalSize, &compressedSize);
|
|
||||||
free(cgfx);
|
|
||||||
if(!compressed) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 pad = 16 - ((BANNER_CBMD_HEADER_LENGTH + 4 + compressedSize) % 16);
|
|
||||||
u32 totalLength = BANNER_CBMD_HEADER_LENGTH + 4 + compressedSize + pad;
|
|
||||||
|
|
||||||
u8* ret = (u8*) malloc(totalLength);
|
|
||||||
memcpy(ret, BANNER_CBMD_HEADER, BANNER_CBMD_HEADER_LENGTH);
|
|
||||||
memcpy(ret + BANNER_CBMD_HEADER_LENGTH, &totalLength, 4);
|
|
||||||
memcpy(ret + BANNER_CBMD_HEADER_LENGTH + 4, compressed, compressedSize);
|
|
||||||
memset(ret + BANNER_CBMD_HEADER_LENGTH + 4 + compressedSize, 0, pad);
|
|
||||||
|
|
||||||
free(compressed);
|
|
||||||
*size = (u32) totalLength;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,31 +40,46 @@ u8* make_audio(const char* file, u32* size) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int make_banner(const char* image, const char* audio, const char* output) {
|
||||||
|
u32 cgfxSize = 0;
|
||||||
|
u8* cgfx = convert_to_cgfx(image, 256, 128, &cgfxSize);
|
||||||
|
if(!cgfx) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 cwavSize = 0;
|
||||||
|
u8* cwav = make_audio(audio, &cwavSize);
|
||||||
|
if(!audio) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
CBMD cbmd;
|
||||||
|
cbmd.cgfxs[CGFX_COMMON] = cgfx;
|
||||||
|
cbmd.cgfxSizes[CGFX_COMMON] = cgfxSize;
|
||||||
|
cbmd.cwav = cwav;
|
||||||
|
cbmd.cwavSize = cwavSize;
|
||||||
|
|
||||||
|
u32 bnrSize = 0;
|
||||||
|
u8* bnr = build_bnr(cbmd, &bnrSize);
|
||||||
|
free(cgfx);
|
||||||
|
free(cwav);
|
||||||
|
|
||||||
|
FILE* fd = fopen(output, "wb");
|
||||||
|
if(!fd) {
|
||||||
|
printf("ERROR: Could not open output file.\n");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(bnr, 1, bnrSize, fd);
|
||||||
|
fclose(fd);
|
||||||
|
free(bnr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if(argc != 4) {
|
if(argc != 4) {
|
||||||
printf("Usage: %s <banner png> <audio bcwav> <output file>", argv[0]);
|
printf("Usage: %s <banner png> <audio bcwav> <output file>", argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 bannerSize = 0;
|
return make_banner(argv[1], argv[2], argv[3]);
|
||||||
u8* banner = make_banner(argv[1], &bannerSize);
|
|
||||||
if(!banner) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 audioSize = 0;
|
|
||||||
u8* audio = make_audio(argv[2], &audioSize);
|
|
||||||
if(!audio) {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* fd = fopen(argv[3], "wb");
|
|
||||||
if(!fd) {
|
|
||||||
printf("ERROR: Could not write output file.\n");
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
fwrite(banner, 1, bannerSize, fd);
|
|
||||||
fwrite(audio, 1, audioSize, fd);
|
|
||||||
fclose(fd);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue