Organize code structure, generate CBMD files without premade headers.

This commit is contained in:
Steven Smith 2015-01-23 18:37:10 -08:00
parent 2e75e2c9cc
commit 239547097f
13 changed files with 245 additions and 103 deletions

View file

@ -3,5 +3,5 @@ project(bannertool)
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})

9
source/3ds/3ds.h Normal file
View 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
View 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
View 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

View file

@ -1,16 +1,15 @@
#include "lz11.h"
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <sstream>
#include <string.h>
#define MIN(a,b) (((a)<(b))?(a):(b))
// 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) {
*disp = 0;
}
@ -46,7 +45,7 @@ int get_occurence_length(u8* newPtr, int newLength, u8* oldPtr, int oldLength, i
return maxLength;
}
u8* compress_lz11(u8* input, u32 inputSize, u32* size) {
u8* lz11_compress(u8* input, u32 inputSize, u32* size) {
if (inputSize > 0xFFFFFF) {
printf("ERROR: LZ11 input is too large.");
return NULL;
@ -74,7 +73,7 @@ u8* compress_lz11(u8* input, u32 inputSize, u32* size) {
int disp = 0;
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) {
outbuffer[bufferlength++] = *(input + (readBytes++));
} else {
@ -110,11 +109,13 @@ u8* compress_lz11(u8* input, u32 inputSize, u32* size) {
compressedLength += bufferlength;
}
int padLength = 4 - (compressedLength % 4);
if(padLength > 0) {
if(compressedLength % 4 != 0) {
int padLength = 4 - (compressedLength % 4);
u8 pad[padLength];
memset(pad, 0, (size_t) padLength);
ss.write((char*) pad, padLength);
compressedLength += padLength;
}
u8* buf = (u8*) malloc((size_t) compressedLength);

View file

@ -3,6 +3,6 @@
#include "types.h"
u8* compress_lz11(u8* input, u32 inputSize, u32* size);
u8* lz11_compress(u8* input, u32 inputSize, u32* size);
#endif

View file

@ -1,7 +1,9 @@
#ifndef __TYPES_H__
#define __TYPES_H__
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
typedef uint8_t u8;
typedef uint16_t u16;

51
source/3ds/util.cpp Normal file
View 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
View 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

View file

@ -1,73 +1,22 @@
#include "data.h"
#include "3ds/3ds.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "data.h"
#include "lz11.h"
#include "types.h"
#include "lodepng.h"
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");
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);
if(converted == NULL) {
return NULL;
}
if(imgWidth != width || imgHeight != height) {
printf("ERROR: Image must be exactly %d x %d in size.\n", width, height);
return NULL;
}
u8* ret = (u8*) malloc(BANNER_CGFX_HEADER_LENGTH + convertedSize);
memcpy(ret, BANNER_CGFX_HEADER, BANNER_CGFX_HEADER_LENGTH);
memcpy(ret + BANNER_CGFX_HEADER_LENGTH, converted, convertedSize);
u8 converted[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);
}
}
}
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;
*size = BANNER_CGFX_HEADER_LENGTH + convertedSize;
return ret;
}
@ -91,31 +40,46 @@ u8* make_audio(const char* file, u32* size) {
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[]) {
if(argc != 4) {
printf("Usage: %s <banner png> <audio bcwav> <output file>", argv[0]);
}
u32 bannerSize = 0;
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;
return make_banner(argv[1], argv[2], argv[3]);
}