diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ea802b..f51429e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,5 +3,5 @@ project(bannertool) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") -set(SOURCE_FILES source/main.cpp source/commandline.cpp source/3ds/cbmd.cpp source/3ds/lz11.cpp source/3ds/util.cpp source/lodepng/lodepng.cpp) +set(SOURCE_FILES source/main.cpp source/commandline.cpp source/wav.cpp source/3ds/cbmd.cpp source/3ds/cwav.cpp source/3ds/lz11.cpp source/3ds/util.cpp source/lodepng/lodepng.cpp) add_executable(bannertool ${SOURCE_FILES}) \ No newline at end of file diff --git a/README.md b/README.md index 50b8157..590aa1b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@

bannertool

========== -A tool for creating 3DS banners. Currently does not support WAV files as audio input. \ No newline at end of file +A tool for creating 3DS banners. \ No newline at end of file diff --git a/source/3ds/3ds.h b/source/3ds/3ds.h index f99e4d7..efcad23 100644 --- a/source/3ds/3ds.h +++ b/source/3ds/3ds.h @@ -2,6 +2,7 @@ #define __3DS_H__ #include "cbmd.h" +#include "cwav.h" #include "lz11.h" #include "util.h" diff --git a/source/3ds/cwav.cpp b/source/3ds/cwav.cpp new file mode 100644 index 0000000..368ed6c --- /dev/null +++ b/source/3ds/cwav.cpp @@ -0,0 +1,73 @@ +#include "cwav.h" + +#include "../wav.h" + +#include +#include +#include + +u8* build_cwav(WAV wav, u32* size) { + Header header; + u32 offset = sizeof(Header); + + header.infoChunkOffset = offset; + header.infoChunkLength = sizeof(InfoHeader); + offset += header.infoChunkLength; + + offset += (sizeof(ChannelDataPointer) + sizeof(ChannelData)) * wav.format.numChannels; + + header.dataChunkOffset = offset; + header.dataChunkLength = (u32) sizeof(DataHeader) + wav.data.chunkSize; + offset += header.dataChunkLength; + + header.fileSize = offset; + + u8* output = (u8*) malloc(offset); + u32 pos = 0; + + memcpy(output + pos, &header, sizeof(Header)); + pos += sizeof(Header); + + InfoHeader infoHeader; + infoHeader.type = wav.format.bitsPerSample == 16 ? 1 : 0; + infoHeader.sampleRate = wav.format.sampleRate; + infoHeader.totalSamples = wav.data.chunkSize / (wav.format.bitsPerSample / 8) / wav.format.numChannels; + infoHeader.totalChannels = wav.format.numChannels; + memcpy(output + pos, &infoHeader, sizeof(InfoHeader)); + pos += sizeof(InfoHeader); + + for(int i = 0; i < wav.format.numChannels; i++) { + ChannelDataPointer pointer; + pointer.offset = 0x4 + (wav.format.numChannels * (u32) sizeof(ChannelDataPointer)) + (i * (u32) sizeof(ChannelData)); + memcpy(output + pos, &pointer, sizeof(ChannelDataPointer)); + pos += sizeof(ChannelDataPointer); + } + + for(int i = 0; i < wav.format.numChannels; i++) { + ChannelData data; + data.offset = i * (wav.data.chunkSize / wav.format.numChannels); + memcpy(output + pos, &data, sizeof(ChannelData)); + pos += sizeof(ChannelData); + } + + DataHeader dataHeader; + dataHeader.length = (u32) sizeof(DataHeader) + wav.data.chunkSize; + memcpy(output + pos, &dataHeader, sizeof(DataHeader)); + pos += sizeof(DataHeader); + + u32 bytesPerChannel = wav.data.chunkSize / wav.format.numChannels; + u32 bytesPerSample = (u32) wav.format.bitsPerSample / 8; + for(int i = 0; i < wav.data.chunkSize; i += wav.format.numChannels * bytesPerSample) { + for(int c = 0; c < wav.format.numChannels; c++) { + memcpy(output + pos + (bytesPerChannel * c) + (i / wav.format.numChannels), wav.dataBytes + i + (c * bytesPerSample), bytesPerSample); + } + } + + pos += wav.data.chunkSize; + + if(size != NULL) { + *size = pos; + } + + return output; +} \ No newline at end of file diff --git a/source/3ds/cwav.h b/source/3ds/cwav.h new file mode 100644 index 0000000..7ba31b9 --- /dev/null +++ b/source/3ds/cwav.h @@ -0,0 +1,54 @@ +#ifndef __CWAV_H__ +#define __CWAV_H__ + +#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 \ No newline at end of file diff --git a/source/3ds/lz11.cpp b/source/3ds/lz11.cpp index e5a8c02..cda8e3b 100644 --- a/source/3ds/lz11.cpp +++ b/source/3ds/lz11.cpp @@ -47,7 +47,7 @@ int lz11_get_occurence_length(u8* newPtr, int newLength, u8* oldPtr, int oldLeng u8* lz11_compress(u8* input, u32 inputSize, u32* size) { if (inputSize > 0xFFFFFF) { - printf("ERROR: LZ11 input is too large."); + printf("ERROR: LZ11 input is too large.\n"); return NULL; } diff --git a/source/commandline.cpp b/source/commandline.cpp index b0d58b2..7680dc0 100644 --- a/source/commandline.cpp +++ b/source/commandline.cpp @@ -40,7 +40,7 @@ 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: Audio file to use as the banner's tune.\n"); + printf(" -a/--audio: WAV file to use as the banner's tune.\n"); printf(" -o/--output: File to output the created banner to.\n"); } } diff --git a/source/main.cpp b/source/main.cpp index 2de07d0..3bce946 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -2,6 +2,7 @@ #include "commandline.h" #include "data.h" #include "types.h" +#include "wav.h" #include #include @@ -22,24 +23,18 @@ u8* convert_to_cgfx(const char* image, u32 width, u32 height, u32* size) { return ret; } -u8* make_audio(const char* file, u32* size) { - // TODO: convert from a WAV file. - FILE* fd = fopen(file, "rb"); - if(!fd) { - printf("ERROR: Could not load audio file.\n"); +u8* convert_to_cwav(const char* file, u32* size) { + WAV* wav = read_wav(file); + if(!wav) { return NULL; } - fseek(fd, 0, SEEK_END); - size_t length = (size_t) ftell(fd); - fseek(fd, 0, SEEK_SET); + u8* cwav = build_cwav(*wav, size); - u8* data = (u8*) malloc(length); - fread(data, 1, length, fd); - fclose(fd); + free(wav->dataBytes); + free(wav); - *size = (u32) length; - return data; + return cwav; } int make_banner(const char* image, const char* audio, const char* output) { @@ -50,8 +45,8 @@ int make_banner(const char* image, const char* audio, const char* output) { } u32 cwavSize = 0; - u8* cwav = make_audio(audio, &cwavSize); - if(!audio) { + u8* cwav = convert_to_cwav(audio, &cwavSize); + if(!cwav) { return 2; } @@ -75,7 +70,7 @@ int make_banner(const char* image, const char* audio, const char* output) { fwrite(bnr, 1, bnrSize, fd); fclose(fd); free(bnr); - printf("Created banner \"%s\".", output); + printf("Created banner \"%s\".\n", output); return 0; } diff --git a/source/wav.cpp b/source/wav.cpp new file mode 100644 index 0000000..631518d --- /dev/null +++ b/source/wav.cpp @@ -0,0 +1,62 @@ +#include "wav.h" + +#include +#include +#include + +bool find_chunk(FILE* fd, const char* magic) { + char curr[5] = {0}; + while(strcmp(curr, magic) != 0) { + u32 read = (u32) fread(curr, 1, 4, fd); + if(read == 0) { + return false; + } + } + + fseek(fd, -4, SEEK_CUR); + return true; +} + +WAV* read_wav(const char* file) { + FILE* fd = fopen(file, "r"); + if(!fd) { + printf("ERROR: Could not open WAV file.\n"); + return NULL; + } + + if(!find_chunk(fd, "RIFF")) { + printf("ERROR: Could not find WAV RIFF chunk.\n"); + return NULL; + } + + Riff riff; + fread(&riff, sizeof(Riff), 1, fd); + + if(!find_chunk(fd, "fmt ")) { + printf("ERROR: Could not find WAV format chunk.\n"); + return NULL; + } + + Format format; + fread(&format, sizeof(Format), 1, fd); + + if(!find_chunk(fd, "data")) { + printf("ERROR: Could not find WAV data chunk.\n"); + return NULL; + } + + Data data; + fread(&data, sizeof(Data), 1, fd); + + u8* dataBytes = (u8*) malloc(data.chunkSize); + fread(dataBytes, 1, data.chunkSize, fd); + + fclose(fd); + + WAV* wav = (WAV*) malloc(sizeof(WAV)); + wav->riff = riff; + wav->format = format; + wav->data = data; + wav->dataBytes = dataBytes; + return wav; +} \ No newline at end of file diff --git a/source/wav.h b/source/wav.h new file mode 100644 index 0000000..d0a7d96 --- /dev/null +++ b/source/wav.h @@ -0,0 +1,37 @@ +#ifndef __WAV_H__ +#define __WAV_H__ + +#include "types.h" + +typedef struct { + char chunkId[4]; + u32 chunkSize; + char format[4]; +} Riff; + +typedef struct { + char chunkId[4]; + u32 chunkSize; + u16 format; + u16 numChannels; + u32 sampleRate; + u32 byteRate; + u16 align; + u16 bitsPerSample; +} Format; + +typedef struct { + char chunkId[4]; + u32 chunkSize; +} Data; + +typedef struct { + Riff riff; + Format format; + Data data; + u8* dataBytes; +} WAV; + +WAV* read_wav(const char* file); + +#endif \ No newline at end of file