Organize code structure, generate CBMD files without premade headers.
This commit is contained in:
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
|
||||
126
source/3ds/lz11.cpp
Normal file
126
source/3ds/lz11.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#include "lz11.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#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 lz11_get_occurence_length(u8* newPtr, int newLength, u8* oldPtr, int oldLength, int* disp) {
|
||||
if(disp != NULL) {
|
||||
*disp = 0;
|
||||
}
|
||||
|
||||
if(newLength == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int maxLength = 0;
|
||||
for(int i = 0; i < oldLength - 1; i++) {
|
||||
u8* currentOldStart = oldPtr + i;
|
||||
int currentLength = 0;
|
||||
for(int j = 0; j < newLength; j++) {
|
||||
if(*(currentOldStart + j) != *(newPtr + j)) {
|
||||
break;
|
||||
}
|
||||
|
||||
currentLength++;
|
||||
}
|
||||
|
||||
if(currentLength > maxLength) {
|
||||
maxLength = currentLength;
|
||||
if(disp != NULL) {
|
||||
*disp = oldLength - i;
|
||||
}
|
||||
|
||||
if(maxLength == newLength) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
u8* lz11_compress(u8* input, u32 inputSize, u32* size) {
|
||||
if (inputSize > 0xFFFFFF) {
|
||||
printf("ERROR: LZ11 input is too large.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
u8 header[4] = { 0x11, (u8) (inputSize & 0xFF), (u8) ((inputSize >> 8) & 0xFF), (u8) ((inputSize >> 16) & 0xFF) };
|
||||
ss.write((char*) header, 4);
|
||||
|
||||
int compressedLength = 4;
|
||||
u8 outbuffer[8 * 4 + 1];
|
||||
outbuffer[0] = 0;
|
||||
int bufferlength = 1;
|
||||
int bufferedBlocks = 0;
|
||||
int readBytes = 0;
|
||||
while(readBytes < inputSize) {
|
||||
if(bufferedBlocks == 8) {
|
||||
ss.write((char*) outbuffer, bufferlength);
|
||||
compressedLength += bufferlength;
|
||||
outbuffer[0] = 0;
|
||||
bufferlength = 1;
|
||||
bufferedBlocks = 0;
|
||||
}
|
||||
|
||||
int disp = 0;
|
||||
int oldLength = MIN(readBytes, 0x1000);
|
||||
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 {
|
||||
readBytes += length;
|
||||
outbuffer[0] |= (u8)(1 << (7 - bufferedBlocks));
|
||||
if(length > 0x110) {
|
||||
outbuffer[bufferlength] = 0x10;
|
||||
outbuffer[bufferlength] |= (u8)(((length - 0x111) >> 12) & 0x0F);
|
||||
bufferlength++;
|
||||
outbuffer[bufferlength] = (u8)(((length - 0x111) >> 4) & 0xFF);
|
||||
bufferlength++;
|
||||
outbuffer[bufferlength] = (u8)(((length - 0x111) << 4) & 0xF0);
|
||||
} else if(length > 0x10) {
|
||||
outbuffer[bufferlength] = 0x00;
|
||||
outbuffer[bufferlength] |= (u8)(((length - 0x111) >> 4) & 0x0F);
|
||||
bufferlength++;
|
||||
outbuffer[bufferlength] = (u8)(((length - 0x111) << 4) & 0xF0);
|
||||
} else {
|
||||
outbuffer[bufferlength] = (u8)(((length - 1) << 4) & 0xF0);
|
||||
}
|
||||
|
||||
outbuffer[bufferlength] |= (u8)(((disp - 1) >> 8) & 0x0F);
|
||||
bufferlength++;
|
||||
outbuffer[bufferlength] = (u8)((disp - 1) & 0xFF);
|
||||
bufferlength++;
|
||||
}
|
||||
|
||||
bufferedBlocks++;
|
||||
}
|
||||
|
||||
if(bufferedBlocks > 0) {
|
||||
ss.write((char*) outbuffer, bufferlength);
|
||||
compressedLength += bufferlength;
|
||||
}
|
||||
|
||||
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);
|
||||
ss.read((char*) buf, compressedLength);
|
||||
|
||||
*size = (u32) compressedLength;
|
||||
return buf;
|
||||
}
|
||||
8
source/3ds/lz11.h
Normal file
8
source/3ds/lz11.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef __LZ11_H__
|
||||
#define __LZ11_H__
|
||||
|
||||
#include "types.h"
|
||||
|
||||
u8* lz11_compress(u8* input, u32 inputSize, u32* size);
|
||||
|
||||
#endif
|
||||
13
source/3ds/types.h
Normal file
13
source/3ds/types.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef __TYPES_H__
|
||||
#define __TYPES_H__
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
|
||||
#endif
|
||||
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
|
||||
Reference in New Issue
Block a user