├── revorb ├── api.h └── revorb.c ├── .gitignore ├── ww2ogg ├── api.h ├── crc.h ├── COPYING ├── errors.hpp ├── wwriff.hpp ├── codebook.hpp ├── crc.c ├── ww2ogg.cpp ├── codebook.cpp ├── Bit_stream.hpp └── wwriff.cpp ├── manifest.rc ├── wpk.h ├── bnk.h ├── bnk-extract.exe.manifest ├── extract.h ├── bin.h ├── general_utils.h ├── README.md ├── defs.h ├── Makefile ├── general_utils.c ├── bnk.c ├── wpk.c ├── bin.c ├── list.h ├── extract.c └── sound.c /revorb/api.h: -------------------------------------------------------------------------------- 1 | int revorb(int argc, const char** argv); 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.vscode 2 | **/.vs 3 | 4 | bnk-extract 5 | bnk-extract.exe 6 | 7 | **/*.o 8 | -------------------------------------------------------------------------------- /ww2ogg/api.h: -------------------------------------------------------------------------------- 1 | #include "../defs.h" 2 | 3 | BinaryData* ww2ogg(int argc, char** argv); 4 | -------------------------------------------------------------------------------- /manifest.rc: -------------------------------------------------------------------------------- 1 | #include 2 | CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "bnk-extract.exe.manifest" 3 | -------------------------------------------------------------------------------- /wpk.h: -------------------------------------------------------------------------------- 1 | #ifndef WPK_H 2 | #define WPK_H 3 | 4 | #include 5 | #include "bin.h" 6 | 7 | void extract_wpk_file(char* wpk_path, StringHashes* string_hashes, char* output_path, bool wems_only, bool oggs_only, bool alternate_filenames); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /ww2ogg/crc.h: -------------------------------------------------------------------------------- 1 | #ifndef _CRC_H 2 | #define _CRC_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | uint32_t checksum(unsigned char *data, int bytes); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /bnk.h: -------------------------------------------------------------------------------- 1 | #ifndef BNK_H 2 | #define BNK_H 3 | 4 | #include 5 | #include 6 | #include "bin.h" 7 | 8 | uint32_t skip_to_section(FILE* bnk_file, char name[4], bool from_beginning); 9 | 10 | void extract_bnk_file(char* bnk_path, StringHashes* string_hashes, char* output_path, bool wems_only, bool oggs_only, bool alternate_filenames); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /bnk-extract.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /extract.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "defs.h" 3 | #include "bin.h" 4 | #include "list.h" 5 | 6 | typedef struct audio_data { 7 | uint32_t id; 8 | BinaryData data; 9 | } AudioData; 10 | 11 | typedef LIST(AudioData) AudioDataList; 12 | 13 | void extract_all_audio(char* output_path, AudioDataList* audio_data_list, StringHashes* string_hashes, bool wems_only, bool oggs_onlys, bool alternate_filenames); 14 | -------------------------------------------------------------------------------- /bin.h: -------------------------------------------------------------------------------- 1 | #ifndef BIN_H 2 | #define BIN_H 3 | 4 | #include 5 | #include "list.h" 6 | 7 | struct string_hash { 8 | char* string; 9 | uint32_t hash; 10 | uint32_t container_id; 11 | uint32_t music_segment_id; 12 | uint32_t switch_id; 13 | uint32_t sound_index; 14 | }; 15 | typedef LIST(struct string_hash) StringHashes; 16 | 17 | uint32_t fnv_1_hash(const char* input); 18 | StringHashes* parse_bin_file(char* bin_path); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /general_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef GENERAL_UTILS_H 2 | #define GENERAL_UTILS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | char* lower(const char* string); 11 | 12 | char* lower_inplace(char* string); 13 | 14 | void hex2bytes(const char* input, void* output, int input_length); 15 | 16 | void bytes2hex(const void* input, char* output, int input_length); 17 | 18 | int create_dir(char* path); 19 | 20 | int create_dirs(char* dir_path, bool create_last); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bnk-extract 2 | 3 | Build depends on the libogg and libvorbis libraries, which are not included and must be installed manually (e.g. sudo apt install libogg-dev libvorbis-dev). 4 | 5 | Shoutouts to the original creators of [ww2ogg](https://github.com/hcs64/ww2ogg) and [revorb](https://github.com/jonboydell/revorb-nix), which I use in a modified version for this program (they are included in their respective subfolders). 6 | 7 | Linux systems and mingw should be able to build out-of-the-box using a simple ``make`` (after installing the needed packages). If the compilation fails, try compiling dynamically instead of statically (I've had troubles with the static libvorbis package on linux). 8 | -------------------------------------------------------------------------------- /defs.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFS_H 2 | #define DEFS_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | #include 10 | 11 | extern int VERBOSE; 12 | 13 | #ifdef _WIN32 14 | #define mkdir(path, mode) mkdir(path) 15 | #define flockfile(file) _lock_file(file) 16 | #define funlockfile(file) _unlock_file(file) 17 | #define link(existing_file, new_file) !CreateHardLink(new_file, existing_file, NULL) 18 | #endif 19 | 20 | typedef struct { 21 | uint64_t length; 22 | uint8_t* data; 23 | } BinaryData; 24 | 25 | #ifdef DEBUG 26 | #define dprintf(...) printf(__VA_ARGS__) 27 | #else 28 | #define dprintf(...) 29 | #endif 30 | #define eprintf(...) fprintf(stderr, __VA_ARGS__) 31 | #define v_printf(level, ...) if (VERBOSE >= level) printf(__VA_ARGS__) 32 | 33 | #ifndef __cplusplus 34 | #ifdef max 35 | #undef max 36 | #endif 37 | #define max(a, b) __extension__ ({ \ 38 | __typeof__(a) _a = (a); \ 39 | __typeof__(b) _b = (b); \ 40 | _a > _b ? _a : _b; \ 41 | }) 42 | 43 | #ifdef min 44 | #undef min 45 | #endif 46 | #define min(a, b) __extension__ ({ \ 47 | __typeof__(a) _a = (a); \ 48 | __typeof__(b) _b = (b); \ 49 | _a < _b ? _a : _b; \ 50 | }) 51 | #endif 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /ww2ogg/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2002, Xiph.org Foundation 2 | Copyright (c) 2009-2016, Adam Gashlin 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of the Xiph.org Foundation nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION 23 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC := gcc 2 | CXX := g++ 3 | ifdef DEBUG 4 | _DEBUG := -DDEBUG 5 | endif 6 | CFLAGS := -std=gnu18 -g -Wall -Wextra -pedantic -Os -flto -ffunction-sections -fdata-sections $(_DEBUG) 7 | CXXFLAGS := -g -Wall -Wextra -Wno-unused-function -Os -flto -ffunction-sections -fdata-sections 8 | LDFLAGS := -l:libogg.a -l:libvorbis.a -Wl,--gc-sections 9 | target := bnk-extract 10 | 11 | sound_OBJECTS=general_utils.o bin.o bnk.o extract.o wpk.o sound.o 12 | 13 | ifeq ($(OS),Windows_NT) 14 | LDFLAGS := $(LDFLAGS) -static 15 | target := $(target).exe 16 | sound_OBJECTS += manifest.res 17 | endif 18 | 19 | all: $(target) 20 | strip: LDFLAGS := $(LDFLAGS) -s 21 | strip: all 22 | 23 | %.res: %.rc 24 | windres --output-format=coff $< $@ 25 | general_utils.o: general_utils.h defs.h 26 | bin.o: bin.h defs.h list.h 27 | bnk.o: bnk.h bin.h defs.h extract.h 28 | extract.c: extract.h defs.h general_utils.h 29 | wpk.o: wpk.h bin.h defs.h extract.h 30 | sound.o: bin.h bnk.h defs.h wpk.h 31 | 32 | BIT_STREAM_HEADERS=ww2ogg/Bit_stream.hpp ww2ogg/crc.h ww2ogg/errors.hpp 33 | WWRIFF_HEADERS=ww2ogg/wwriff.hpp $(BIT_STREAM_HEADERS) 34 | 35 | ww2ogg_OBJECTS=ww2ogg/ww2ogg.o ww2ogg/wwriff.o ww2ogg/codebook.o ww2ogg/crc.o 36 | 37 | ww2ogg/ww2ogg.o: ww2ogg/ww2ogg.cpp $(WWRIFF_HEADERS) general_utils.h 38 | ww2ogg/wwriff.o: ww2ogg/wwriff.cpp ww2ogg/codebook.hpp $(WWRIFF_HEADERS) defs.h 39 | ww2ogg/codebook.o: ww2ogg/codebook.cpp ww2ogg/codebook.hpp $(BIT_STREAM_HEADERS) defs.h 40 | ww2ogg/crc.o: ww2ogg/crc.c ww2ogg/crc.h 41 | 42 | revorb_OBJECTS=revorb/revorb.o 43 | revorb/revorb.o: revorb/revorb.c defs.h general_utils.h 44 | 45 | bnk-extract bnk-extract.exe: $(ww2ogg_OBJECTS) $(revorb_OBJECTS) $(sound_OBJECTS) 46 | $(CXX) $(CXXFLAGS) $^ $(LDFLAGS) -o $@ 47 | 48 | 49 | clean: 50 | rm -f bnk-extract bnk-extract.exe $(sound_OBJECTS) $(ww2ogg_OBJECTS) $(revorb_OBJECTS) 51 | -------------------------------------------------------------------------------- /ww2ogg/errors.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _ERRORS_H 2 | #define _ERRORS_H 3 | 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | class Argument_error { 9 | string errmsg; 10 | public: 11 | inline void print(FILE* out) const { 12 | fprintf(out, "Argument error: %s", errmsg.c_str()); 13 | } 14 | 15 | explicit inline Argument_error(const char * str) : errmsg(str) {} 16 | }; 17 | 18 | class File_open_error { 19 | string filename; 20 | public: 21 | inline void print(FILE* out) const { 22 | fprintf(out, "Error opening: %s", filename.c_str()); 23 | } 24 | 25 | explicit inline File_open_error(const string& name) : filename(name) {} 26 | }; 27 | 28 | class Parse_error { 29 | protected: 30 | virtual inline void print_self(FILE* out) const { 31 | fprintf(out, "unspecified."); 32 | } 33 | public: 34 | inline void print(FILE* out) const { 35 | fprintf(out, "Parse error: "); 36 | print_self(out); 37 | fprintf(out, "\n"); 38 | } 39 | inline virtual ~Parse_error() = default; 40 | }; 41 | 42 | class Parse_error_str : public Parse_error { 43 | string str; 44 | protected: 45 | inline void print_self(FILE* out) const override { 46 | fprintf(out, "%s", str.c_str()); 47 | } 48 | public: 49 | explicit inline Parse_error_str(string s) : str(s) {} 50 | }; 51 | 52 | class Size_mismatch : public Parse_error { 53 | const unsigned long long real_size, read_size; 54 | protected: 55 | inline void print_self(FILE* out) const override { 56 | fprintf(out, "expected %llu bits, read %llu", real_size, read_size); 57 | } 58 | public: 59 | explicit inline Size_mismatch(unsigned long real_s, unsigned long read_s) : real_size(real_s), read_size(read_s) {} 60 | }; 61 | 62 | class Invalid_id : public Parse_error { 63 | const int id; 64 | protected: 65 | inline void print_self(FILE* out) const override { 66 | fprintf(out, "invalid codebook id %d, try --inline-codebooks", id); 67 | } 68 | public: 69 | explicit inline Invalid_id(int i) : id(i) {} 70 | }; 71 | 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /ww2ogg/wwriff.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _WWRIFF_H 2 | #define _WWRIFF_H 3 | 4 | #ifndef __STDC_CONSTANT_MACROS 5 | #define __STDC_CONSTANT_MACROS 6 | #endif 7 | #include 8 | #include "Bit_stream.hpp" 9 | #include "stdint.h" 10 | #include "errors.hpp" 11 | 12 | #define VERSION "0.24b" 13 | 14 | using namespace std; 15 | 16 | enum ForcePacketFormat { 17 | kNoForcePacketFormat, 18 | kForceModPackets, 19 | kForceNoModPackets 20 | }; 21 | 22 | 23 | class Wwise_RIFF_Vorbis 24 | { 25 | const BinaryData& _infile_data; 26 | string _codebooks_name; 27 | 28 | bool _little_endian; 29 | bool _is_wav; 30 | 31 | uint32_t _riff_size; 32 | long _fmt_offset, _cue_offset, _LIST_offset, _smpl_offset, _vorb_offset, _data_offset; 33 | int64_t _fmt_size, _cue_size, _LIST_size, _smpl_size, _vorb_size, _data_size; 34 | 35 | // RIFF fmt 36 | uint16_t _channels; 37 | uint32_t _sample_rate; 38 | uint32_t _avg_bytes_per_second; 39 | uint16_t _block_align; 40 | uint16_t _bits_per_sample; 41 | 42 | // RIFF extended fmt 43 | uint16_t _ext_unk; 44 | uint32_t _subtype; 45 | 46 | // cue info 47 | uint32_t _cue_count; 48 | 49 | // smpl info 50 | uint32_t _loop_count, _loop_start, _loop_end; 51 | 52 | // vorbis info 53 | uint32_t _sample_count; 54 | uint32_t _setup_packet_offset; 55 | uint32_t _first_audio_packet_offset; 56 | uint32_t _uid; 57 | uint8_t _blocksize_0_pow; 58 | uint8_t _blocksize_1_pow; 59 | 60 | const bool _inline_codebooks, _full_setup; 61 | bool _header_triad_present, _old_packet_headers; 62 | bool _no_granule, _mod_packets; 63 | 64 | uint16_t (*_read_16)(unsigned char b[4]); 65 | uint32_t (*_read_32)(unsigned char b[4]); 66 | public: 67 | Wwise_RIFF_Vorbis( 68 | const BinaryData& bd, 69 | const string& _codebooks_name, 70 | bool inline_codebooks, 71 | bool full_setup, 72 | ForcePacketFormat force_packet_format 73 | ); 74 | 75 | void print_info(void); 76 | 77 | void generate_ogg(BinaryData& bd); 78 | void generate_wav_header(BinaryData& bd); 79 | void generate_ogg_header(Bit_oggstream& os, bool * & mode_blockflag, int & mode_bits); 80 | void generate_ogg_header_with_triad(Bit_oggstream& os); 81 | }; 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /ww2ogg/codebook.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _CODEBOOK_H 2 | #define _CODEBOOK_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "errors.hpp" 8 | #include "Bit_stream.hpp" 9 | 10 | using namespace std; 11 | 12 | /* stuff from Tremor (lowmem) */ 13 | namespace { 14 | int ilog(unsigned int v){ 15 | int ret=0; 16 | while(v){ 17 | ret++; 18 | v>>=1; 19 | } 20 | return(ret); 21 | } 22 | 23 | unsigned int _book_maptype1_quantvals(unsigned int entries, unsigned int dimensions){ 24 | /* get us a starting hint, we'll polish it below */ 25 | int bits=ilog(entries); 26 | int vals=entries>>((bits-1)*(dimensions-1)/dimensions); 27 | 28 | while(1){ 29 | unsigned long acc=1; 30 | unsigned long acc1=1; 31 | unsigned int i; 32 | for(i=0;ientries){ 37 | return(vals); 38 | }else{ 39 | if(acc>entries){ 40 | vals--; 41 | }else{ 42 | vals++; 43 | } 44 | } 45 | } 46 | } 47 | 48 | } 49 | 50 | class codebook_library 51 | { 52 | char * codebook_data; 53 | uint32_t * codebook_offsets; 54 | int codebook_count; 55 | 56 | // Intentionally undefined 57 | codebook_library& operator=(const codebook_library& rhs); 58 | codebook_library(const codebook_library& rhs); 59 | 60 | public: 61 | codebook_library(unsigned char* data, int length); 62 | codebook_library(const string& filename); 63 | codebook_library(void); 64 | 65 | ~codebook_library() 66 | { 67 | delete [] codebook_data; 68 | delete [] codebook_offsets; 69 | } 70 | 71 | const char * get_codebook(int i) const 72 | { 73 | if (!codebook_data || !codebook_offsets) 74 | { 75 | throw Parse_error_str("codebook library not loaded"); 76 | } 77 | if (i >= codebook_count-1 || i < 0) return NULL; 78 | return &codebook_data[codebook_offsets[i]]; 79 | } 80 | 81 | long get_codebook_size(int i) const 82 | { 83 | if (!codebook_data || !codebook_offsets) 84 | { 85 | throw Parse_error_str("codebook library not loaded"); 86 | } 87 | if (i >= codebook_count-1 || i < 0) return -1; 88 | return codebook_offsets[i+1]-codebook_offsets[i]; 89 | } 90 | 91 | void rebuild(int i, Bit_oggstream& bos); 92 | 93 | void rebuild(Bit_stream &bis, unsigned long cb_size, Bit_oggstream& bos); 94 | 95 | void copy(Bit_stream &bis, Bit_oggstream& bos); 96 | }; 97 | #endif 98 | -------------------------------------------------------------------------------- /general_utils.c: -------------------------------------------------------------------------------- 1 | #ifndef _WIN32 2 | # include 3 | #else 4 | # include 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "general_utils.h" 14 | #include "defs.h" 15 | 16 | char* lower_inplace(char* string) 17 | { 18 | for (char* c = string; *c; c++) { 19 | *c = tolower(*c); 20 | } 21 | 22 | return string; 23 | } 24 | 25 | char* lower(const char* string) 26 | { 27 | size_t string_length = strlen(string); 28 | char* lower_string = malloc(string_length + 1); 29 | for (size_t i = 0; i < string_length; i++) { 30 | lower_string[i] = tolower(string[i]); 31 | } 32 | lower_string[string_length] = '\0'; 33 | 34 | return lower_string; 35 | } 36 | 37 | int char2int(char input) 38 | { 39 | if (input >= '0' && input <= '9') 40 | return input - '0'; 41 | if (input >= 'A' && input <= 'F') 42 | return input - 'A' + 10; 43 | if (input >= 'a' && input <= 'f') 44 | return input - 'a' + 10; 45 | eprintf("Error: Malformed input to \"%s\" (%X).\n", __func__, input); 46 | return -1; 47 | } 48 | 49 | void hex2bytes(const char* input, void* output, int input_length) 50 | { 51 | for (int i = 0; i < input_length; i += 2) { 52 | ((uint8_t*) output)[i / 2] = char2int(input[i]) * 16 + char2int(input[i + 1]); 53 | } 54 | } 55 | 56 | // converts input_length bytes from input into uppercase hexadecimal representation and saves them in output. 57 | // Will not modify input, add a null byte at the end of output, or allocate the output buffer (make sure it's big enough) 58 | void bytes2hex(const void* input, char* output, int input_length) 59 | { 60 | const uint8_t* in = input; 61 | for (int i = 0; i < input_length; i++) { 62 | output[2 * i] = ((in[i] & 0xF0) >> 4) + (in[i] > 159 ? 55 : 48); 63 | output[2 * i + 1] = (in[i] & 0x0F) + ((in[i] & 0x0F) > 9 ? 55 : 48); 64 | } 65 | } 66 | 67 | int create_dir(char* path) 68 | { 69 | int ret = mkdir(path, 0700); 70 | if (ret == -1 && errno != EEXIST) 71 | return -1; 72 | return 0; 73 | } 74 | 75 | int create_dirs(char* dir_path, bool create_last) 76 | { 77 | char* c = dir_path; 78 | if (strlen(c) >= 3 && c[1] == ':' && (c[2] == '\\' || c[2] == '/')) 79 | c += 3; 80 | while (*c != 0) { 81 | c++; 82 | if (*c == '/' || *c == '\\') { 83 | char _c = *c; 84 | *c = '\0'; 85 | int ret = create_dir(dir_path); 86 | *c = _c; 87 | if (ret == -1) 88 | return -1; 89 | } 90 | } 91 | if (create_last && create_dir(dir_path) == -1) 92 | return -1; 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /bnk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "defs.h" 9 | #include "bin.h" 10 | #include "extract.h" 11 | 12 | struct BNKFileEntry { 13 | uint32_t file_id; 14 | uint32_t offset; 15 | uint32_t length; 16 | uint8_t* data; 17 | }; 18 | 19 | struct BNKFile { 20 | uint32_t length; 21 | struct BNKFileEntry* entries; 22 | }; 23 | 24 | uint32_t skip_to_section(FILE* bnk_file, char name[4], bool from_beginning) 25 | { 26 | if (from_beginning) 27 | fseek(bnk_file, 0, SEEK_SET); 28 | 29 | uint8_t header[4]; 30 | uint32_t section_length = 0; 31 | 32 | do { 33 | fseek(bnk_file, section_length, SEEK_CUR); 34 | if (fread(header, 1, 4, bnk_file) != 4) 35 | return 0; 36 | assert(fread(§ion_length, 4, 1, bnk_file) == 1); 37 | } while (memcmp(header, name, 4) != 0); 38 | 39 | return section_length; 40 | } 41 | 42 | static int parse_bnk_file_entries(FILE* bnk_file, struct BNKFile* bnkfile) 43 | { 44 | uint32_t section_length = skip_to_section(bnk_file, "DIDX", false); 45 | if (!section_length) 46 | return -1; 47 | 48 | bnkfile->length = section_length / 12; 49 | bnkfile->entries = malloc(bnkfile->length * sizeof(struct BNKFileEntry)); 50 | for (uint32_t i = 0; i < bnkfile->length; i++) { 51 | assert(fread(&bnkfile->entries[i].file_id, 4, 1, bnk_file) == 1); 52 | assert(fread(&bnkfile->entries[i].offset, 4, 1, bnk_file) == 1); 53 | assert(fread(&bnkfile->entries[i].length, 4, 1, bnk_file) == 1); 54 | } 55 | 56 | section_length = skip_to_section(bnk_file, "DATA", false); 57 | if (!section_length) 58 | return -1; 59 | 60 | uint32_t data_offset = ftell(bnk_file); 61 | for (uint32_t i = 0; i < bnkfile->length; i++) { 62 | fseek(bnk_file, data_offset + bnkfile->entries[i].offset, SEEK_SET); 63 | bnkfile->entries[i].data = malloc(bnkfile->entries[i].length); 64 | assert(fread(bnkfile->entries[i].data, 1, bnkfile->entries[i].length, bnk_file) == bnkfile->entries[i].length); 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | void extract_bnk_file(char* bnk_path, StringHashes* string_hashes, char* output_path, bool wems_only, bool oggs_only, bool alternate_filenames) 71 | { 72 | FILE* bnk_file = fopen(bnk_path, "rb"); 73 | if (!bnk_file) { 74 | eprintf("Error: Failed to open \"%s\".\n", bnk_path); 75 | exit(EXIT_FAILURE); 76 | } 77 | 78 | struct BNKFile bnkfile; 79 | if (parse_bnk_file_entries(bnk_file, &bnkfile) == -1) { 80 | eprintf("Error: Failed to find the required sections in file \"%s\". Make sure to provide the correct file.\n", bnk_path); 81 | exit(EXIT_FAILURE); 82 | } 83 | fclose(bnk_file); 84 | 85 | AudioDataList audio_data_list; 86 | initialize_list_size(&audio_data_list, bnkfile.length); 87 | for (uint32_t i = 0; i < bnkfile.length; i++) { 88 | add_object(&audio_data_list, (&(AudioData) {.id = bnkfile.entries[i].file_id, .data = {.length = bnkfile.entries[i].length, .data = bnkfile.entries[i].data}})); 89 | } 90 | 91 | extract_all_audio(output_path, &audio_data_list, string_hashes, wems_only, oggs_only, alternate_filenames); 92 | 93 | for (uint32_t i = 0; i < bnkfile.length; i++) { 94 | free(bnkfile.entries[i].data); 95 | } 96 | free(bnkfile.entries); 97 | free(audio_data_list.objects); 98 | } 99 | -------------------------------------------------------------------------------- /ww2ogg/crc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "crc.h" 3 | 4 | /* from Tremor (lowmem) */ 5 | static uint32_t crc_lookup[256]={ 6 | 0x00000000,0x04c11db7,0x09823b6e,0x0d4326d9, 7 | 0x130476dc,0x17c56b6b,0x1a864db2,0x1e475005, 8 | 0x2608edb8,0x22c9f00f,0x2f8ad6d6,0x2b4bcb61, 9 | 0x350c9b64,0x31cd86d3,0x3c8ea00a,0x384fbdbd, 10 | 0x4c11db70,0x48d0c6c7,0x4593e01e,0x4152fda9, 11 | 0x5f15adac,0x5bd4b01b,0x569796c2,0x52568b75, 12 | 0x6a1936c8,0x6ed82b7f,0x639b0da6,0x675a1011, 13 | 0x791d4014,0x7ddc5da3,0x709f7b7a,0x745e66cd, 14 | 0x9823b6e0,0x9ce2ab57,0x91a18d8e,0x95609039, 15 | 0x8b27c03c,0x8fe6dd8b,0x82a5fb52,0x8664e6e5, 16 | 0xbe2b5b58,0xbaea46ef,0xb7a96036,0xb3687d81, 17 | 0xad2f2d84,0xa9ee3033,0xa4ad16ea,0xa06c0b5d, 18 | 0xd4326d90,0xd0f37027,0xddb056fe,0xd9714b49, 19 | 0xc7361b4c,0xc3f706fb,0xceb42022,0xca753d95, 20 | 0xf23a8028,0xf6fb9d9f,0xfbb8bb46,0xff79a6f1, 21 | 0xe13ef6f4,0xe5ffeb43,0xe8bccd9a,0xec7dd02d, 22 | 0x34867077,0x30476dc0,0x3d044b19,0x39c556ae, 23 | 0x278206ab,0x23431b1c,0x2e003dc5,0x2ac12072, 24 | 0x128e9dcf,0x164f8078,0x1b0ca6a1,0x1fcdbb16, 25 | 0x018aeb13,0x054bf6a4,0x0808d07d,0x0cc9cdca, 26 | 0x7897ab07,0x7c56b6b0,0x71159069,0x75d48dde, 27 | 0x6b93dddb,0x6f52c06c,0x6211e6b5,0x66d0fb02, 28 | 0x5e9f46bf,0x5a5e5b08,0x571d7dd1,0x53dc6066, 29 | 0x4d9b3063,0x495a2dd4,0x44190b0d,0x40d816ba, 30 | 0xaca5c697,0xa864db20,0xa527fdf9,0xa1e6e04e, 31 | 0xbfa1b04b,0xbb60adfc,0xb6238b25,0xb2e29692, 32 | 0x8aad2b2f,0x8e6c3698,0x832f1041,0x87ee0df6, 33 | 0x99a95df3,0x9d684044,0x902b669d,0x94ea7b2a, 34 | 0xe0b41de7,0xe4750050,0xe9362689,0xedf73b3e, 35 | 0xf3b06b3b,0xf771768c,0xfa325055,0xfef34de2, 36 | 0xc6bcf05f,0xc27dede8,0xcf3ecb31,0xcbffd686, 37 | 0xd5b88683,0xd1799b34,0xdc3abded,0xd8fba05a, 38 | 0x690ce0ee,0x6dcdfd59,0x608edb80,0x644fc637, 39 | 0x7a089632,0x7ec98b85,0x738aad5c,0x774bb0eb, 40 | 0x4f040d56,0x4bc510e1,0x46863638,0x42472b8f, 41 | 0x5c007b8a,0x58c1663d,0x558240e4,0x51435d53, 42 | 0x251d3b9e,0x21dc2629,0x2c9f00f0,0x285e1d47, 43 | 0x36194d42,0x32d850f5,0x3f9b762c,0x3b5a6b9b, 44 | 0x0315d626,0x07d4cb91,0x0a97ed48,0x0e56f0ff, 45 | 0x1011a0fa,0x14d0bd4d,0x19939b94,0x1d528623, 46 | 0xf12f560e,0xf5ee4bb9,0xf8ad6d60,0xfc6c70d7, 47 | 0xe22b20d2,0xe6ea3d65,0xeba91bbc,0xef68060b, 48 | 0xd727bbb6,0xd3e6a601,0xdea580d8,0xda649d6f, 49 | 0xc423cd6a,0xc0e2d0dd,0xcda1f604,0xc960ebb3, 50 | 0xbd3e8d7e,0xb9ff90c9,0xb4bcb610,0xb07daba7, 51 | 0xae3afba2,0xaafbe615,0xa7b8c0cc,0xa379dd7b, 52 | 0x9b3660c6,0x9ff77d71,0x92b45ba8,0x9675461f, 53 | 0x8832161a,0x8cf30bad,0x81b02d74,0x857130c3, 54 | 0x5d8a9099,0x594b8d2e,0x5408abf7,0x50c9b640, 55 | 0x4e8ee645,0x4a4ffbf2,0x470cdd2b,0x43cdc09c, 56 | 0x7b827d21,0x7f436096,0x7200464f,0x76c15bf8, 57 | 0x68860bfd,0x6c47164a,0x61043093,0x65c52d24, 58 | 0x119b4be9,0x155a565e,0x18197087,0x1cd86d30, 59 | 0x029f3d35,0x065e2082,0x0b1d065b,0x0fdc1bec, 60 | 0x3793a651,0x3352bbe6,0x3e119d3f,0x3ad08088, 61 | 0x2497d08d,0x2056cd3a,0x2d15ebe3,0x29d4f654, 62 | 0xc5a92679,0xc1683bce,0xcc2b1d17,0xc8ea00a0, 63 | 0xd6ad50a5,0xd26c4d12,0xdf2f6bcb,0xdbee767c, 64 | 0xe3a1cbc1,0xe760d676,0xea23f0af,0xeee2ed18, 65 | 0xf0a5bd1d,0xf464a0aa,0xf9278673,0xfde69bc4, 66 | 0x89b8fd09,0x8d79e0be,0x803ac667,0x84fbdbd0, 67 | 0x9abc8bd5,0x9e7d9662,0x933eb0bb,0x97ffad0c, 68 | 0xafb010b1,0xab710d06,0xa6322bdf,0xa2f33668, 69 | 0xbcb4666d,0xb8757bda,0xb5365d03,0xb1f740b4}; 70 | 71 | uint32_t checksum(unsigned char *data, int bytes){ 72 | uint32_t crc_reg=0; 73 | int i; 74 | 75 | for(i=0;i> 24)&0xff)^data[i]]; 77 | 78 | return crc_reg; 79 | } 80 | -------------------------------------------------------------------------------- /wpk.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "wpk.h" 8 | #include "defs.h" 9 | #include "bin.h" 10 | #include "extract.h" 11 | 12 | struct WPKFileEntry { 13 | uint32_t data_offset; 14 | uint32_t data_length; 15 | char* filename; 16 | uint8_t* data; 17 | }; 18 | 19 | struct WPKFile { 20 | uint8_t magic[4]; 21 | uint32_t version; 22 | uint32_t file_count; 23 | uint32_t offset_amount; 24 | uint32_t* offsets; 25 | struct WPKFileEntry* wpk_file_entries; 26 | }; 27 | 28 | 29 | void parse_header(FILE* wpk_file, struct WPKFile* wpkfile) 30 | { 31 | assert(fread(wpkfile->magic, 1, 4, wpk_file) == 4); 32 | assert(memcmp(wpkfile->magic, "r3d2", 4) == 0); 33 | assert(fread(&wpkfile->version, 4, 1, wpk_file) == 1); 34 | assert(fread(&wpkfile->file_count, 4, 1, wpk_file) == 1); 35 | } 36 | 37 | void parse_offsets(FILE* wpk_file, struct WPKFile* wpkfile) 38 | { 39 | wpkfile->offsets = malloc(wpkfile->file_count * 4); 40 | wpkfile->offset_amount = wpkfile->file_count; 41 | assert(fread(wpkfile->offsets, 4, wpkfile->file_count, wpk_file) == wpkfile->file_count); 42 | } 43 | 44 | void parse_data(FILE* wpk_file, struct WPKFile* wpkfile) 45 | { 46 | int real_file_count = 0; 47 | for (uint32_t i = 0; i < wpkfile->offset_amount; i++) { 48 | if (wpkfile->offsets[i] != 0) // riot with their padding bytes :) 49 | real_file_count++; 50 | } 51 | wpkfile->file_count = real_file_count; 52 | 53 | wpkfile->wpk_file_entries = malloc(wpkfile->file_count * sizeof(struct WPKFileEntry)); 54 | int real_index = 0; 55 | for (uint32_t i = 0; i < wpkfile->offset_amount; i++, real_index++) { 56 | if (wpkfile->offsets[i] == 0) { 57 | real_index--; 58 | continue; 59 | } 60 | fseek(wpk_file, wpkfile->offsets[real_index], SEEK_SET); 61 | 62 | assert(fread(&wpkfile->wpk_file_entries[real_index].data_offset, 4, 1, wpk_file) == 1); 63 | assert(fread(&wpkfile->wpk_file_entries[real_index].data_length, 4, 1, wpk_file) == 1); 64 | 65 | int filename_size; 66 | assert(fread(&filename_size, 4, 1, wpk_file) == 1); 67 | wpkfile->wpk_file_entries[real_index].filename = malloc(filename_size + 1); 68 | for (int j = 0; j < filename_size; j++) { 69 | wpkfile->wpk_file_entries[real_index].filename[j] = getc(wpk_file); 70 | fseek(wpk_file, 1, SEEK_CUR); 71 | } 72 | wpkfile->wpk_file_entries[real_index].filename[filename_size] = '\0'; 73 | dprintf("string: \"%s\"\n", wpkfile->wpk_file_entries[real_index].filename); 74 | 75 | wpkfile->wpk_file_entries[real_index].data = malloc(wpkfile->wpk_file_entries[real_index].data_length); 76 | fseek(wpk_file, wpkfile->wpk_file_entries[real_index].data_offset, SEEK_SET); 77 | assert(fread(wpkfile->wpk_file_entries[real_index].data, 1, wpkfile->wpk_file_entries[real_index].data_length, wpk_file) == wpkfile->wpk_file_entries[real_index].data_length); 78 | } 79 | } 80 | 81 | void extract_wpk_file(char* wpk_path, StringHashes* string_hashes, char* output_path, bool wems_only, bool oggs_only, bool alternate_filenames) 82 | { 83 | FILE* wpk_file = fopen(wpk_path, "rb"); 84 | if (!wpk_file) { 85 | eprintf("Error: Failed to open \"%s\".\n", wpk_path); 86 | exit(EXIT_FAILURE); 87 | } 88 | 89 | struct WPKFile wpkfile; 90 | parse_header(wpk_file, &wpkfile); 91 | parse_offsets(wpk_file, &wpkfile); 92 | parse_data(wpk_file, &wpkfile); 93 | fclose(wpk_file); 94 | 95 | AudioDataList audio_data_list; 96 | initialize_list_size(&audio_data_list, wpkfile.file_count); 97 | for (uint32_t i = 0; i < wpkfile.file_count; i++) { 98 | add_object(&audio_data_list, (&(AudioData) {.id = strtoul(wpkfile.wpk_file_entries[i].filename, NULL, 10), .data = {.length = wpkfile.wpk_file_entries[i].data_length, .data = wpkfile.wpk_file_entries[i].data}})); 99 | } 100 | 101 | extract_all_audio(output_path, &audio_data_list, string_hashes, wems_only, oggs_only, alternate_filenames); 102 | 103 | for (uint32_t i = 0; i < wpkfile.file_count; i++) { 104 | free(wpkfile.wpk_file_entries[i].filename); 105 | free(wpkfile.wpk_file_entries[i].data); 106 | } 107 | free(wpkfile.offsets); 108 | free(wpkfile.wpk_file_entries); 109 | free(audio_data_list.objects); 110 | } 111 | -------------------------------------------------------------------------------- /bin.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "defs.h" 8 | #include "bin.h" 9 | 10 | uint32_t fnv_1_hash(const char* input) 11 | { 12 | uint32_t hash = 0x811c9dc5; 13 | const char* c = input; 14 | while (*c != 0) { 15 | hash *= 0x01000193; 16 | hash ^= (*c > 64 && *c < 91) ? *c + 32 : *c; 17 | c++; 18 | } 19 | 20 | return hash; 21 | } 22 | 23 | // format: uint16 length, then (length) bytes string (not null-terminated). 24 | static char* read_string(const uint8_t* buffer, int buffer_size, uint16_t* out_string_length) { 25 | if (buffer_size < 2) return NULL; 26 | 27 | uint16_t string_length = *(uint16_t*) buffer; 28 | if (out_string_length) *out_string_length = string_length; 29 | if (buffer_size < 2 + string_length) return NULL; 30 | 31 | char* string = malloc(string_length + 1); 32 | memcpy(string, buffer + 2, string_length); 33 | string[string_length] = '\0'; 34 | 35 | return string; 36 | } 37 | 38 | #ifndef _DEFAULT_SOURCE 39 | static uint8_t* memmem(const uint8_t* haystack, size_t haystackLen, const uint8_t* needle, size_t needleLen) 40 | { 41 | if (needleLen > haystackLen) return NULL; 42 | 43 | const uint8_t* max_start = haystack + haystackLen - needleLen; 44 | 45 | for (const uint8_t* current = haystack; current <= max_start; current++) { 46 | if (memcmp(current, needle, needleLen) == 0) { 47 | return (uint8_t*) current; 48 | } 49 | } 50 | 51 | return NULL; 52 | } 53 | #endif 54 | 55 | StringHashes* parse_bin_file(char* bin_path) 56 | { 57 | FILE* bin_file = fopen(bin_path, "rb"); 58 | if (!bin_file) { 59 | eprintf("Error: Failed to open \"%s\".\n", bin_path); 60 | exit(EXIT_FAILURE); 61 | } 62 | 63 | StringHashes* saved_strings = malloc(sizeof(StringHashes)); 64 | initialize_list(saved_strings); 65 | 66 | fseek(bin_file, 0, SEEK_END); 67 | size_t bin_size = ftell(bin_file); 68 | rewind(bin_file); 69 | 70 | uint8_t* buffer = malloc(bin_size); 71 | assert(fread(buffer, 1, bin_size, bin_file) == bin_size); 72 | fclose(bin_file); 73 | 74 | #define BUFFER_REMAINING (buffer + bin_size - current_position) 75 | 76 | // search for 'events' containers 77 | { 78 | uint8_t to_search[] = { 0x84, 0xE3, 0xD8, 0x12, 0x80, 0x10 }; 79 | uint8_t* current_position = buffer; 80 | while (1) { 81 | uint8_t* found = memmem(current_position, BUFFER_REMAINING, to_search, sizeof(to_search)); 82 | if (!found) break; 83 | 84 | current_position = found + sizeof(to_search); 85 | 86 | if (BUFFER_REMAINING < 8) goto error; 87 | current_position += 4; // skip object size 88 | uint32_t amount = *(uint32_t*) current_position; 89 | current_position += 4; 90 | dprintf("event amount: %u\n", amount); 91 | 92 | for (uint32_t i = 0; i < amount; i++) { 93 | uint16_t string_length = 0; 94 | char* string = read_string(current_position, BUFFER_REMAINING, &string_length); 95 | if (!string) goto error; 96 | struct string_hash new_pair = { 97 | .string = string, 98 | .hash = fnv_1_hash(string) 99 | }; 100 | dprintf("saved string \"%s\"\n", string); 101 | add_object(saved_strings, &new_pair); 102 | current_position += 2 + string_length; 103 | } 104 | }} 105 | 106 | // search for 'music' embedded objects 107 | { 108 | uint8_t to_search[] = { 0xD4, 0x4F, 0x9C, 0x9F, 0x83 }; 109 | uint8_t* current_position = buffer; 110 | while (1) { 111 | uint8_t* found = memmem(current_position, BUFFER_REMAINING, to_search, sizeof(to_search)); 112 | if (!found) break; 113 | 114 | current_position = found + sizeof(to_search); 115 | 116 | if (BUFFER_REMAINING < 4) goto error; 117 | uint32_t type_hash = *(uint32_t*) current_position; 118 | current_position += 4; 119 | if (!type_hash) { 120 | continue; 121 | } 122 | 123 | if (BUFFER_REMAINING < 6) goto error; 124 | current_position += 4; // skip object size 125 | uint16_t amount = *(uint16_t*) current_position; 126 | current_position += 2; 127 | dprintf("music amount: %u\n", amount); 128 | 129 | for (uint16_t i = 0; i < amount; i++) { 130 | if (BUFFER_REMAINING < 5) goto error; 131 | current_position += 4; // skip name (hash) 132 | uint8_t bin_type = *current_position; 133 | if (bin_type != 0x10 /* string */) goto error; 134 | current_position++; 135 | 136 | uint16_t string_length = 0; 137 | char* string = read_string(current_position, BUFFER_REMAINING, &string_length); 138 | if (!string) goto error; 139 | struct string_hash new_pair = { 140 | .string = string, 141 | .hash = fnv_1_hash(string) 142 | }; 143 | dprintf("saved string \"%s\"\n", string); 144 | add_object(saved_strings, &new_pair); 145 | current_position += 2 + string_length; 146 | } 147 | }} 148 | 149 | free(buffer); 150 | 151 | for (uint32_t i = 0; i < saved_strings->length; i++) { 152 | dprintf("string at position %u: \"%s\".\n", i, saved_strings->objects[i].string); 153 | } 154 | 155 | return saved_strings; 156 | 157 | error: 158 | eprintf("Error: Malformed/truncated bin file!\n"); 159 | exit(EXIT_FAILURE); 160 | } 161 | -------------------------------------------------------------------------------- /ww2ogg/ww2ogg.cpp: -------------------------------------------------------------------------------- 1 | #define __STDC_CONSTANT_MACROS 2 | #include 3 | #include "wwriff.hpp" 4 | #include "stdint.h" 5 | #include "errors.hpp" 6 | #include "../defs.h" 7 | #include "../general_utils.h" 8 | 9 | using namespace std; 10 | 11 | class ww2ogg_options 12 | { 13 | BinaryData* in_filedata; 14 | string in_filename; 15 | string out_filename; 16 | string codebooks_filename; 17 | bool inline_codebooks; 18 | bool full_setup; 19 | ForcePacketFormat force_packet_format; 20 | public: 21 | ww2ogg_options(void) : in_filename(""), 22 | out_filename(""), 23 | codebooks_filename("packed_codebooks.bin"), 24 | inline_codebooks(false), 25 | full_setup(false), 26 | force_packet_format(kNoForcePacketFormat) 27 | {} 28 | void parse_args(int argc, char **argv); 29 | const BinaryData& get_in_filedata(void) const {return *in_filedata;} 30 | const string& get_in_filename(void) const {return in_filename;} 31 | const string& get_out_filename(void) const {return out_filename;} 32 | const string& get_codebooks_filename(void) const {return codebooks_filename;} 33 | bool get_inline_codebooks(void) const {return inline_codebooks;} 34 | bool get_full_setup(void) const {return full_setup;} 35 | ForcePacketFormat get_force_packet_format(void) const {return force_packet_format;} 36 | }; 37 | 38 | void usage(void) 39 | { 40 | fprintf(stderr, "usage: ww2ogg input.wav [-o output.ogg] [--inline-codebooks] [--full-setup]\n" 41 | " [--mod-packets | --no-mod-packets]\n" 42 | " [--pcb packed_codebooks.bin]\n\n"); 43 | } 44 | 45 | extern "C" BinaryData* ww2ogg(int argc, char **argv) 46 | { 47 | // cout << "Audiokinetic Wwise RIFF/RIFX Vorbis to Ogg Vorbis converter " VERSION " by hcs" << endl << endl; 48 | 49 | ww2ogg_options opt; 50 | 51 | try 52 | { 53 | opt.parse_args(argc, argv); 54 | } 55 | catch (const Argument_error& ae) 56 | { 57 | ae.print(stderr); 58 | 59 | usage(); 60 | return NULL; 61 | } 62 | 63 | BinaryData* ogg_data = (BinaryData*) calloc(1, sizeof(BinaryData)); 64 | 65 | try { 66 | Wwise_RIFF_Vorbis ww(opt.get_in_filedata(), 67 | opt.get_codebooks_filename(), 68 | opt.get_inline_codebooks(), 69 | opt.get_full_setup(), 70 | opt.get_force_packet_format() 71 | ); 72 | 73 | ww.generate_ogg(*ogg_data); 74 | } catch (const Parse_error& pe) { 75 | pe.print(stderr); 76 | free(ogg_data); 77 | return NULL; 78 | } 79 | 80 | return ogg_data; 81 | } 82 | 83 | void ww2ogg_options::parse_args(int argc, char ** argv) 84 | { 85 | bool set_input = false, set_output = false; 86 | for (int i = 1; i < argc; i++) 87 | { 88 | if (!strcmp(argv[i], "--binarydata")) 89 | { 90 | if (i+1 >= argc) 91 | { 92 | throw Argument_error("--binarydata needs an option"); 93 | } 94 | 95 | hex2bytes(argv[++i], &in_filedata, 16); 96 | } 97 | if (!strcmp(argv[i], "-o")) 98 | { 99 | // switch for output file name 100 | if (i+1 >= argc) 101 | { 102 | throw Argument_error("-o needs an option"); 103 | } 104 | 105 | if (set_output) 106 | { 107 | throw Argument_error("only one output file at a time"); 108 | } 109 | 110 | out_filename = argv[++i]; 111 | set_output = true; 112 | } 113 | else if (!strcmp(argv[i], "--inline-codebooks")) 114 | { 115 | // switch for inline codebooks 116 | inline_codebooks = true; 117 | } 118 | else if (!strcmp(argv[i], "--full-setup")) 119 | { 120 | // early version with setup almost entirely intact 121 | full_setup = true; 122 | inline_codebooks = true; 123 | } 124 | else if (!strcmp(argv[i], "--mod-packets") || !strcmp(argv[i], "--no-mod-packets")) 125 | { 126 | if (force_packet_format != kNoForcePacketFormat) 127 | { 128 | throw Argument_error("only one of --mod-packets or --no-mod-packets is allowed"); 129 | } 130 | 131 | if (!strcmp(argv[i], "--mod-packets")) 132 | { 133 | force_packet_format = kForceModPackets; 134 | } 135 | else 136 | { 137 | force_packet_format = kForceNoModPackets; 138 | } 139 | } 140 | else if (!strcmp(argv[i], "--pcb")) 141 | { 142 | // override default packed codebooks file 143 | if (i+1 >= argc) 144 | { 145 | throw Argument_error("--pcb needs an option"); 146 | } 147 | 148 | codebooks_filename = argv[++i]; 149 | } 150 | else 151 | { 152 | // assume anything else is an input file name 153 | if (set_input) 154 | { 155 | throw Argument_error("only one input file at a time"); 156 | } 157 | 158 | in_filename = argv[i]; 159 | set_input = true; 160 | } 161 | } 162 | 163 | if (!set_input) 164 | { 165 | throw Argument_error("input name not specified"); 166 | } 167 | 168 | if (!set_output) 169 | { 170 | size_t found = in_filename.find_last_of('.'); 171 | 172 | out_filename = in_filename.substr(0, found); 173 | out_filename.append(".ogg"); 174 | 175 | // TODO: should be case insensitive for Windows 176 | if (out_filename == in_filename) 177 | { 178 | out_filename.append("_conv.ogg"); 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H 2 | #define LIST_H 3 | 4 | #include 5 | #include 6 | #include "defs.h" 7 | 8 | #define LIST(type) struct { \ 9 | uint32_t length; \ 10 | uint32_t allocated_length; \ 11 | type* objects; \ 12 | } 13 | 14 | #define initialize_list(list) do { \ 15 | (list)->length = 0; \ 16 | (list)->allocated_length = 16; \ 17 | (list)->objects = malloc(16 * sizeof((list)->objects[0])); \ 18 | } while (0) 19 | 20 | #define initialize_list_size(list, size) do { \ 21 | (list)->length = 0; \ 22 | (list)->allocated_length = size; \ 23 | (list)->objects = malloc((size) * sizeof((list)->objects[0])); \ 24 | } while (0) 25 | 26 | #define add_object(list, object) do { \ 27 | if ((list)->length == (list)->allocated_length) { \ 28 | (list)->objects = realloc((list)->objects, ((list)->allocated_length + ((list)->allocated_length >> 1)) * sizeof(*object)); \ 29 | (list)->allocated_length += (list)->allocated_length >> 1; \ 30 | } \ 31 | \ 32 | (list)->objects[(list)->length] = *object; \ 33 | (list)->length++; \ 34 | } while (0) 35 | 36 | #define remove_object(list, index) do { \ 37 | (list)->objects[index] = (list)->objects[(list)->length - 1]; \ 38 | (list)->length--; \ 39 | if ((list)->allocated_length != 16 && (list)->length == (list)->allocated_length >> 1) { \ 40 | (list)->objects = realloc((list)->objects, max((list)->length + ((list)->length >> 1), (uint32_t) 16) * sizeof(typeof((list)->objects[0]))); \ 41 | } \ 42 | } while (0) 43 | 44 | #define add_objects(list, position, amount) do { \ 45 | if ((list)->allocated_length - ((list)->length) < (amount)) { \ 46 | (list)->objects = realloc((list)->objects, ((list)->length + (amount)) * sizeof((list)->objects[0])); \ 47 | (list)->allocated_length = (list)->length + (amount); \ 48 | } \ 49 | \ 50 | memcpy(&(list)->objects[(list)->length], position, (amount) * sizeof((list)->objects[0])); \ 51 | (list)->length += amount; \ 52 | } while (0) 53 | 54 | #define find_object_s(list, out_object, key, value) do { \ 55 | if ((list)->length != 0) { \ 56 | uint32_t position = (list)->length / 2; \ 57 | for (int step = position / 2; step > 2; step /= 2) { \ 58 | if ((list)->objects[position].key > (value)) \ 59 | position -= step; \ 60 | else \ 61 | position += step; \ 62 | } \ 63 | while (position > 0 && (list)->objects[position].key > (value)) \ 64 | position--; \ 65 | while (position < (list)->length-1 && (list)->objects[position].key < (value)) \ 66 | position++; \ 67 | if ((list)->objects[position].key == (value)) \ 68 | (out_object) = &(list)->objects[position]; \ 69 | } \ 70 | } while (0) 71 | 72 | 73 | #define add_object_s(list, object, key) do { \ 74 | if ((list)->length == 0) { \ 75 | (list)->objects[(list)->length] = *object; \ 76 | (list)->length++; \ 77 | } else { \ 78 | if ((list)->length == (list)->allocated_length) { \ 79 | (list)->objects = realloc((list)->objects, ((list)->allocated_length + ((list)->allocated_length >> 1)) * sizeof(*object)); \ 80 | (list)->allocated_length += (list)->allocated_length >> 1; \ 81 | } \ 82 | \ 83 | uint32_t position = (list)->length / 2; \ 84 | for (int step = position / 2; step > 2; step /= 2) { \ 85 | if ((list)->objects[position].key > (object)->key) \ 86 | position -= step; \ 87 | else \ 88 | position += step; \ 89 | } \ 90 | while (position > 0 && (list)->objects[position].key > (object)->key) \ 91 | position--; \ 92 | while (position < (list)->length && (list)->objects[position].key < (object)->key) \ 93 | position++; \ 94 | if (position < (list)->length) \ 95 | memmove(&(list)->objects[position + 1], &(list)->objects[position], ((list)->length - position) * sizeof(*object)); \ 96 | (list)->objects[position] = *object; \ 97 | (list)->length++; \ 98 | } \ 99 | } while (0) 100 | 101 | #define sort_list(list, key) do { \ 102 | int n = (list)->length; \ 103 | __typeof__(list) temp = malloc(sizeof(__typeof__(*list))); \ 104 | temp->length = (list)->length; temp->allocated_length = (list)->allocated_length; \ 105 | temp->objects = malloc((list)->allocated_length * sizeof(__typeof__((list)->objects[0]))); \ 106 | memcpy(temp->objects, (list)->objects, (list)->length * sizeof(__typeof__((list)->objects[0]))); \ 107 | int parity = 0; \ 108 | for (int width = 1; width < n; width = 2 * width) { \ 109 | parity++; \ 110 | for (int i = 0; i < n; i = i + 2 * width) { \ 111 | if (parity % 2 == 0) \ 112 | sortHelper(list, i, min(i+width, n), min(i+2*width, n), temp, key); \ 113 | else \ 114 | sortHelper(temp, i, min(i+width, n), min(i+2*width, n), list, key); \ 115 | } \ 116 | } \ 117 | if (parity % 2 == 0) { \ 118 | free((list)->objects); \ 119 | (list)->objects = temp->objects; \ 120 | free(temp); \ 121 | } else { \ 122 | free(temp->objects); \ 123 | free(temp); \ 124 | } \ 125 | } while (0) 126 | 127 | #define sortHelper(listLeft, iLeft, iRight, iEnd, listRight, key) do { \ 128 | int ___i = iLeft, ___j = iRight; \ 129 | for (int ___k = iLeft; ___k < iEnd; ___k++) { \ 130 | if (___i < iRight && (___j >= iEnd || (listLeft)->objects[___i].key <= (listLeft)->objects[___j].key)) { \ 131 | (listRight)->objects[___k] = (listLeft)->objects[___i]; \ 132 | ___i++; \ 133 | } else { \ 134 | (listRight)->objects[___k] = (listLeft)->objects[___j]; \ 135 | ___j++; \ 136 | } \ 137 | } \ 138 | } while (0) 139 | 140 | typedef LIST(uint8_t) uint8_list; 141 | typedef LIST(uint16_t) uint16_list; 142 | typedef LIST(uint32_t) uint32_list; 143 | typedef LIST(uint64_t) uint64_list; 144 | #ifdef __SIZEOF_INT128__ 145 | typedef LIST(__uint128_t) uint128_list; 146 | #endif 147 | 148 | #endif 149 | -------------------------------------------------------------------------------- /extract.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #ifdef _WIN32 7 | # include 8 | #else 9 | # include 10 | #endif 11 | 12 | #include "bin.h" 13 | #include "extract.h" 14 | #include "defs.h" 15 | #include "general_utils.h" 16 | #include "ww2ogg/api.h" 17 | #include "revorb/api.h" 18 | 19 | void hardlink_file(char* existing_path, char* new_path, uint8_t type) 20 | { 21 | if (type & 1) { 22 | if (link(existing_path, new_path) != 0 && errno != EEXIST) { 23 | eprintf("Error: Failed hard-linking \"%s\" to \"%s\"\n", new_path, existing_path); 24 | return; 25 | } 26 | } 27 | if (type <= 1) return; 28 | 29 | int existing_path_length = strlen(existing_path); 30 | int new_path_length = strlen(new_path); 31 | char converted_existing_path[existing_path_length+1], converted_new_path[new_path_length+1]; 32 | memcpy(converted_existing_path, existing_path, existing_path_length); 33 | memcpy(converted_existing_path + existing_path_length - 4, type & 2 ? ".ogg" : ".wav", 5); 34 | memcpy(converted_new_path, new_path, new_path_length); 35 | memcpy(converted_new_path + new_path_length - 4, type & 2 ? ".ogg" : ".wav", 5); 36 | if (link(converted_existing_path, converted_new_path) != 0 && errno != EEXIST) { 37 | eprintf("Error: Failed hard-linking \"%s\" to \"%s\"\n", converted_new_path, converted_existing_path); 38 | return; 39 | } 40 | } 41 | 42 | uint8_t extract_audio(char* output_path, BinaryData* wem_data, bool wem_only, bool ogg_only) 43 | { 44 | uint8_t type = 0; 45 | if (!ogg_only) { 46 | FILE* output_file = fopen(output_path, "wb"); 47 | if (!output_file) { 48 | eprintf("Error: Failed to open \"%s\"\n", output_path); 49 | return type; 50 | } 51 | v_printf(1, "Extracting \"%s\"\n", output_path); 52 | fwrite(wem_data->data, 1, wem_data->length, output_file); 53 | fclose(output_file); 54 | type |= 1; 55 | } 56 | 57 | if (!wem_only) { 58 | // convert the .wem to .ogg 59 | int string_length = strlen(output_path) + 1; 60 | char ogg_path[string_length]; 61 | memcpy(ogg_path, output_path, string_length); 62 | ogg_path[string_length - 5] = '\0'; 63 | 64 | v_printf(1, "Extracting \"%s", ogg_path); 65 | fflush(stdout); 66 | char data_pointer[17] = {0}; 67 | bytes2hex(&wem_data, data_pointer, 8); 68 | char* ww2ogg_args[] = {"", "--binarydata", data_pointer, NULL}; 69 | BinaryData* raw_ogg = ww2ogg(sizeof(ww2ogg_args) / sizeof(ww2ogg_args[0]) - 1, ww2ogg_args); 70 | if (!raw_ogg) 71 | return type; 72 | if (memcmp(raw_ogg->data, "RIFF", 4) == 0) { // got a wav file instead of an ogg one 73 | memcpy(&ogg_path[string_length - 5], ".wav", 5); 74 | FILE* wav_file = fopen(ogg_path, "wb"); 75 | if (!wav_file) { 76 | eprintf("Could not open output file.\n"); 77 | goto end; 78 | } 79 | fwrite(raw_ogg->data, raw_ogg->length, 1, wav_file); 80 | fclose(wav_file); 81 | type |= 4; 82 | } else { 83 | memcpy(&ogg_path[string_length - 5], ".ogg", 5); 84 | bytes2hex(&raw_ogg, data_pointer, 8); 85 | const char* revorb_args[] = {"", data_pointer, ogg_path, NULL}; 86 | if (revorb(sizeof(revorb_args) / sizeof(revorb_args[0]) - 1, revorb_args) != 0) { 87 | goto end; 88 | } 89 | type |= 2; 90 | } 91 | end: 92 | free(raw_ogg->data); 93 | free(raw_ogg); 94 | } 95 | 96 | return type; 97 | } 98 | 99 | void extract_all_audio(char* output_path, AudioDataList* audio_data, StringHashes* string_hashes, bool wems_only, bool oggs_only, bool alternate_filenames) 100 | { 101 | create_dirs(output_path, true); 102 | int output_path_length = strlen(output_path); 103 | 104 | for (uint32_t i = 0; i < audio_data->length; i++) { 105 | bool extracted = false; 106 | uint8_t extracted_type; 107 | char* initial_output_path; 108 | for (uint32_t string_index = 0; string_index < string_hashes->length; string_index++) { 109 | if (string_hashes->objects[string_index].hash == audio_data->objects[i].id) { 110 | char cur_output_path[256]; 111 | int current_position = output_path_length; 112 | strcpy(cur_output_path, output_path); 113 | if (string_hashes->objects[string_index].switch_id) 114 | current_position += sprintf(cur_output_path + current_position, "/%u", string_hashes->objects[string_index].switch_id); 115 | current_position += sprintf(cur_output_path + current_position, "/%s", string_hashes->objects[string_index].string); 116 | if (string_hashes->objects[string_index].container_id) 117 | current_position += sprintf(cur_output_path + current_position, "/%u", string_hashes->objects[string_index].container_id); 118 | if (string_hashes->objects[string_index].music_segment_id) 119 | current_position += sprintf(cur_output_path + current_position, "/%u", string_hashes->objects[string_index].music_segment_id); 120 | 121 | create_dirs(cur_output_path, true); 122 | if (alternate_filenames && !string_hashes->objects[string_index].music_segment_id) { 123 | sprintf(cur_output_path + current_position, "/%s_%u.wem", string_hashes->objects[string_index].string, string_hashes->objects[string_index].sound_index); 124 | } else { 125 | sprintf(cur_output_path + current_position, "/%u.wem", audio_data->objects[i].id); 126 | } 127 | 128 | if (extracted) { 129 | hardlink_file(initial_output_path, cur_output_path, extracted_type); 130 | } else { 131 | extracted = true; 132 | extracted_type = extract_audio(cur_output_path, &audio_data->objects[i].data, wems_only, oggs_only); 133 | initial_output_path = strdup(cur_output_path); 134 | } 135 | } 136 | } 137 | if (!extracted) { 138 | char cur_output_path[output_path_length + 16]; 139 | sprintf(cur_output_path, "%s/%u.wem", output_path, audio_data->objects[i].id); 140 | extract_audio(cur_output_path, &audio_data->objects[i].data, wems_only, oggs_only); 141 | } else { 142 | free(initial_output_path); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /revorb/revorb.c: -------------------------------------------------------------------------------- 1 | /* 2 | * REVORB - Recomputes page granule positions in Ogg Vorbis files. 3 | * version 0.3 (2015/09/03) - Jon Boydell - version for *NIX systems 4 | * version 0.2 (2008/06/29) 5 | * 6 | * Copyright (c) 2008, Jiri Hruska 7 | * 8 | * Permission to use, copy, modify, and/or distribute this software for any 9 | * purpose with or without fee is hereby granted, provided that the above 10 | * copyright notice and this permission notice appear in all copies. 11 | * 12 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 | */ 20 | 21 | /*# INCLUDE=.\include #*/ 22 | /*# LIB=.\lib #*/ 23 | /*# CFLAGS=/D_UNICODE #*/ 24 | /*# LFLAGS=/NODEFAULTLIB:MSVCRT /LTCG /OPT:REF /MANIFEST:NO #*/ 25 | 26 | #ifdef __APPLE__ 27 | #include 28 | #else 29 | #include 30 | #endif 31 | #include 32 | #include "stdlib.h" 33 | #include 34 | #include 35 | #include 36 | #include "../defs.h" 37 | #include "../general_utils.h" 38 | 39 | bool g_failed; 40 | 41 | uint32_t copy_headers(BinaryData* file_data, ogg_sync_state *si, ogg_stream_state *is, 42 | FILE *fo, ogg_stream_state *os, vorbis_info *vi) 43 | { 44 | char *buffer = ogg_sync_buffer(si, 4096); 45 | uint32_t numread = min(file_data->length, 4096u); 46 | memcpy(buffer, file_data->data, numread); 47 | uint32_t file_pos = numread; 48 | ogg_sync_wrote(si, numread); 49 | 50 | ogg_page page; 51 | if (ogg_sync_pageout(si, &page) != 1) { 52 | eprintf("Input is not an Ogg.\n"); 53 | return false; 54 | } 55 | 56 | ogg_stream_init(is, ogg_page_serialno(&page)); 57 | ogg_stream_init(os, ogg_page_serialno(&page)); 58 | 59 | if (ogg_stream_pagein(is,&page) < 0) { 60 | eprintf("Error in the first page.\n"); 61 | ogg_stream_clear(is); 62 | ogg_stream_clear(os); 63 | return false; 64 | } 65 | 66 | ogg_packet packet; 67 | if (ogg_stream_packetout(is,&packet) != 1) { 68 | eprintf("Error in the first packet.\n"); 69 | ogg_stream_clear(is); 70 | ogg_stream_clear(os); 71 | return false; 72 | } 73 | 74 | vorbis_comment vc; 75 | vorbis_comment_init(&vc); 76 | if (vorbis_synthesis_headerin(vi, &vc, &packet) < 0) { 77 | eprintf("Error in header, probably not a Vorbis file.\n"); 78 | vorbis_comment_clear(&vc); 79 | ogg_stream_clear(is); 80 | ogg_stream_clear(os); 81 | return false; 82 | } 83 | 84 | ogg_stream_packetin(os, &packet); 85 | 86 | int i = 0; 87 | while(i < 2) { 88 | int res = ogg_sync_pageout(si, &page); 89 | 90 | if (res == 0) { 91 | buffer = ogg_sync_buffer(si, 4096); 92 | int numread = min(file_data->length - file_pos, 4096u); 93 | memcpy(buffer, &file_data->data[file_pos], numread); 94 | file_pos += numread; 95 | if (numread == 0 && i < 2) { 96 | eprintf("Headers are damaged, file is probably truncated.\n"); 97 | ogg_stream_clear(is); 98 | ogg_stream_clear(os); 99 | return false; 100 | } 101 | ogg_sync_wrote(si, 4096); 102 | continue; 103 | } 104 | 105 | if (res == 1) { 106 | ogg_stream_pagein(is, &page); 107 | while(i < 2) { 108 | res = ogg_stream_packetout(is, &packet); 109 | if (res == 0) 110 | break; 111 | if (res < 0) { 112 | eprintf("Secondary header is corrupted.\n"); 113 | vorbis_comment_clear(&vc); 114 | ogg_stream_clear(is); 115 | ogg_stream_clear(os); 116 | return false; 117 | } 118 | vorbis_synthesis_headerin(vi, &vc, &packet); 119 | ogg_stream_packetin(os, &packet); 120 | i++; 121 | } 122 | } 123 | } 124 | 125 | vorbis_comment_clear(&vc); 126 | 127 | while(ogg_stream_flush(os,&page)) { 128 | if (fwrite(page.header, 1, page.header_len, fo) != (size_t) page.header_len || fwrite(page.body, 1, page.body_len, fo) != (size_t) page.body_len) { 129 | fprintf(stderr,"Cannot write headers to output.\n"); 130 | ogg_stream_clear(is); 131 | ogg_stream_clear(os); 132 | return false; 133 | } 134 | } 135 | 136 | return file_pos; 137 | } 138 | 139 | int revorb(int argc, const char **argv) 140 | { 141 | if (argc < 2) { 142 | eprintf("-= REVORB - 2008/06/29 =-\n"); 143 | eprintf("Recomputes page granule positions in Ogg Vorbis files.\n"); 144 | eprintf("Usage:\n"); 145 | eprintf(" revorb [output.ogg]\n"); 146 | return 1; 147 | } 148 | 149 | FILE *fo; 150 | fo = fopen(argv[2], "wb"); 151 | if (!fo) { 152 | eprintf("Could not open output file.\n"); 153 | return 2; 154 | } 155 | 156 | ogg_sync_state sync_in; 157 | ogg_sync_init(&sync_in); 158 | 159 | ogg_stream_state stream_in, stream_out; 160 | vorbis_info vi; 161 | vorbis_info_init(&vi); 162 | 163 | ogg_packet packet; 164 | ogg_page page; 165 | 166 | BinaryData* file_data; 167 | hex2bytes(argv[1], &file_data, 16); 168 | uint32_t file_pos; 169 | if ( (file_pos = copy_headers(file_data, &sync_in, &stream_in, fo, &stream_out, &vi)) ) { 170 | ogg_int64_t granpos = 0, packetnum = 0; 171 | int lastbs = 0; 172 | 173 | while(1) { 174 | 175 | int eos = 0; 176 | while(!eos) { 177 | int res = ogg_sync_pageout(&sync_in, &page); 178 | if (res == 0) { 179 | char *buffer = ogg_sync_buffer(&sync_in, 4096); 180 | int numread = min(file_data->length - file_pos, 4096u); 181 | memcpy(buffer, &file_data->data[file_pos], numread); 182 | file_pos += numread; 183 | if (numread > 0) 184 | ogg_sync_wrote(&sync_in, numread); 185 | else 186 | eos = 2; 187 | continue; 188 | } 189 | 190 | if (res < 0) { 191 | eprintf("Warning: Corrupted or missing data in bitstream.\n"); 192 | g_failed = true; 193 | } else { 194 | if (ogg_page_eos(&page)) 195 | eos = 1; 196 | ogg_stream_pagein(&stream_in,&page); 197 | 198 | while(1) { 199 | res = ogg_stream_packetout(&stream_in, &packet); 200 | if (res == 0) 201 | break; 202 | if (res < 0) { 203 | eprintf("Warning: Bitstream error.\n"); 204 | g_failed = true; 205 | continue; 206 | } 207 | 208 | /* 209 | if (packet.granulepos >= 0) { 210 | granpos = packet.granulepos + logstream_startgran; 211 | packet.granulepos = granpos; 212 | } 213 | */ 214 | int bs = vorbis_packet_blocksize(&vi, &packet); 215 | if (lastbs) 216 | granpos += (lastbs+bs) / 4; 217 | lastbs = bs; 218 | 219 | packet.granulepos = granpos; 220 | packet.packetno = packetnum++; 221 | if (!packet.e_o_s) { 222 | ogg_stream_packetin(&stream_out, &packet); 223 | 224 | ogg_page opage; 225 | while(ogg_stream_pageout(&stream_out, &opage)) { 226 | if (fwrite(opage.header, 1, opage.header_len, fo) != (size_t) opage.header_len || fwrite(opage.body, 1, opage.body_len, fo) != (size_t) opage.body_len) { 227 | eprintf("Unable to write page to output.\n"); 228 | eos = 2; 229 | g_failed = true; 230 | break; 231 | } 232 | } 233 | } 234 | } 235 | } 236 | } 237 | 238 | if (eos == 2) 239 | break; 240 | 241 | { 242 | packet.e_o_s = 1; 243 | ogg_stream_packetin(&stream_out, &packet); 244 | ogg_page opage; 245 | while(ogg_stream_flush(&stream_out, &opage)) { 246 | if (fwrite(opage.header, 1, opage.header_len, fo) != (size_t) opage.header_len || fwrite(opage.body, 1, opage.body_len, fo) != (size_t) opage.body_len) { 247 | eprintf("Unable to write page to output.\n"); 248 | g_failed = true; 249 | break; 250 | } 251 | } 252 | ogg_stream_clear(&stream_in); 253 | break; 254 | } 255 | } 256 | 257 | ogg_stream_clear(&stream_out); 258 | } else { 259 | g_failed = true; 260 | } 261 | 262 | vorbis_info_clear(&vi); 263 | 264 | ogg_sync_clear(&sync_in); 265 | 266 | fclose(fo); 267 | 268 | return 0; 269 | } 270 | -------------------------------------------------------------------------------- /ww2ogg/codebook.cpp: -------------------------------------------------------------------------------- 1 | #define __STDC_CONSTANT_MACROS 2 | #include "codebook.hpp" 3 | #include 4 | #include "../defs.h" 5 | #include 6 | 7 | codebook_library::codebook_library(void) 8 | : codebook_data(NULL), codebook_offsets(NULL), codebook_count(0) 9 | { } 10 | 11 | codebook_library::codebook_library(unsigned char* data, int length) 12 | : codebook_data(NULL), codebook_offsets(NULL), codebook_count(0) 13 | { 14 | int offset_offset = read_32_le(&data[length - 4]); 15 | codebook_count = (length - offset_offset) / 4; 16 | 17 | codebook_data = new char[offset_offset]; 18 | codebook_offsets = new uint32_t[codebook_count]; 19 | 20 | memcpy(codebook_data, data, offset_offset); 21 | memcpy(codebook_offsets, &data[offset_offset], codebook_count * 4); 22 | } 23 | 24 | codebook_library::codebook_library(const string& filename) 25 | : codebook_data(NULL), codebook_offsets(NULL), codebook_count(0) 26 | { 27 | FILE* is = fopen(filename.c_str(), "rb"); 28 | 29 | if (!is) throw File_open_error(filename); 30 | 31 | fseek(is, 0, SEEK_END); 32 | long file_size = ftell(is); 33 | 34 | fseek(is, file_size - 4, SEEK_SET); 35 | long offset_offset = read_32_le(is); 36 | codebook_count = (file_size - offset_offset) / 4; 37 | 38 | codebook_data = new char [offset_offset]; 39 | codebook_offsets = new uint32_t [codebook_count]; 40 | 41 | fseek(is, 0, SEEK_SET); 42 | assert(fread(codebook_data, 1, offset_offset, is) == (size_t) offset_offset); 43 | 44 | for (long i = 0; i < codebook_count; i++) 45 | { 46 | codebook_offsets[i] = read_32_le(is); 47 | } 48 | 49 | fclose(is); 50 | } 51 | 52 | void codebook_library::rebuild(int i, Bit_oggstream& bos) 53 | { 54 | const char * cb = get_codebook(i); 55 | unsigned long cb_size; 56 | 57 | { 58 | long signed_cb_size = get_codebook_size(i); 59 | 60 | if (!cb || -1 == signed_cb_size) throw Invalid_id(i); 61 | 62 | cb_size = signed_cb_size; 63 | } 64 | 65 | BinaryData bd = (BinaryData) {.length = cb_size, .data = (uint8_t*) cb}; 66 | Bit_stream bis(bd); 67 | 68 | rebuild(bis, cb_size, bos); 69 | } 70 | 71 | /* cb_size == 0 to not check size (for an inline bitstream) */ 72 | void codebook_library::copy(Bit_stream &bis, Bit_oggstream& bos) 73 | { 74 | /* IN: 24 bit identifier, 16 bit dimensions, 24 bit entry count */ 75 | 76 | Bit_uint<24> id; 77 | Bit_uint<16> dimensions; 78 | Bit_uint<24> entries; 79 | 80 | bis >> id >> dimensions >> entries; 81 | 82 | if (0x564342 != id) 83 | { 84 | throw Parse_error_str("invalid codebook identifier"); 85 | } 86 | 87 | //cout << "Codebook with " << dimensions << " dimensions, " << entries << " entries" << endl; 88 | 89 | /* OUT: 24 bit identifier, 16 bit dimensions, 24 bit entry count */ 90 | bos << id << Bit_uint<16>(dimensions) << Bit_uint<24>(entries); 91 | 92 | // gather codeword lengths 93 | 94 | /* IN/OUT: 1 bit ordered flag */ 95 | Bit_uint<1> ordered; 96 | bis >> ordered; 97 | bos << ordered; 98 | if (ordered) 99 | { 100 | //cout << "Ordered " << endl; 101 | 102 | /* IN/OUT: 5 bit initial length */ 103 | Bit_uint<5> initial_length; 104 | bis >> initial_length; 105 | bos << initial_length; 106 | 107 | unsigned int current_entry = 0; 108 | while (current_entry < entries) 109 | { 110 | /* IN/OUT: ilog(entries-current_entry) bit count w/ given length */ 111 | Bit_uintv number(ilog(entries-current_entry)); 112 | bis >> number; 113 | bos << number; 114 | current_entry += number; 115 | } 116 | if (current_entry > entries) throw Parse_error_str("current_entry out of range"); 117 | } 118 | else 119 | { 120 | /* IN/OUT: 1 bit sparse flag */ 121 | Bit_uint<1> sparse; 122 | bis >> sparse; 123 | bos << sparse; 124 | 125 | //cout << "Unordered, "; 126 | 127 | //if (sparse) 128 | //{ 129 | // cout << "Sparse" << endl; 130 | //} 131 | //else 132 | //{ 133 | // cout << "Nonsparse" << endl; 134 | //} 135 | 136 | for (unsigned int i = 0; i < entries; i++) 137 | { 138 | bool present_bool = true; 139 | 140 | if (sparse) 141 | { 142 | /* IN/OUT 1 bit sparse presence flag */ 143 | Bit_uint<1> present; 144 | bis >> present; 145 | bos << present; 146 | 147 | present_bool = (0 != present); 148 | } 149 | 150 | if (present_bool) 151 | { 152 | /* IN/OUT: 5 bit codeword length-1 */ 153 | Bit_uint<5> codeword_length; 154 | bis >> codeword_length; 155 | bos << codeword_length; 156 | } 157 | } 158 | } // done with lengths 159 | 160 | 161 | // lookup table 162 | 163 | /* IN/OUT: 4 bit lookup type */ 164 | Bit_uint<4> lookup_type; 165 | bis >> lookup_type; 166 | bos << lookup_type; 167 | 168 | if (0 == lookup_type) 169 | { 170 | //cout << "no lookup table" << endl; 171 | } 172 | else if (1 == lookup_type) 173 | { 174 | //cout << "lookup type 1" << endl; 175 | 176 | /* IN/OUT: 32 bit minimum length, 32 bit maximum length, 4 bit value length-1, 1 bit sequence flag */ 177 | Bit_uint<32> min, max; 178 | Bit_uint<4> value_length; 179 | Bit_uint<1> sequence_flag; 180 | bis >> min >> max >> value_length >> sequence_flag; 181 | bos << min << max << value_length << sequence_flag; 182 | 183 | unsigned int quantvals = _book_maptype1_quantvals(entries, dimensions); 184 | for (unsigned int i = 0; i < quantvals; i++) 185 | { 186 | /* IN/OUT: n bit value */ 187 | Bit_uintv val(value_length+1); 188 | bis >> val; 189 | bos << val; 190 | } 191 | } 192 | else if (2 == lookup_type) 193 | { 194 | throw Parse_error_str("didn't expect lookup type 2"); 195 | } 196 | else 197 | { 198 | throw Parse_error_str("invalid lookup type"); 199 | } 200 | 201 | //cout << "total bits read = " << bis.get_total_bits_read() << endl; 202 | } 203 | 204 | /* cb_size == 0 to not check size (for an inline bitstream) */ 205 | void codebook_library::rebuild(Bit_stream &bis, unsigned long cb_size, Bit_oggstream& bos) 206 | { 207 | /* IN: 4 bit dimensions, 14 bit entry count */ 208 | 209 | Bit_uint<4> dimensions; 210 | Bit_uint<14> entries; 211 | 212 | bis >> dimensions >> entries; 213 | 214 | //cout << "Codebook " << i << ", " << dimensions << " dimensions, " << entries << " entries" << endl; 215 | //cout << "Codebook with " << dimensions << " dimensions, " << entries << " entries" << endl; 216 | 217 | /* OUT: 24 bit identifier, 16 bit dimensions, 24 bit entry count */ 218 | bos << Bit_uint<24>(0x564342) << Bit_uint<16>(dimensions) << Bit_uint<24>(entries); 219 | 220 | // gather codeword lengths 221 | 222 | /* IN/OUT: 1 bit ordered flag */ 223 | Bit_uint<1> ordered; 224 | bis >> ordered; 225 | bos << ordered; 226 | if (ordered) 227 | { 228 | //cout << "Ordered " << endl; 229 | 230 | /* IN/OUT: 5 bit initial length */ 231 | Bit_uint<5> initial_length; 232 | bis >> initial_length; 233 | bos << initial_length; 234 | 235 | unsigned int current_entry = 0; 236 | while (current_entry < entries) 237 | { 238 | /* IN/OUT: ilog(entries-current_entry) bit count w/ given length */ 239 | Bit_uintv number(ilog(entries-current_entry)); 240 | bis >> number; 241 | bos << number; 242 | current_entry += number; 243 | } 244 | if (current_entry > entries) throw Parse_error_str("current_entry out of range"); 245 | } 246 | else 247 | { 248 | /* IN: 3 bit codeword length length, 1 bit sparse flag */ 249 | Bit_uint<3> codeword_length_length; 250 | Bit_uint<1> sparse; 251 | bis >> codeword_length_length >> sparse; 252 | 253 | //cout << "Unordered, " << codeword_length_length << " bit lengths, "; 254 | 255 | if (0 == codeword_length_length || 5 < codeword_length_length) 256 | { 257 | throw Parse_error_str("nonsense codeword length"); 258 | } 259 | 260 | /* OUT: 1 bit sparse flag */ 261 | bos << sparse; 262 | //if (sparse) 263 | //{ 264 | // cout << "Sparse" << endl; 265 | //} 266 | //else 267 | //{ 268 | // cout << "Nonsparse" << endl; 269 | //} 270 | 271 | for (unsigned int i = 0; i < entries; i++) 272 | { 273 | bool present_bool = true; 274 | 275 | if (sparse) 276 | { 277 | /* IN/OUT 1 bit sparse presence flag */ 278 | Bit_uint<1> present; 279 | bis >> present; 280 | bos << present; 281 | 282 | present_bool = (0 != present); 283 | } 284 | 285 | if (present_bool) 286 | { 287 | /* IN: n bit codeword length-1 */ 288 | Bit_uintv codeword_length(codeword_length_length); 289 | bis >> codeword_length; 290 | 291 | /* OUT: 5 bit codeword length-1 */ 292 | bos << Bit_uint<5>(codeword_length); 293 | } 294 | } 295 | } // done with lengths 296 | 297 | 298 | // lookup table 299 | 300 | /* IN: 1 bit lookup type */ 301 | Bit_uint<1> lookup_type; 302 | bis >> lookup_type; 303 | /* OUT: 4 bit lookup type */ 304 | bos << Bit_uint<4>(lookup_type); 305 | 306 | if (0 == lookup_type) 307 | { 308 | //cout << "no lookup table" << endl; 309 | } 310 | else if (1 == lookup_type) 311 | { 312 | //cout << "lookup type 1" << endl; 313 | 314 | /* IN/OUT: 32 bit minimum length, 32 bit maximum length, 4 bit value length-1, 1 bit sequence flag */ 315 | Bit_uint<32> min, max; 316 | Bit_uint<4> value_length; 317 | Bit_uint<1> sequence_flag; 318 | bis >> min >> max >> value_length >> sequence_flag; 319 | bos << min << max << value_length << sequence_flag; 320 | 321 | unsigned int quantvals = _book_maptype1_quantvals(entries, dimensions); 322 | for (unsigned int i = 0; i < quantvals; i++) 323 | { 324 | /* IN/OUT: n bit value */ 325 | Bit_uintv val(value_length+1); 326 | bis >> val; 327 | bos << val; 328 | } 329 | } 330 | else if (2 == lookup_type) 331 | { 332 | throw Parse_error_str("didn't expect lookup type 2"); 333 | } 334 | else 335 | { 336 | throw Parse_error_str("invalid lookup type"); 337 | } 338 | 339 | //cout << "total bits read = " << bis.get_total_bits_read() << endl; 340 | 341 | /* check that we used exactly all bytes */ 342 | /* note: if all bits are used in the last byte there will be one extra 0 byte */ 343 | if ( 0 != cb_size && bis.get_total_bits_read()/8+1 != cb_size ) 344 | { 345 | throw Size_mismatch( cb_size, bis.get_total_bits_read()/8+1 ); 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /ww2ogg/Bit_stream.hpp: -------------------------------------------------------------------------------- 1 | #ifndef _BIT_STREAM_H 2 | #define _BIT_STREAM_H 3 | 4 | #ifndef __STDC_CONSTANT_MACROS 5 | #define __STDC_CONSTANT_MACROS 6 | #endif 7 | #include 8 | #include 9 | #include 10 | 11 | #include "errors.hpp" 12 | #include "crc.h" 13 | #include "../defs.h" 14 | #include "string.h" 15 | #include 16 | 17 | // host-endian-neutral integer reading 18 | namespace { 19 | uint32_t read_32_le(unsigned char b[4]) 20 | { 21 | uint32_t v = 0; 22 | for (int i = 3; i >= 0; i--) 23 | { 24 | v <<= 8; 25 | v |= b[i]; 26 | } 27 | 28 | return v; 29 | } 30 | 31 | uint32_t read_32_le(FILE* is) 32 | { 33 | char b[4]; 34 | assert(fread(b, 4, 1, is) == 1); 35 | 36 | return read_32_le(reinterpret_cast(b)); 37 | } 38 | 39 | void write_32_le(unsigned char b[4], uint32_t v) 40 | { 41 | for (int i = 0; i < 4; i++) 42 | { 43 | b[i] = v & 0xFF; 44 | v >>= 8; 45 | } 46 | } 47 | 48 | void write_32_le(FILE* os, uint32_t v) 49 | { 50 | char b[4]; 51 | 52 | write_32_le(reinterpret_cast(b), v); 53 | 54 | fwrite(b, 4, 1, os); 55 | } 56 | 57 | uint16_t read_16_le(unsigned char b[2]) 58 | { 59 | uint16_t v = 0; 60 | for (int i = 1; i >= 0; i--) 61 | { 62 | v <<= 8; 63 | v |= b[i]; 64 | } 65 | 66 | return v; 67 | } 68 | 69 | uint16_t read_16_le(FILE* is) 70 | { 71 | char b[2]; 72 | fread(b, 2, 1, is); 73 | 74 | return read_16_le(reinterpret_cast(b)); 75 | } 76 | 77 | void write_16_le(unsigned char b[2], uint16_t v) 78 | { 79 | for (int i = 0; i < 2; i++) 80 | { 81 | b[i] = v & 0xFF; 82 | v >>= 8; 83 | } 84 | } 85 | 86 | void write_16_le(FILE* os, uint16_t v) 87 | { 88 | char b[2]; 89 | 90 | write_16_le(reinterpret_cast(b), v); 91 | 92 | fwrite(b, 2, 1, os); 93 | } 94 | 95 | uint32_t read_32_be(unsigned char b[4]) 96 | { 97 | uint32_t v = 0; 98 | for (int i = 0; i < 4; i++) 99 | { 100 | v <<= 8; 101 | v |= b[i]; 102 | } 103 | 104 | return v; 105 | } 106 | 107 | uint32_t read_32_be(FILE *is) 108 | { 109 | char b[4]; 110 | fread(b, 4, 1, is); 111 | 112 | return read_32_be(reinterpret_cast(b)); 113 | } 114 | 115 | void write_32_be(unsigned char b[4], uint32_t v) 116 | { 117 | for (int i = 3; i >= 0; i--) 118 | { 119 | b[i] = v & 0xFF; 120 | v >>= 8; 121 | } 122 | } 123 | 124 | void write_32_be(FILE *os, uint32_t v) 125 | { 126 | char b[4]; 127 | 128 | write_32_be(reinterpret_cast(b), v); 129 | 130 | fwrite(b, 4, 1, os); 131 | } 132 | 133 | uint16_t read_16_be(unsigned char b[2]) 134 | { 135 | uint16_t v = 0; 136 | for (int i = 0; i < 2; i++) 137 | { 138 | v <<= 8; 139 | v |= b[i]; 140 | } 141 | 142 | return v; 143 | } 144 | 145 | uint16_t read_16_be(FILE* is) 146 | { 147 | char b[2]; 148 | fread(b, 2, 1, is); 149 | 150 | return read_16_be(reinterpret_cast(b)); 151 | } 152 | 153 | void write_16_be(unsigned char b[2], uint16_t v) 154 | { 155 | for (int i = 1; i >= 0; i--) 156 | { 157 | b[i] = v & 0xFF; 158 | v >>= 8; 159 | } 160 | } 161 | 162 | void write_16_be(FILE* os, uint16_t v) 163 | { 164 | char b[2]; 165 | 166 | write_16_be(reinterpret_cast(b), v); 167 | 168 | fwrite(b, 2, 1, os); 169 | } 170 | 171 | } 172 | 173 | // using an istream, pull off individual bits with get_bit (LSB first) 174 | class Bit_stream { 175 | const BinaryData& bd; 176 | int initial_position; 177 | 178 | unsigned char bit_buffer; 179 | unsigned int bits_left; 180 | unsigned long total_bits_read; 181 | 182 | public: 183 | class Weird_char_size {}; 184 | class Out_of_bits {}; 185 | 186 | Bit_stream(const BinaryData& _bd, int initial_position = 0) : bd(_bd), initial_position(initial_position), bit_buffer(0), bits_left(0), total_bits_read(0) { 187 | if ( std::numeric_limits::digits != 8) 188 | throw Weird_char_size(); 189 | } 190 | bool get_bit() { 191 | if (bits_left == 0) { 192 | 193 | if (initial_position + total_bits_read / 8 == bd.length) 194 | throw Out_of_bits(); 195 | bit_buffer = bd.data[initial_position + total_bits_read / 8]; 196 | bits_left = 8; 197 | 198 | } 199 | total_bits_read++; 200 | bits_left --; 201 | return ( ( bit_buffer & ( 0x80 >> bits_left ) ) != 0); 202 | } 203 | 204 | unsigned long get_total_bits_read(void) const 205 | { 206 | return total_bits_read; 207 | } 208 | }; 209 | 210 | class Bit_oggstream { 211 | BinaryData& bd; 212 | 213 | unsigned char bit_buffer; 214 | unsigned int bits_stored; 215 | 216 | enum {header_bytes = 27, max_segments = 255, segment_size = 255}; 217 | 218 | unsigned int payload_bytes; 219 | bool first, continued; 220 | unsigned char page_buffer[header_bytes + max_segments + segment_size * max_segments]; 221 | uint32_t granule; 222 | uint32_t seqno; 223 | 224 | public: 225 | class Weird_char_size {}; 226 | 227 | Bit_oggstream(BinaryData& _bd) : 228 | bd(_bd), bit_buffer(0), bits_stored(0), payload_bytes(0), first(true), continued(false), granule(0), seqno(0) { 229 | if ( std::numeric_limits::digits != 8) 230 | throw Weird_char_size(); 231 | } 232 | 233 | void put_bit(bool bit) { 234 | if (bit) 235 | bit_buffer |= 1<= segment_size) 299 | { 300 | bytes_left -= segment_size; 301 | page_buffer[27 + i] = segment_size; 302 | } 303 | else 304 | { 305 | page_buffer[27 + i] = bytes_left; 306 | } 307 | } 308 | 309 | // checksum 310 | write_32_le(&page_buffer[22], 311 | checksum(page_buffer, header_bytes + segments + payload_bytes) 312 | ); 313 | 314 | bd.data = (uint8_t*) realloc(bd.data, bd.length + header_bytes + segments + payload_bytes); 315 | memcpy(&bd.data[bd.length], page_buffer, header_bytes + segments + payload_bytes); 316 | bd.length += header_bytes + segments + payload_bytes; 317 | 318 | seqno++; 319 | first = false; 320 | continued = next_continued; 321 | payload_bytes = 0; 322 | } 323 | } 324 | 325 | ~Bit_oggstream() { 326 | flush_page(); 327 | } 328 | }; 329 | 330 | // integer of a certain number of bits, to allow reading just that many 331 | // bits from the Bit_stream 332 | template 333 | class Bit_uint { 334 | unsigned int total; 335 | public: 336 | class Too_many_bits {}; 337 | class Int_too_big {}; 338 | 339 | Bit_uint() : total(0) { 340 | if (BIT_SIZE > static_cast(std::numeric_limits::digits)) 341 | throw Too_many_bits(); 342 | } 343 | 344 | explicit Bit_uint(unsigned int v) : total(v) { 345 | if (BIT_SIZE > static_cast(std::numeric_limits::digits)) 346 | throw Too_many_bits(); 347 | if ((v >> (BIT_SIZE-1U)) > 1U) 348 | throw Int_too_big(); 349 | } 350 | 351 | Bit_uint& operator = (unsigned int v) { 352 | if ((v >> (BIT_SIZE-1U)) > 1U) 353 | throw Int_too_big(); 354 | total = v; 355 | return *this; 356 | } 357 | 358 | operator unsigned int() const { return total; } 359 | 360 | friend Bit_stream& operator >> (Bit_stream& bstream, Bit_uint& bui) { 361 | bui.total = 0; 362 | for ( unsigned int i = 0; i < BIT_SIZE; i++) { 363 | if ( bstream.get_bit() ) bui.total |= (1U << i); 364 | } 365 | return bstream; 366 | } 367 | 368 | friend Bit_oggstream& operator << (Bit_oggstream& bstream, const Bit_uint& bui) { 369 | for ( unsigned int i = 0; i < BIT_SIZE; i++) { 370 | bstream.put_bit((bui.total & (1U << i)) != 0); 371 | } 372 | return bstream; 373 | } 374 | }; 375 | 376 | // integer of a run-time specified number of bits 377 | // bits from the Bit_stream 378 | class Bit_uintv { 379 | unsigned int size; 380 | unsigned int total; 381 | public: 382 | class Too_many_bits {}; 383 | class Int_too_big {}; 384 | 385 | explicit Bit_uintv(unsigned int s) : size(s), total(0) { 386 | if (s > static_cast(std::numeric_limits::digits)) 387 | throw Too_many_bits(); 388 | } 389 | 390 | Bit_uintv(unsigned int s, unsigned int v) : size(s), total(v) { 391 | if (size > static_cast(std::numeric_limits::digits)) 392 | throw Too_many_bits(); 393 | if ((v >> (size-1U)) > 1U) 394 | throw Int_too_big(); 395 | } 396 | 397 | Bit_uintv& operator = (unsigned int v) { 398 | if ((v >> (size-1U)) > 1U) 399 | throw Int_too_big(); 400 | total = v; 401 | return *this; 402 | } 403 | 404 | operator unsigned int() const { return total; } 405 | 406 | friend Bit_stream& operator >> (Bit_stream& bstream, Bit_uintv& bui) { 407 | bui.total = 0; 408 | for ( unsigned int i = 0; i < bui.size; i++) { 409 | if ( bstream.get_bit() ) bui.total |= (1U << i); 410 | } 411 | return bstream; 412 | } 413 | 414 | friend Bit_oggstream& operator << (Bit_oggstream& bstream, const Bit_uintv& bui) { 415 | for ( unsigned int i = 0; i < bui.size; i++) { 416 | bstream.put_bit((bui.total & (1U << i)) != 0); 417 | } 418 | return bstream; 419 | } 420 | }; 421 | 422 | #endif // _BIT_STREAM_H 423 | -------------------------------------------------------------------------------- /sound.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "defs.h" 8 | #include "bin.h" 9 | #include "bnk.h" 10 | #include "wpk.h" 11 | 12 | int VERBOSE = 0; 13 | 14 | // http://wiki.xentax.com/index.php/Wwise_SoundBank_(*.bnk) 15 | 16 | // almost full documentation of all types and versions: https://github.com/bnnm/wwiser 17 | 18 | struct sound { 19 | uint32_t self_id; 20 | uint32_t file_id; 21 | uint8_t is_streamed; 22 | }; 23 | 24 | struct music_track { 25 | uint32_t self_id; 26 | uint32_t track_count; 27 | uint32_t* file_ids; 28 | bool has_switch_ids; 29 | uint32_t switch_group_id; 30 | uint32_t* switch_ids; 31 | uint32_t parent_id; 32 | }; 33 | 34 | struct music_container { 35 | uint32_t self_id; 36 | uint32_t music_switch_id; 37 | uint32_t sound_object_id; 38 | uint32_t music_track_id_amount; 39 | uint32_t* music_track_ids; 40 | }; 41 | 42 | struct music_switch { 43 | uint32_t self_id; 44 | uint32_t parent_id; 45 | uint32_t num_children; 46 | uint32_t* children; 47 | uint32_t num_arguments; 48 | struct { 49 | uint32_t group_id; 50 | uint8_t group_type; 51 | }* arguments; 52 | uint32_t num_nodes; 53 | struct { 54 | uint32_t key; 55 | uint32_t audio_id; 56 | }* nodes; 57 | }; 58 | 59 | struct event_action { 60 | uint32_t self_id; 61 | uint8_t scope; 62 | uint8_t type; 63 | union { 64 | uint32_t sound_object_id; 65 | uint32_t switch_state_id; 66 | uint32_t target_state_id; 67 | }; 68 | union { 69 | uint32_t switch_group_id; 70 | uint32_t state_group_id; 71 | }; 72 | }; 73 | 74 | struct event { 75 | uint32_t self_id; 76 | uint8_t event_amount; 77 | uint32_t* event_ids; 78 | }; 79 | 80 | struct random_container { 81 | uint32_t self_id; 82 | uint32_t switch_container_id; 83 | uint32_t sound_id_amount; 84 | uint32_t* sound_ids; 85 | }; 86 | 87 | struct switch_container { 88 | uint32_t self_id; 89 | uint32_t parent_id; 90 | uint8_t group_type; 91 | uint32_t group_id; 92 | uint32_t num_children; 93 | uint32_t* children; 94 | }; 95 | 96 | typedef LIST(struct sound) SoundSection; 97 | typedef LIST(struct event_action) EventActionSection; 98 | typedef LIST(struct event) EventSection; 99 | typedef LIST(struct random_container) RandomContainerSection; 100 | typedef LIST(struct switch_container) SwitchContainerSection; 101 | typedef LIST(struct music_container) MusicContainerSection; 102 | typedef LIST(struct music_track) MusicTrackSection; 103 | typedef LIST(struct music_switch) MusicSwitchSection; 104 | 105 | struct BNKSections { 106 | SoundSection sounds; 107 | EventActionSection event_actions; 108 | EventSection events; 109 | RandomContainerSection random_containers; 110 | SwitchContainerSection switch_containers; 111 | MusicContainerSection music_segments; 112 | MusicTrackSection music_tracks; 113 | MusicSwitchSection music_switches; 114 | MusicContainerSection music_playlists; 115 | }; 116 | 117 | struct __attribute__((packed)) track_source_info { 118 | uint32_t track_index; 119 | uint32_t file_id; 120 | uint32_t event_id; 121 | double play_at; 122 | double begin_trim_offset; 123 | double end_trim_offset; 124 | double source_duration; 125 | }; 126 | 127 | void skip_initial_fx_params(FILE* bnk_file, uint32_t bnk_version) 128 | { 129 | fseek(bnk_file, 1, SEEK_CUR); 130 | uint8_t num_fx = getc(bnk_file); 131 | if (num_fx) fseek(bnk_file, 1, SEEK_CUR); 132 | fseek(bnk_file, num_fx * (bnk_version <= 0x91 ? 7 : 6), SEEK_CUR); 133 | } 134 | 135 | void skip_initial_params(FILE* bnk_file) 136 | { 137 | uint8_t prop_count = getc(bnk_file); 138 | fseek(bnk_file, 5 * prop_count, SEEK_CUR); 139 | prop_count = getc(bnk_file); 140 | fseek(bnk_file, 9 * prop_count, SEEK_CUR); 141 | } 142 | 143 | void skip_positioning_params(FILE* bnk_file, uint32_t bnk_version) 144 | { 145 | uint8_t positioning_bits = getc(bnk_file); 146 | bool has_positioning = positioning_bits & 1, has_3d = false, has_automation = false; 147 | if (has_positioning) { 148 | if (bnk_version <= 0x59) { 149 | bool has_2d = getc(bnk_file); 150 | has_3d = getc(bnk_file); 151 | if (has_2d) getc(bnk_file); 152 | } else { 153 | has_3d = positioning_bits & 0x2; 154 | } 155 | } 156 | if (has_positioning && has_3d) { 157 | if (bnk_version <= 0x59) { 158 | has_automation = (getc(bnk_file) & 3) != 1; 159 | fseek(bnk_file, 8, SEEK_CUR); 160 | } else { 161 | has_automation = (positioning_bits >> 5) & 3; 162 | getc(bnk_file); 163 | } 164 | } 165 | if (has_automation) { 166 | fseek(bnk_file, (bnk_version <= 0x59 ? 9 : 5), SEEK_CUR); 167 | uint32_t num_vertices; 168 | assert(fread(&num_vertices, 4, 1, bnk_file) == 1); 169 | fseek(bnk_file, 16 * num_vertices, SEEK_CUR); 170 | uint32_t num_playlist_items; 171 | assert(fread(&num_playlist_items, 4, 1, bnk_file) == 1); 172 | dprintf("num vertices: %d, num_playlist items: %d, ftell: %ld\n", num_vertices, num_playlist_items, ftell(bnk_file)); 173 | fseek(bnk_file, (bnk_version <= 0x59 ? 16 : 20) * num_playlist_items, SEEK_CUR); 174 | } else if (bnk_version <= 0x59) { 175 | getc(bnk_file); 176 | } 177 | } 178 | 179 | void skip_aux_params(FILE* bnk_file, uint32_t bnk_version) 180 | { 181 | bool has_aux = (getc(bnk_file) >> 3) & 1; 182 | if (has_aux) fseek(bnk_file, 4 * sizeof(uint32_t), SEEK_CUR); 183 | if (bnk_version > 0x87) fseek(bnk_file, 4, SEEK_CUR); 184 | } 185 | 186 | void skip_state_chunk(FILE* bnk_file) 187 | { 188 | uint8_t state_props = getc(bnk_file); 189 | fseek(bnk_file, 3 * state_props, SEEK_CUR); 190 | uint8_t state_groups = getc(bnk_file); 191 | for (uint8_t i = 0; i < state_groups; i++) { 192 | fseek(bnk_file, 5, SEEK_CUR); 193 | uint8_t states = getc(bnk_file); 194 | fseek(bnk_file, 8 * states, SEEK_CUR); 195 | } 196 | } 197 | 198 | void skip_rtpc(FILE* bnk_file, uint32_t bnk_version) 199 | { 200 | uint16_t num_rtpc; 201 | assert(fread(&num_rtpc, 2, 1, bnk_file) == 1); 202 | for (int i = 0; i < num_rtpc; i++) { 203 | fseek(bnk_file, bnk_version <= 0x59 ? 13 : 12, SEEK_CUR); 204 | uint16_t point_count; 205 | assert(fread(&point_count, 2, 1, bnk_file) == 1); 206 | fseek(bnk_file, 12 * point_count, SEEK_CUR); 207 | } 208 | } 209 | 210 | void skip_clip_automation(FILE* bnk_file) 211 | { 212 | uint32_t num_clip_automation; 213 | assert(fread(&num_clip_automation, 4, 1, bnk_file) == 1); 214 | for (uint32_t i = 0; i < num_clip_automation; i++) { 215 | fseek(bnk_file, 8, SEEK_CUR); 216 | uint32_t point_count; 217 | assert(fread(&point_count, 4, 1, bnk_file) == 1); 218 | fseek(bnk_file, 12 * point_count, SEEK_CUR); 219 | } 220 | } 221 | 222 | uint32_t skip_base_params(FILE* bnk_file, uint32_t bnk_version, uint32_t* out_bus_id) 223 | { 224 | skip_initial_fx_params(bnk_file, bnk_version); 225 | if (bnk_version > 0x88) { 226 | fseek(bnk_file, 1, SEEK_CUR); 227 | uint8_t num_fx = getc(bnk_file); 228 | fseek(bnk_file, 6 * num_fx, SEEK_CUR); 229 | } 230 | 231 | if (bnk_version > 0x59 && bnk_version <= 0x91) fseek(bnk_file, 1, SEEK_CUR); 232 | if (!out_bus_id) out_bus_id = &(uint32_t) {0}; 233 | assert(fread(out_bus_id, 4, 1, bnk_file) == 1); 234 | dprintf("reading in parent id at position %ld\n", ftell(bnk_file)); 235 | uint32_t parent_id; 236 | assert(fread(&parent_id, 4, 1, bnk_file) == 1); 237 | fseek(bnk_file, bnk_version <= 0x59 ? 2 : 1, SEEK_CUR); 238 | 239 | skip_initial_params(bnk_file); 240 | skip_positioning_params(bnk_file, bnk_version); 241 | skip_aux_params(bnk_file, bnk_version); 242 | 243 | fseek(bnk_file, 6, SEEK_CUR); 244 | 245 | uint8_t state_props = getc(bnk_file); 246 | fseek(bnk_file, 3 * state_props, SEEK_CUR); 247 | uint8_t state_groups = getc(bnk_file); 248 | for (uint8_t i = 0; i < state_groups; i++) { 249 | fseek(bnk_file, 5, SEEK_CUR); 250 | uint8_t states = getc(bnk_file); 251 | fseek(bnk_file, 8 * states, SEEK_CUR); 252 | } 253 | 254 | dprintf("skipping rtpc at position %ld\n", ftell(bnk_file)); 255 | skip_rtpc(bnk_file, bnk_version); 256 | 257 | return parent_id; 258 | } 259 | 260 | 261 | #define free_simple_section(section) do { \ 262 | free((section)->objects); \ 263 | } while (0) 264 | 265 | void free_event_section(EventSection* section) 266 | { 267 | for (uint32_t i = 0; i < section->length; i++) { 268 | free(section->objects[i].event_ids); 269 | } 270 | free(section->objects); 271 | } 272 | 273 | void free_random_container_section(RandomContainerSection* section) 274 | { 275 | for (uint32_t i = 0; i < section->length; i++) { 276 | free(section->objects[i].sound_ids); 277 | } 278 | free(section->objects); 279 | } 280 | 281 | void free_switch_container_section(SwitchContainerSection* section) 282 | { 283 | for (uint32_t i = 0; i < section->length; i++) { 284 | free(section->objects[i].children); 285 | } 286 | free(section->objects); 287 | } 288 | 289 | void free_music_container_section(MusicContainerSection* section) 290 | { 291 | for (uint32_t i = 0; i < section->length; i++) { 292 | free(section->objects[i].music_track_ids); 293 | } 294 | free(section->objects); 295 | } 296 | 297 | void free_music_track_section(MusicTrackSection* section) 298 | { 299 | for (uint32_t i = 0; i < section->length; i++) { 300 | free(section->objects[i].file_ids); 301 | if (section->objects[i].has_switch_ids) { 302 | free(section->objects[i].switch_ids); 303 | } 304 | } 305 | free(section->objects); 306 | } 307 | 308 | void free_music_switch_section(MusicSwitchSection* section) 309 | { 310 | for (uint32_t i = 0; i < section->length; i++) { 311 | free(section->objects[i].children); 312 | if (section->objects[i].num_arguments) { 313 | free(section->objects[i].arguments); 314 | } 315 | if (section->objects[i].num_nodes) { 316 | free(section->objects[i].nodes); 317 | } 318 | } 319 | free(section->objects); 320 | } 321 | 322 | int read_random_container_object(FILE* bnk_file, RandomContainerSection* random_containers, uint32_t bnk_version) 323 | { 324 | struct random_container new_random_container_object; 325 | assert(fread(&new_random_container_object.self_id, 4, 1, bnk_file) == 1); 326 | dprintf("at the beginning: %ld\n", ftell(bnk_file)); 327 | 328 | new_random_container_object.switch_container_id = skip_base_params(bnk_file, bnk_version, NULL); 329 | 330 | fseek(bnk_file, 24, SEEK_CUR); 331 | assert(fread(&new_random_container_object.sound_id_amount, 4, 1, bnk_file) == 1); 332 | dprintf("sound object id amount: %u\n", new_random_container_object.sound_id_amount); 333 | if (new_random_container_object.sound_id_amount > 100) { 334 | eprintf("Would have allocated %u bytes. That can't be right. (ERROR btw)\n", new_random_container_object.sound_id_amount * 4); 335 | return -1; 336 | } 337 | new_random_container_object.sound_ids = malloc(new_random_container_object.sound_id_amount * 4); 338 | assert(fread(new_random_container_object.sound_ids, 4, new_random_container_object.sound_id_amount, bnk_file) == new_random_container_object.sound_id_amount); 339 | 340 | add_object(random_containers, &new_random_container_object); 341 | 342 | return 0; 343 | } 344 | 345 | int read_switch_container_object(FILE *bnk_file, SwitchContainerSection* switch_containers, uint32_t bnk_version) 346 | { 347 | struct switch_container new_switch_container_object; 348 | assert(fread(&new_switch_container_object.self_id, 4, 1, bnk_file) == 1); 349 | 350 | new_switch_container_object.parent_id = skip_base_params(bnk_file, bnk_version, NULL); 351 | assert(fread(&new_switch_container_object.group_type, 1, 1, bnk_file) == 1); 352 | if (bnk_version <= 0x59) fseek(bnk_file, 3, SEEK_CUR); 353 | assert(fread(&new_switch_container_object.group_id, 4, 1, bnk_file) == 1); 354 | fseek(bnk_file, 5, SEEK_CUR); 355 | 356 | assert(fread(&new_switch_container_object.num_children, 4, 1, bnk_file) == 1); 357 | new_switch_container_object.children = malloc(new_switch_container_object.num_children * sizeof(uint32_t)); 358 | assert(fread(new_switch_container_object.children, 4, new_switch_container_object.num_children, bnk_file) == new_switch_container_object.num_children); 359 | 360 | add_object(switch_containers, &new_switch_container_object); 361 | 362 | return 0; 363 | } 364 | 365 | int read_sound_object(FILE* bnk_file, SoundSection* sounds, uint32_t bnk_version) 366 | { 367 | struct sound new_sound_object; 368 | assert(fread(&new_sound_object.self_id, 4, 1, bnk_file) == 1); 369 | fseek(bnk_file, 4, SEEK_CUR); 370 | assert(fread(&new_sound_object.is_streamed, 1, 1, bnk_file) == 1); 371 | if (bnk_version <= 0x59) fseek(bnk_file, 3, SEEK_CUR); // was 4 byte field with 3 bytes zero 372 | if (bnk_version <= 0x70) fseek(bnk_file, 4, SEEK_CUR); 373 | assert(fread(&new_sound_object.file_id, 4, 1, bnk_file) == 1); 374 | 375 | add_object(sounds, &new_sound_object); 376 | 377 | return 0; 378 | } 379 | 380 | int read_event_action_object(FILE* bnk_file, EventActionSection* event_actions) 381 | { 382 | struct event_action new_event_action_object; 383 | assert(fread(&new_event_action_object.self_id, 4, 1, bnk_file) == 1); 384 | assert(fread(&new_event_action_object.scope, 1, 1, bnk_file) == 1); 385 | assert(fread(&new_event_action_object.type, 1, 1, bnk_file) == 1); 386 | if (new_event_action_object.type == 25 /* set switch */) { 387 | fseek(bnk_file, 5, SEEK_CUR); 388 | skip_initial_params(bnk_file); 389 | assert(fread(&new_event_action_object.switch_group_id, 4, 1, bnk_file) == 1); 390 | assert(fread(&new_event_action_object.switch_state_id, 4, 1, bnk_file) == 1); 391 | } else if (new_event_action_object.type == 18 /* "set state" */) { 392 | fseek(bnk_file, 5, SEEK_CUR); 393 | skip_initial_params(bnk_file); 394 | assert(fread(&new_event_action_object.state_group_id, 4, 1, bnk_file) == 1); 395 | assert(fread(&new_event_action_object.target_state_id, 4, 1, bnk_file) == 1); 396 | } else { 397 | assert(fread(&new_event_action_object.sound_object_id, 4, 1, bnk_file) == 1); 398 | } 399 | 400 | add_object(event_actions, &new_event_action_object); 401 | 402 | return 0; 403 | } 404 | 405 | int read_event_object(FILE* bnk_file, EventSection* events, uint32_t bnk_version) 406 | { 407 | struct event new_event_object; 408 | assert(fread(&new_event_object.self_id, 4, 1, bnk_file) == 1); 409 | assert(fread(&new_event_object.event_amount, 1, 1, bnk_file) == 1); 410 | if (bnk_version == 0x58) fseek(bnk_file, 3, SEEK_CUR); // presumably padding bytes or 4 byte int which was later deemed unnecessarily high 411 | new_event_object.event_ids = malloc(new_event_object.event_amount * 4); 412 | assert(fread(new_event_object.event_ids, 4, new_event_object.event_amount, bnk_file) == new_event_object.event_amount); 413 | 414 | add_object(events, &new_event_object); 415 | 416 | return 0; 417 | } 418 | 419 | int read_music_container_object(FILE* bnk_file, MusicContainerSection* music_containers, uint32_t bnk_version) 420 | { 421 | struct music_container new_music_container_object; 422 | assert(fread(&new_music_container_object.self_id, 4, 1, bnk_file) == 1); 423 | fseek(bnk_file, 1, SEEK_CUR); 424 | new_music_container_object.sound_object_id = skip_base_params(bnk_file, bnk_version, &new_music_container_object.music_switch_id); 425 | assert(fread(&new_music_container_object.music_track_id_amount, 4, 1, bnk_file) == 1); 426 | new_music_container_object.music_track_ids = malloc(new_music_container_object.music_track_id_amount * 4); 427 | assert(fread(new_music_container_object.music_track_ids, 4, new_music_container_object.music_track_id_amount, bnk_file) == new_music_container_object.music_track_id_amount); 428 | 429 | add_object(music_containers, &new_music_container_object); 430 | 431 | return 0; 432 | } 433 | 434 | int read_music_track_object(FILE* bnk_file, MusicTrackSection* music_tracks, uint32_t bnk_version) 435 | { 436 | struct music_track new_music_track_object; 437 | assert(fread(&new_music_track_object.self_id, 4, 1, bnk_file) == 1); 438 | getc(bnk_file); 439 | uint32_t count; 440 | assert(fread(&count, 4, 1, bnk_file) == 1); 441 | fseek(bnk_file, 14 * count, SEEK_CUR); 442 | 443 | // read playlist count 444 | assert(fread(&count, 4, 1, bnk_file) == 1); 445 | // this "playlist count" lists all tracks, but there may be a dummy "null" track which has a switch id associated with it 446 | // seek forward and read in the subtrack count for an accurate value 447 | // let's hope this is safe and the subtrack count can't be other values (: 448 | fseek(bnk_file, count * sizeof(struct track_source_info), SEEK_CUR); 449 | assert(fread(&new_music_track_object.track_count, 4, 1, bnk_file) == 1); 450 | // printf("track count: %u, count: %u, offset: %lX\n", new_music_track_object.track_count, count, ftell(bnk_file)); 451 | fseek(bnk_file, - 4 - count * sizeof(struct track_source_info), SEEK_CUR); 452 | 453 | new_music_track_object.file_ids = calloc(new_music_track_object.track_count, sizeof(uint32_t)); 454 | for (uint32_t i = 0; i < count; i++) { 455 | struct track_source_info source_info = {0}; 456 | assert(fread(&source_info, sizeof(struct track_source_info), 1, bnk_file) == 1); 457 | new_music_track_object.file_ids[source_info.track_index] = source_info.file_id; 458 | } 459 | fseek(bnk_file, 4, SEEK_CUR); 460 | skip_clip_automation(bnk_file); 461 | new_music_track_object.parent_id = skip_base_params(bnk_file, bnk_version, NULL); 462 | uint8_t track_type = getc(bnk_file); 463 | new_music_track_object.has_switch_ids = track_type == 0x3; 464 | if (new_music_track_object.has_switch_ids) { 465 | getc(bnk_file); 466 | assert(fread(&new_music_track_object.switch_group_id, 4, 1, bnk_file) == 1); 467 | fseek(bnk_file, 4, SEEK_CUR); // default switch id 468 | assert(fread(&count, 4, 1, bnk_file) == 1); 469 | 470 | if (count != new_music_track_object.track_count) { 471 | printf("Error: Switch id count does not match track count!\n"); 472 | return -1; 473 | } 474 | new_music_track_object.switch_ids = malloc(new_music_track_object.track_count * sizeof(uint32_t)); 475 | assert(fread(new_music_track_object.switch_ids, sizeof(uint32_t), new_music_track_object.track_count, bnk_file) == new_music_track_object.track_count); 476 | } 477 | 478 | add_object(music_tracks, &new_music_track_object); 479 | 480 | return 0; 481 | } 482 | 483 | int read_music_switch_object(FILE* bnk_file, MusicSwitchSection* music_switches, uint32_t bnk_version) 484 | { 485 | struct music_switch new_music_switch_object; 486 | assert(fread(&new_music_switch_object.self_id, 4, 1, bnk_file) == 1); 487 | fseek(bnk_file, 1, SEEK_CUR); 488 | new_music_switch_object.parent_id = skip_base_params(bnk_file, bnk_version, NULL); 489 | assert(fread(&new_music_switch_object.num_children, 4, 1, bnk_file) == 1); 490 | new_music_switch_object.children = malloc(new_music_switch_object.num_children * sizeof(uint32_t)); 491 | assert(fread(new_music_switch_object.children, 4, new_music_switch_object.num_children, bnk_file) == new_music_switch_object.num_children); 492 | fseek(bnk_file, 23, SEEK_CUR); 493 | uint32_t num_stingers; 494 | assert(fread(&num_stingers, 4, 1, bnk_file) == 1); 495 | fseek(bnk_file, 24 * num_stingers, SEEK_CUR); 496 | uint32_t num_rules; 497 | assert(fread(&num_rules, 4, 1, bnk_file) == 1); 498 | for (uint32_t i = 0; i < num_rules; i++) { 499 | uint32_t num_sources; 500 | assert(fread(&num_sources, 4, 1, bnk_file) == 1); 501 | fseek(bnk_file, 4 * num_sources, SEEK_CUR); 502 | uint32_t num_destinations; 503 | assert(fread(&num_destinations, 4, 1, bnk_file) == 1); 504 | fseek(bnk_file, 4 * num_destinations, SEEK_CUR); 505 | fseek(bnk_file, bnk_version <= 0x84 ? 45 : 47, SEEK_CUR); 506 | bool has_transobject = getc(bnk_file); 507 | if (has_transobject) fseek(bnk_file, 30, SEEK_CUR); 508 | } 509 | fseek(bnk_file, 1, SEEK_CUR); 510 | assert(fread(&new_music_switch_object.num_arguments, 4, 1, bnk_file) == 1); 511 | new_music_switch_object.arguments = malloc(new_music_switch_object.num_arguments * sizeof(*new_music_switch_object.arguments)); 512 | for (uint32_t i = 0; i < new_music_switch_object.num_arguments; i++) { 513 | assert(fread(&new_music_switch_object.arguments[i].group_id, 4, 1, bnk_file) == 1); 514 | } 515 | for (uint32_t i = 0; i < new_music_switch_object.num_arguments; i++) { 516 | assert(fread(&new_music_switch_object.arguments[i].group_type, 1, 1, bnk_file) == 1); 517 | } 518 | uint32_t tree_size; 519 | assert(fread(&tree_size, 4, 1, bnk_file) == 1); 520 | fseek(bnk_file, 1, SEEK_CUR); 521 | new_music_switch_object.num_nodes = tree_size / 12; 522 | new_music_switch_object.nodes = malloc(new_music_switch_object.num_nodes * sizeof(*new_music_switch_object.nodes)); 523 | for (uint32_t i = 0; i < new_music_switch_object.num_nodes; i++) { 524 | assert(fread(&new_music_switch_object.nodes[i].key, 4, 1, bnk_file) == 1); 525 | assert(fread(&new_music_switch_object.nodes[i].audio_id, 4, 1, bnk_file) == 1); 526 | fseek(bnk_file, 4, SEEK_CUR); 527 | } 528 | 529 | add_object(music_switches, &new_music_switch_object); 530 | 531 | return 0; 532 | } 533 | 534 | void parse_event_bnk_file(char* path, struct BNKSections* sections) 535 | { 536 | FILE* bnk_file = fopen(path, "rb"); 537 | if (!bnk_file) { 538 | eprintf("Error: Failed to open \"%s\".\n", path); 539 | exit(EXIT_FAILURE); 540 | } 541 | char magic[4]; 542 | assert(fread(magic, 1, 4, bnk_file) == 4); 543 | if (memcmp(magic, "BKHD", 4) != 0) { 544 | eprintf("Error: Not a bnk file!\n"); 545 | exit(EXIT_FAILURE); 546 | } 547 | fseek(bnk_file, 4, SEEK_CUR); 548 | uint32_t bnk_version; 549 | assert(fread(&bnk_version, 4, 1, bnk_file) == 1); 550 | 551 | uint32_t section_length = skip_to_section(bnk_file, "HIRC", true); 552 | if (!section_length) { 553 | eprintf("Error: Failed to skip to section \"HIRC\" in file \"%s\".\nMake sure to provide the correct file.\n", path); 554 | exit(EXIT_FAILURE); 555 | } 556 | uint32_t initial_position = ftell(bnk_file); 557 | uint32_t num_of_objects; 558 | assert(fread(&num_of_objects, 4, 1, bnk_file) == 1); 559 | uint32_t objects_read = 0; 560 | while ((uint32_t) ftell(bnk_file) < initial_position + section_length) { 561 | uint8_t type; 562 | uint32_t object_length; 563 | assert(fread(&type, 1, 1, bnk_file) == 1); 564 | assert(fread(&object_length, 4, 1, bnk_file) == 1); 565 | 566 | dprintf("Am here with an object of type %u\n", type); 567 | int object_start = ftell(bnk_file); 568 | switch (type) 569 | { 570 | case 2: 571 | read_sound_object(bnk_file, §ions->sounds, bnk_version); 572 | break; 573 | case 3: 574 | read_event_action_object(bnk_file, §ions->event_actions); 575 | break; 576 | case 4: 577 | read_event_object(bnk_file, §ions->events, bnk_version); 578 | break; 579 | case 5: 580 | read_random_container_object(bnk_file, §ions->random_containers, bnk_version); 581 | break; 582 | case 6: 583 | read_switch_container_object(bnk_file, §ions->switch_containers, bnk_version); 584 | break; 585 | case 10: 586 | read_music_container_object(bnk_file, §ions->music_segments, bnk_version); 587 | break; 588 | case 11: 589 | read_music_track_object(bnk_file, §ions->music_tracks, bnk_version); 590 | break; 591 | case 12: 592 | read_music_switch_object(bnk_file, §ions->music_switches, bnk_version); 593 | break; 594 | case 13: 595 | read_music_container_object(bnk_file, §ions->music_playlists, bnk_version); 596 | break; 597 | default: 598 | dprintf("Skipping object, as it is irrelevant for me.\n"); 599 | dprintf("gonna seek %u forward\n", object_length); 600 | } 601 | fseek(bnk_file, object_start + object_length, SEEK_SET); 602 | 603 | objects_read++; 604 | } 605 | dprintf("objects read: %u, num of objects: %u\n", objects_read, num_of_objects); 606 | assert(objects_read == num_of_objects); 607 | dprintf("Current offset: %ld\n", ftell(bnk_file)); 608 | 609 | for (uint32_t i = 0; i < sections->sounds.length; i++) { 610 | dprintf("is streamed: %s, file id: %u\n", sections->sounds.objects[i].is_streamed ? "true" : "false", sections->sounds.objects[i].file_id); 611 | } 612 | 613 | for (uint32_t i = 0; i < sections->events.length; i++) { 614 | dprintf("Self id of all event objects: %u\n", sections->events.objects[i].self_id); 615 | } 616 | dprintf("amount of event ids: %u\n", sections->events.length); 617 | dprintf("amount of event actions: %u, amount of sounds: %u\n", sections->event_actions.length, sections->sounds.length); 618 | for (uint32_t i = 0; i < sections->event_actions.length; i++) { 619 | dprintf("event action sound object ids: %u\n", sections->event_actions.objects[i].sound_object_id); 620 | } 621 | 622 | fclose(bnk_file); 623 | } 624 | 625 | void add_connected_files(char* string, uint32_t id, uint32_t parent_id, StringHashes* stringHashes, struct BNKSections* sections) 626 | { 627 | struct music_switch* music_switch = NULL; 628 | find_object_s(§ions->music_switches, music_switch, self_id, id); 629 | if (music_switch) { 630 | for (uint32_t i = 0; i < music_switch->num_children; i++) { 631 | add_connected_files(string, music_switch->children[i], id, stringHashes, sections); 632 | } 633 | return; 634 | } 635 | 636 | struct music_container* music_playlist = NULL; 637 | find_object_s(§ions->music_playlists, music_playlist, self_id, id); 638 | if (music_playlist) { 639 | for (uint32_t i = 0; i < music_playlist->music_track_id_amount; i++) { 640 | add_connected_files(string, music_playlist->music_track_ids[i], music_playlist->music_track_id_amount > 1 ? id : parent_id, stringHashes, sections); 641 | } 642 | return; 643 | } 644 | 645 | struct random_container* random_container = NULL; 646 | find_object_s(§ions->random_containers, random_container, self_id, id); 647 | if (random_container) { 648 | for (uint32_t i = 0; i < random_container->sound_id_amount; i++) { 649 | add_connected_files(string, random_container->sound_ids[i], random_container->sound_id_amount > 1 ? id : parent_id, stringHashes, sections); 650 | } 651 | return; 652 | } 653 | 654 | struct switch_container* switch_container = NULL; 655 | find_object_s(§ions->switch_containers, switch_container, self_id, id); 656 | if (switch_container) { 657 | for (uint32_t i = 0; i < switch_container->num_children; i++) { 658 | add_connected_files(string, switch_container->children[i], id, stringHashes, sections); 659 | } 660 | return; 661 | } 662 | 663 | struct music_container* music_segment = NULL; 664 | find_object_s(§ions->music_segments, music_segment, self_id, id); 665 | if (music_segment) { 666 | for (uint32_t i = 0; i < music_segment->music_track_id_amount; i++) { 667 | struct music_track* music_track = NULL; 668 | find_object_s(§ions->music_tracks, music_track, self_id, music_segment->music_track_ids[i]); 669 | for (uint32_t j = 0; j < music_track->track_count; j++) { 670 | dprintf("Found a matching id in a music track!\n"); 671 | v_printf(2, "Event %s belongs to file \"%u.wem\".\n", string, music_track->file_ids[j]); 672 | add_object(stringHashes, (&(struct string_hash) { 673 | .string = string, 674 | .hash = music_track->file_ids[j], 675 | .container_id = parent_id, 676 | .music_segment_id = music_segment->music_track_id_amount > 1 ? music_segment->self_id : 0 677 | })); 678 | } 679 | } 680 | return; 681 | } 682 | 683 | for (uint32_t i = 0; i < sections->sounds.length; i++) { 684 | if (sections->sounds.objects[i].self_id == id) { 685 | dprintf("Found a matching sound id!\n"); 686 | v_printf(2, "Event %s belongs to file \"%u.wem\".\n", string, sections->sounds.objects[i].file_id); 687 | add_object(stringHashes, (&(struct string_hash) { 688 | .string = string, 689 | .hash = sections->sounds.objects[i].file_id, 690 | .container_id = parent_id, 691 | .sound_index = i 692 | })); 693 | return; 694 | } 695 | } 696 | } 697 | 698 | 699 | #define VERSION "1.9" 700 | void print_help() 701 | { 702 | printf("bnk-extract "VERSION" - a tool to extract bnk and wpk files, optionally sorting them into named groups.\n\n"); 703 | printf("Syntax: ./bnk-extract --audio path/to/audio.[bnk|wpk] [--bin path/to/skinX.bin --events path/to/events.bnk] [-o path/to/output] [--wems-only] [--oggs-only]\n\n"); 704 | printf("Options: \n"); 705 | printf(" [-a|--audio] path\n Specify the path to the audio bnk/wpk file that is to be extracted (mandatory).\n Specifying this option without -e and -b will only extract files without grouping them by event name.\n\n"); 706 | printf(" [-e|--events] path\n Specify the path to the events bnk file that contains information about the events that trigger certain audio files.\n\n"); 707 | printf(" [-b|--bin] path\n Specify the path to the bin file that lists the clear names of all events.\n\n Must specify both -e and -b options (or neither).\n\n"); 708 | printf(" [-o|--output] path\n Specify output path. Default is \"output\".\n\n"); 709 | printf(" [--wems-only]\n Extract wem files only.\n\n"); 710 | printf(" [--oggs-only]\n Extract ogg files only.\n By default, both .wem and converted .ogg files will be extracted.\n\n"); 711 | printf(" [--alternate-filenames]\n Files will be named by event name + unique id instead of by their audio id.\n\n"); 712 | printf(" [-v [-v ...]]\n Increases verbosity level by one per \"-v\".\n"); 713 | } 714 | 715 | int main(int argc, char* argv[]) 716 | { 717 | if (argc < 2) { 718 | eprintf("Missing arguments! (type --help for more info).\n"); 719 | exit(EXIT_FAILURE); 720 | } 721 | if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { 722 | print_help(); 723 | exit(EXIT_FAILURE); 724 | } 725 | 726 | char* bin_path = NULL; 727 | char* audio_path = NULL; 728 | char* events_path = NULL; 729 | char* output_path = "output"; 730 | bool wems_only = false; 731 | bool oggs_only = false; 732 | bool alternate_filenames = false; 733 | for (char** arg = &argv[1]; *arg; arg++) { 734 | if (strcmp(*arg, "-a") == 0 || strcmp(*arg, "--audio") == 0) { 735 | if (*(arg + 1)) { 736 | arg++; 737 | audio_path = *arg; 738 | } 739 | } else if (strcmp(*arg, "-e") == 0 || strcmp(*arg, "--events") == 0) { 740 | if (*(arg + 1)) { 741 | arg++; 742 | events_path = *arg; 743 | } 744 | } else if (strcmp(*arg, "-b") == 0 || strcmp(*arg, "--bin") == 0) { 745 | if (*(arg + 1)) { 746 | arg++; 747 | bin_path = *arg; 748 | } 749 | } else if (strcmp(*arg, "-o") == 0 || strcmp(*arg, "--output") == 0) { 750 | if (*(arg + 1)) { 751 | arg++; 752 | output_path = *arg; 753 | } 754 | } else if (strcmp(*arg, "--wem-only") == 0 || strcmp(*arg, "--wems-only") == 0) { 755 | wems_only = true; 756 | } else if (strcmp(*arg, "--ogg-only") == 0 || strcmp(*arg, "--oggs-only") == 0) { 757 | oggs_only = true; 758 | } else if (strcmp(*arg, "--alternate-filename") == 0 || strcmp(*arg, "--alternate-filenames") == 0) { 759 | alternate_filenames = true; 760 | } else if (strcmp(*arg, "-v") == 0) { 761 | VERBOSE++; 762 | } 763 | } 764 | if (!audio_path) { 765 | eprintf("Error: No audio file provided.\n"); 766 | exit(EXIT_FAILURE); 767 | } else if (!events_path != !bin_path) { // one given, but not both 768 | eprintf("Error: Provide both events and bin file.\n"); 769 | exit(EXIT_FAILURE); 770 | } 771 | 772 | StringHashes string_files; 773 | initialize_list(&string_files); 774 | if (!bin_path) { 775 | if (strlen(audio_path) >= 4 && memcmp(&audio_path[strlen(audio_path) - 4], ".bnk", 4) == 0) 776 | extract_bnk_file(audio_path, &string_files, output_path, wems_only, oggs_only, alternate_filenames); 777 | else 778 | extract_wpk_file(audio_path, &string_files, output_path, wems_only, oggs_only, alternate_filenames); 779 | free(string_files.objects); 780 | exit(EXIT_SUCCESS); 781 | } 782 | 783 | StringHashes* read_strings = parse_bin_file(bin_path); 784 | struct BNKSections sections = {0}; 785 | initialize_list(§ions.sounds); 786 | initialize_list(§ions.event_actions); 787 | initialize_list(§ions.events); 788 | initialize_list(§ions.random_containers); 789 | initialize_list(§ions.switch_containers); 790 | initialize_list(§ions.music_segments); 791 | initialize_list(§ions.music_tracks); 792 | initialize_list(§ions.music_switches); 793 | initialize_list(§ions.music_playlists); 794 | 795 | parse_event_bnk_file(events_path, §ions); 796 | sort_list(§ions.sounds, self_id); 797 | sort_list(§ions.event_actions, self_id); 798 | sort_list(§ions.events, self_id); 799 | sort_list(§ions.random_containers, self_id); 800 | sort_list(§ions.switch_containers, self_id); 801 | sort_list(§ions.music_segments, self_id); 802 | sort_list(§ions.music_tracks, self_id); 803 | sort_list(§ions.music_switches, self_id); 804 | sort_list(§ions.music_playlists, self_id); 805 | 806 | dprintf("amount: %u\n", read_strings->length); 807 | for (uint32_t i = 0; i < read_strings->length; i++) { 808 | uint32_t hash = read_strings->objects[i].hash; 809 | dprintf("hashes[%u]: %u, string: %s\n", i, read_strings->objects[i].hash, read_strings->objects[i].string); 810 | 811 | struct event* event = NULL; 812 | find_object_s(§ions.events, event, self_id, hash); 813 | if (!event) continue; 814 | for (uint32_t j = 0; j < event->event_amount; j++) { 815 | struct event_action* event_action = NULL; 816 | find_object_s(§ions.event_actions, event_action, self_id, event->event_ids[j]); 817 | if (!event_action) continue; 818 | 819 | if (event_action->type == 4 /* "play" */) { 820 | add_connected_files(read_strings->objects[i].string, event_action->sound_object_id, 0, &string_files, §ions); 821 | } else if (event_action->type == 25 /* "set switch" */) { 822 | for (uint32_t k = 0; k < sections.music_tracks.length; k++) { 823 | struct music_track* music_track = §ions.music_tracks.objects[k]; 824 | if (music_track->has_switch_ids && music_track->switch_group_id == event_action->switch_group_id) { 825 | for (uint32_t l = 0; l < music_track->track_count; l++) { 826 | if (music_track->switch_ids[l] == event_action->switch_state_id) { 827 | dprintf("Found a music track matching the event switch id!\n"); 828 | v_printf(2, "Event %s belongs to file \"%u.wem\".\n", read_strings->objects[i].string, music_track->file_ids[l]); 829 | add_object(&string_files, (&(struct string_hash) { 830 | .string = read_strings->objects[i].string, 831 | .hash = music_track->file_ids[l], 832 | .switch_id = music_track->switch_group_id 833 | })); 834 | } 835 | } 836 | } 837 | } 838 | } else if (event_action->type == 18 /* "set state" */) { 839 | for (uint32_t k = 0; k < sections.music_switches.length; k++) { 840 | struct music_switch* music_switch = §ions.music_switches.objects[k]; 841 | for (uint32_t l = 0; l < music_switch->num_arguments; l++) { 842 | if (music_switch->arguments[l].group_type == 1 /* state */ && music_switch->arguments[l].group_id == event_action->state_group_id) { 843 | for (uint32_t m = 0; m < music_switch->num_nodes; m++) { 844 | if (music_switch->nodes[m].key == event_action->target_state_id) { 845 | add_connected_files(read_strings->objects[i].string, music_switch->nodes[m].audio_id, music_switch->self_id, &string_files, §ions); 846 | } 847 | } 848 | } 849 | } 850 | } 851 | } 852 | } 853 | } 854 | 855 | free_simple_section(§ions.sounds); 856 | free_simple_section(§ions.event_actions); 857 | free_event_section(§ions.events); 858 | free_random_container_section(§ions.random_containers); 859 | free_switch_container_section(§ions.switch_containers); 860 | free_music_container_section(§ions.music_segments); 861 | free_music_track_section(§ions.music_tracks); 862 | free_music_switch_section(§ions.music_switches); 863 | free_music_container_section(§ions.music_playlists); 864 | 865 | sort_list(&string_files, hash); 866 | if (strlen(audio_path) >= 4 && memcmp(&audio_path[strlen(audio_path) - 4], ".bnk", 4) == 0) 867 | extract_bnk_file(audio_path, &string_files, output_path, wems_only, oggs_only, alternate_filenames); 868 | else 869 | extract_wpk_file(audio_path, &string_files, output_path, wems_only, oggs_only, alternate_filenames); 870 | 871 | free(string_files.objects); 872 | for (uint32_t i = 0; i < read_strings->length; i++) { 873 | free(read_strings->objects[i].string); 874 | } 875 | free(read_strings->objects); 876 | free(read_strings); 877 | } 878 | -------------------------------------------------------------------------------- /ww2ogg/wwriff.cpp: -------------------------------------------------------------------------------- 1 | #define __STDC_CONSTANT_MACROS 2 | #include 3 | #include "stdint.h" 4 | #include "errors.hpp" 5 | #include "wwriff.hpp" 6 | #include "Bit_stream.hpp" 7 | #include "codebook.hpp" 8 | #include "codebook.bin" 9 | #include "../defs.h" 10 | 11 | using namespace std; 12 | 13 | /* Modern 2 or 6 byte header */ 14 | class Packet 15 | { 16 | long _offset; 17 | uint16_t _size; 18 | uint32_t _absolute_granule; 19 | bool _no_granule; 20 | public: 21 | Packet(const BinaryData& bd, long o, bool little_endian, bool no_granule = false) : _offset(o), _size(-1), _absolute_granule(0), _no_granule(no_granule) { 22 | // i.seekg(_offset); 23 | 24 | if (little_endian) 25 | { 26 | _size = read_16_le(&bd.data[_offset]); 27 | if (!_no_granule) 28 | { 29 | _absolute_granule = read_32_le(&bd.data[_offset + 2]); 30 | } 31 | } 32 | else 33 | { 34 | _size = read_16_be(&bd.data[_offset]); 35 | if (!_no_granule) 36 | { 37 | _absolute_granule = read_32_be(&bd.data[_offset + 2]); 38 | } 39 | } 40 | } 41 | 42 | long header_size(void) { return _no_granule?2:6; } 43 | long offset(void) { return _offset + header_size(); } 44 | uint16_t size(void) { return _size; } 45 | uint32_t granule(void) { return _absolute_granule; } 46 | long next_offset(void) { return _offset + header_size() + _size; } 47 | }; 48 | 49 | /* Old 8 byte header */ 50 | class Packet_8 51 | { 52 | long _offset; 53 | uint32_t _size; 54 | uint32_t _absolute_granule; 55 | public: 56 | Packet_8(const BinaryData& bd, long o, bool little_endian) : _offset(o), _size(-1), _absolute_granule(0) { 57 | if (little_endian) 58 | { 59 | _size = read_32_le(&bd.data[_offset]); 60 | _absolute_granule = read_32_le(&bd.data[_offset + 4]); 61 | } 62 | else 63 | { 64 | _size = read_32_be(&bd.data[_offset]); 65 | _absolute_granule = read_32_be(&bd.data[_offset + 4]); 66 | } 67 | } 68 | 69 | long header_size(void) { return 8; } 70 | long offset(void) { return _offset + header_size(); } 71 | uint32_t size(void) { return _size; } 72 | uint32_t granule(void) { return _absolute_granule; } 73 | long next_offset(void) { return _offset + header_size() + _size; } 74 | }; 75 | 76 | class Vorbis_packet_header 77 | { 78 | uint8_t type; 79 | 80 | static const char vorbis_str[6]; 81 | 82 | public: 83 | explicit Vorbis_packet_header(uint8_t t) : type(t) {} 84 | 85 | friend Bit_oggstream& operator << (Bit_oggstream& bstream, const Vorbis_packet_header& vph) { 86 | Bit_uint<8> t(vph.type); 87 | bstream << t; 88 | 89 | for ( unsigned int i = 0; i < 6; i++ ) 90 | { 91 | Bit_uint<8> c(vorbis_str[i]); 92 | bstream << c; 93 | } 94 | 95 | return bstream; 96 | } 97 | }; 98 | 99 | const char Vorbis_packet_header::vorbis_str[6] = {'v','o','r','b','i','s'}; 100 | 101 | Wwise_RIFF_Vorbis::Wwise_RIFF_Vorbis( 102 | const BinaryData& infile, 103 | const string& codebooks_name, 104 | bool inline_codebooks, 105 | bool full_setup, 106 | ForcePacketFormat force_packet_format 107 | ) 108 | : 109 | _infile_data(infile), 110 | _codebooks_name(codebooks_name), 111 | _little_endian(true), 112 | _is_wav(false), 113 | _fmt_offset(-1), 114 | _cue_offset(-1), 115 | _LIST_offset(-1), 116 | _smpl_offset(-1), 117 | _vorb_offset(-1), 118 | _data_offset(-1), 119 | _fmt_size(-1), 120 | _cue_size(-1), 121 | _LIST_size(-1), 122 | _smpl_size(-1), 123 | _vorb_size(-1), 124 | _data_size(-1), 125 | _ext_unk(0), 126 | _subtype(0), 127 | _cue_count(0), 128 | _loop_count(0), 129 | _loop_start(0), 130 | _loop_end(0), 131 | _setup_packet_offset(0), 132 | _first_audio_packet_offset(0), 133 | _uid(0), 134 | _blocksize_0_pow(0), 135 | _blocksize_1_pow(0), 136 | _inline_codebooks(inline_codebooks), 137 | _full_setup(full_setup), 138 | _header_triad_present(false), 139 | _old_packet_headers(false), 140 | _no_granule(false), 141 | _mod_packets(false), 142 | _read_16(NULL), 143 | _read_32(NULL) 144 | { 145 | // check RIFF header 146 | { 147 | unsigned char riff_head[4], wave_head[4]; 148 | memcpy(riff_head, _infile_data.data, 4); 149 | 150 | if (memcmp(&riff_head[0],"RIFX",4)) 151 | { 152 | if (memcmp(&riff_head[0],"RIFF",4)) 153 | { 154 | throw Parse_error_str("missing RIFF"); 155 | } 156 | else 157 | { 158 | _little_endian = true; 159 | } 160 | } 161 | else 162 | { 163 | _little_endian = false; 164 | } 165 | 166 | if (_little_endian) 167 | { 168 | _read_16 = read_16_le; 169 | _read_32 = read_32_le; 170 | } 171 | else 172 | { 173 | _read_16 = read_16_be; 174 | _read_32 = read_32_be; 175 | } 176 | 177 | _riff_size = _read_32(&_infile_data.data[4]) + 8; 178 | 179 | if (_riff_size > _infile_data.length) { 180 | v_printf(1, ".?\" "); 181 | throw Parse_error_str("RIFF truncated"); 182 | } 183 | 184 | memcpy(wave_head, &_infile_data.data[8], 4); 185 | if (memcmp(&wave_head[0],"WAVE",4)) throw Parse_error_str("missing WAVE"); 186 | } 187 | 188 | // read chunks 189 | uint32_t chunk_offset = 12; 190 | while (chunk_offset < _riff_size) 191 | { 192 | 193 | if (chunk_offset + 8 > _riff_size) throw Parse_error_str("chunk header truncated"); 194 | 195 | char chunk_type[4]; 196 | memcpy(chunk_type, &_infile_data.data[chunk_offset], 4); 197 | uint32_t chunk_size; 198 | 199 | chunk_size = _read_32(&_infile_data.data[chunk_offset + 4]); 200 | 201 | if (!memcmp(chunk_type,"fmt ",4)) 202 | { 203 | _fmt_offset = chunk_offset + 8; 204 | _fmt_size = chunk_size; 205 | } 206 | else if (!memcmp(chunk_type,"cue ",4)) 207 | { 208 | _cue_offset = chunk_offset + 8; 209 | _cue_size = chunk_size; 210 | } 211 | else if (!memcmp(chunk_type,"LIST",4)) 212 | { 213 | _LIST_offset = chunk_offset + 8; 214 | _LIST_size = chunk_size; 215 | } 216 | else if (!memcmp(chunk_type,"smpl",4)) 217 | { 218 | _smpl_offset = chunk_offset + 8; 219 | _smpl_size = chunk_size; 220 | } 221 | else if (!memcmp(chunk_type,"vorb",4)) 222 | { 223 | _vorb_offset = chunk_offset + 8; 224 | _vorb_size = chunk_size; 225 | } 226 | else if (!memcmp(chunk_type,"data",4)) 227 | { 228 | _data_offset = chunk_offset + 8; 229 | _data_size = chunk_size; 230 | } 231 | 232 | chunk_offset = chunk_offset + 8 + chunk_size; 233 | } 234 | 235 | if (chunk_offset > _riff_size) throw Parse_error_str("chunk truncated"); 236 | 237 | // check that we have the chunks we're expecting 238 | if (-1 == _fmt_offset && -1 == _data_offset) throw Parse_error_str("expected fmt, data chunks"); 239 | 240 | // read fmt 241 | if (_vorb_offset == -1) { 242 | if (_fmt_size == 0x18) { // wav data 243 | _is_wav = true; 244 | v_printf(1, ".wav\"\n"); 245 | } else if (_fmt_size == 0x42) { // ogg data 246 | // fake it out 247 | _vorb_offset = _fmt_offset + 0x18; 248 | v_printf(1, ".ogg\"\n"); 249 | } else { 250 | throw Parse_error_str("expected fmt_size of 0x18 or 0x42 if vorb section missing"); 251 | } 252 | } else if (_fmt_size != 0x28 && _fmt_size != 0x18 && _fmt_size != 0x12) { 253 | throw Parse_error_str("bad fmt size"); 254 | } 255 | 256 | uint16_t codec_id = _read_16(&_infile_data.data[_fmt_offset]); 257 | if ((_is_wav && codec_id != 0xFFFE) || (!_is_wav && codec_id != 0xFFFF)) throw Parse_error_str("bad codec id"); 258 | _channels = _read_16(&_infile_data.data[_fmt_offset + 2]); 259 | _sample_rate = _read_32(&_infile_data.data[_fmt_offset + 4]); 260 | _avg_bytes_per_second = _read_32(&_infile_data.data[_fmt_offset + 8]); 261 | _block_align = _read_16(&_infile_data.data[_fmt_offset + 12]); 262 | _bits_per_sample = _read_16(&_infile_data.data[_fmt_offset + 14]); 263 | if (_fmt_size-0x12 != _read_16(&_infile_data.data[_fmt_offset + 16])) throw Parse_error_str("bad extra fmt length"); 264 | 265 | if (_fmt_size-0x12 >= 2) { 266 | // read extra fmt 267 | _ext_unk = _read_16(&_infile_data.data[_fmt_offset + 18]); 268 | if (_fmt_size-0x12 >= 6) { 269 | _subtype = _read_32(&_infile_data.data[_fmt_offset + 20]); 270 | } 271 | } 272 | 273 | if (_fmt_size == 0x28) 274 | { 275 | char whoknowsbuf[16]; 276 | const unsigned char whoknowsbuf_check[16] = {1,0,0,0, 0,0,0x10,0, 0x80,0,0,0xAA, 0,0x38,0x9b,0x71}; 277 | memcpy(whoknowsbuf, &_infile_data.data[_fmt_offset + 24], 16); 278 | if (memcmp(whoknowsbuf, whoknowsbuf_check, 16)) throw Parse_error_str("expected signature in extra fmt?"); 279 | } 280 | 281 | if (_is_wav) return; 282 | 283 | // read cue 284 | if (-1 != _cue_offset) 285 | { 286 | #if 0 287 | if (0x1c != _cue_size) throw Parse_error_str("bad cue size"); 288 | #endif 289 | _cue_count = _read_32(&_infile_data.data[_cue_offset]); 290 | } 291 | 292 | // read LIST 293 | if (-1 != _LIST_offset) 294 | { 295 | #if 0 296 | if ( 4 != _LIST_size ) throw Parse_error_str("bad LIST size"); 297 | char adtlbuf[4]; 298 | const char adtlbuf_check[4] = {'a','d','t','l'}; 299 | _infile.seekg(_LIST_offset); 300 | _infile.read(adtlbuf, 4); 301 | if (memcmp(adtlbuf, adtlbuf_check, 4)) throw Parse_error_str("expected only adtl in LIST"); 302 | #endif 303 | } 304 | 305 | // read smpl 306 | if (-1 != _smpl_offset) 307 | { 308 | _loop_count = _read_32(&_infile_data.data[_smpl_offset + 0x1C]); 309 | 310 | if (1 != _loop_count) throw Parse_error_str("expected one loop"); 311 | 312 | _loop_start = _read_32(&_infile_data.data[_smpl_offset + 0x2C]); 313 | _loop_end = _read_32(&_infile_data.data[_smpl_offset + 0x30]); 314 | } 315 | 316 | // read vorb 317 | switch (_vorb_size) 318 | { 319 | case -1: 320 | case 0x28: 321 | case 0x2A: 322 | case 0x2C: 323 | case 0x32: 324 | case 0x34: 325 | break; 326 | 327 | default: 328 | throw Parse_error_str("bad vorb size"); 329 | break; 330 | } 331 | 332 | _sample_count = _read_32(&_infile_data.data[_vorb_offset]); 333 | 334 | int file_pos; 335 | switch (_vorb_size) 336 | { 337 | case -1: 338 | case 0x2A: 339 | { 340 | _no_granule = true; 341 | 342 | // _infile.seekg(_vorb_offset + 0x4, ios::beg); 343 | uint32_t mod_signal = _read_32(&_infile_data.data[_vorb_offset + 0x4]); 344 | 345 | // set 346 | // D9 11011001 347 | // CB 11001011 348 | // BC 10111100 349 | // B2 10110010 350 | // unset 351 | // 4A 01001010 352 | // 4B 01001011 353 | // 69 01101001 354 | // 70 01110000 355 | // A7 10100111 !!! 356 | 357 | // seems to be 0xD9 when _mod_packets should be set 358 | // also seen 0xCB, 0xBC, 0xB2 359 | if (0x4A != mod_signal && 0x4B != mod_signal && 0x69 != mod_signal && 0x70 != mod_signal) 360 | { 361 | _mod_packets = true; 362 | } 363 | file_pos = _vorb_offset + 0x10; 364 | break; 365 | } 366 | 367 | default: 368 | file_pos = _vorb_offset + 0x18; 369 | break; 370 | } 371 | 372 | if (force_packet_format == kForceNoModPackets) 373 | { 374 | _mod_packets = false; 375 | } 376 | else if (force_packet_format == kForceModPackets) 377 | { 378 | _mod_packets = true; 379 | } 380 | 381 | _setup_packet_offset = _read_32(&_infile_data.data[file_pos]); 382 | _first_audio_packet_offset = _read_32(&_infile_data.data[file_pos + 4]); 383 | 384 | switch (_vorb_size) 385 | { 386 | case -1: 387 | case 0x2A: 388 | file_pos = _vorb_offset + 0x24; 389 | break; 390 | 391 | case 0x32: 392 | case 0x34: 393 | file_pos = _vorb_offset + 0x2C; 394 | break; 395 | } 396 | 397 | switch(_vorb_size) 398 | { 399 | case 0x28: 400 | case 0x2C: 401 | // ok to leave _uid, _blocksize_0_pow and _blocksize_1_pow unset 402 | _header_triad_present = true; 403 | _old_packet_headers = true; 404 | break; 405 | 406 | case -1: 407 | case 0x2A: 408 | case 0x32: 409 | case 0x34: 410 | _uid = _read_32(&_infile_data.data[file_pos]); 411 | _blocksize_0_pow = _infile_data.data[file_pos + 4]; 412 | _blocksize_1_pow = _infile_data.data[file_pos + 5]; 413 | break; 414 | } 415 | 416 | // check/set loops now that we know total sample count 417 | if (0 != _loop_count) 418 | { 419 | if (_loop_end == 0) 420 | { 421 | _loop_end = _sample_count; 422 | } 423 | else 424 | { 425 | _loop_end = _loop_end + 1; 426 | } 427 | 428 | if (_loop_start >= _sample_count || _loop_end > _sample_count || _loop_start > _loop_end) 429 | throw Parse_error_str("loops out of range"); 430 | } 431 | 432 | // check subtype now that we know the vorb info 433 | // this is clearly just the channel layout 434 | switch (_subtype) 435 | { 436 | case 4: /* 1 channel, no seek table */ 437 | case 3: /* 2 channels */ 438 | case 0x33: /* 4 channels */ 439 | case 0x37: /* 5 channels, seek or not */ 440 | case 0x3b: /* 5 channels, no seek table */ 441 | case 0x3f: /* 6 channels, no seek table */ 442 | break; 443 | default: 444 | //throw Parse_error_str("unknown subtype"); 445 | break; 446 | } 447 | } 448 | 449 | void Wwise_RIFF_Vorbis::print_info(void) 450 | { 451 | printf("%s %d channel%s %u HZ %u bps %u samples\n", 452 | _little_endian ? "RIFF WAVE" : "RIFX WAVE", 453 | _channels, 454 | _channels != 1 ? "s" : "", 455 | _sample_rate, 456 | _avg_bytes_per_second*8, 457 | _sample_count); 458 | 459 | if (0 != _loop_count) 460 | { 461 | printf("loop from %u to %u\n", _loop_start, _loop_end); 462 | } 463 | 464 | if (_old_packet_headers) 465 | { 466 | puts("- 8 byte (old) packet headers"); 467 | } 468 | else if (_no_granule) 469 | { 470 | puts("- 2 byte packet headers, no granule"); 471 | } 472 | else 473 | { 474 | puts("- 6 byte packet headers"); 475 | } 476 | 477 | if (_header_triad_present) 478 | { 479 | puts("- Vorbis header triad present"); 480 | } 481 | 482 | if (_full_setup || _header_triad_present) 483 | { 484 | puts("- full setup header"); 485 | } 486 | else 487 | { 488 | puts("- stripped setup header"); 489 | } 490 | 491 | if (_inline_codebooks || _header_triad_present) 492 | { 493 | puts("- inline codebooks"); 494 | } 495 | else 496 | { 497 | printf("- external codebooks (%s)\n", _codebooks_name.c_str()); 498 | } 499 | 500 | if (_mod_packets) 501 | { 502 | puts("- modified Vorbis packets"); 503 | } 504 | else 505 | { 506 | puts("- standard Vorbis packets"); 507 | } 508 | 509 | #if 0 510 | if (0 != _cue_count) 511 | { 512 | printf("%u cue point%s\n", _cue_count, _cue_count != 1 ? "s" : ""); 513 | } 514 | #endif 515 | } 516 | 517 | void Wwise_RIFF_Vorbis::generate_ogg_header(Bit_oggstream& os, bool * & mode_blockflag, int & mode_bits) 518 | { 519 | // generate identification packet 520 | { 521 | Vorbis_packet_header vhead(1); 522 | 523 | os << vhead; 524 | 525 | Bit_uint<32> version(0); 526 | os << version; 527 | 528 | Bit_uint<8> ch(_channels); 529 | os << ch; 530 | 531 | Bit_uint<32> srate(_sample_rate); 532 | os << srate; 533 | 534 | Bit_uint<32> bitrate_max(0); 535 | os << bitrate_max; 536 | 537 | Bit_uint<32> bitrate_nominal(_avg_bytes_per_second * 8); 538 | os << bitrate_nominal; 539 | 540 | Bit_uint<32> bitrate_minimum(0); 541 | os << bitrate_minimum; 542 | 543 | Bit_uint<4> blocksize_0(_blocksize_0_pow); 544 | os << blocksize_0; 545 | 546 | Bit_uint<4> blocksize_1(_blocksize_1_pow); 547 | os << blocksize_1; 548 | 549 | Bit_uint<1> framing(1); 550 | os << framing; 551 | 552 | // identification packet on its own page 553 | os.flush_page(); 554 | } 555 | 556 | // generate comment packet 557 | { 558 | Vorbis_packet_header vhead(3); 559 | 560 | os << vhead; 561 | 562 | static const char vendor[] = "converted from Audiokinetic Wwise by ww2ogg " VERSION; 563 | Bit_uint<32> vendor_size(strlen(vendor)); 564 | 565 | os << vendor_size; 566 | for (unsigned int i = 0; i < vendor_size; i ++) { 567 | Bit_uint<8> c(vendor[i]); 568 | os << c; 569 | } 570 | 571 | if (0 == _loop_count) 572 | { 573 | // no user comments 574 | Bit_uint<32> user_comment_count(0); 575 | os << user_comment_count; 576 | } 577 | else 578 | { 579 | // two comments, loop start and end 580 | Bit_uint<32> user_comment_count(2); 581 | os << user_comment_count; 582 | 583 | string loop_start_str = "LoopStart=" + std::to_string(_loop_start); 584 | Bit_uint<32> loop_start_comment_length; 585 | loop_start_comment_length = loop_start_str.size(); 586 | os << loop_start_comment_length; 587 | for (unsigned int i = 0; i < loop_start_comment_length; i++) 588 | { 589 | Bit_uint<8> c(loop_start_str[i]); 590 | os << c; 591 | } 592 | 593 | string loop_end_str = "LoopEnd=" + std::to_string(_loop_end); 594 | Bit_uint<32> loop_end_comment_length; 595 | loop_end_comment_length = loop_end_str.size(); 596 | os << loop_end_comment_length; 597 | for (unsigned int i = 0; i < loop_end_comment_length; i++) 598 | { 599 | Bit_uint<8> c(loop_end_str[i]); 600 | os << c; 601 | } 602 | } 603 | 604 | Bit_uint<1> framing(1); 605 | os << framing; 606 | 607 | //os.flush_bits(); 608 | os.flush_page(); 609 | } 610 | 611 | // generate setup packet 612 | { 613 | Vorbis_packet_header vhead(5); 614 | 615 | os << vhead; 616 | 617 | Packet setup_packet(_infile_data, _data_offset + _setup_packet_offset, _little_endian, _no_granule); 618 | 619 | if (setup_packet.granule() != 0) throw Parse_error_str("setup packet granule != 0"); 620 | Bit_stream ss(_infile_data, setup_packet.offset()); 621 | 622 | // codebook count 623 | Bit_uint<8> codebook_count_less1; 624 | ss >> codebook_count_less1; 625 | unsigned int codebook_count = codebook_count_less1 + 1; 626 | os << codebook_count_less1; 627 | 628 | //cout << codebook_count << " codebooks" << endl; 629 | 630 | // rebuild codebooks 631 | if (_inline_codebooks) 632 | { 633 | codebook_library cbl; 634 | 635 | for (unsigned int i = 0; i < codebook_count; i++) 636 | { 637 | if (_full_setup) 638 | { 639 | cbl.copy(ss, os); 640 | } 641 | else 642 | { 643 | cbl.rebuild(ss, 0, os); 644 | } 645 | } 646 | } 647 | else 648 | { 649 | /* external codebooks */ 650 | 651 | codebook_library cbl((unsigned char*) main_codebook, 74387); 652 | 653 | for (unsigned int i = 0; i < codebook_count; i++) 654 | { 655 | Bit_uint<10> codebook_id; 656 | ss >> codebook_id; 657 | //cout << "Codebook " << i << " = " << codebook_id << endl; 658 | try 659 | { 660 | cbl.rebuild(codebook_id, os); 661 | } 662 | catch (const Invalid_id & e) 663 | { 664 | // B C V 665 | // 4 2 4 3 5 6 666 | // 0100 0010 0100 0011 0101 0110 667 | // \_______|____ ___|/ 668 | // X 669 | // 11 0100 0010 670 | 671 | if (codebook_id == 0x342) 672 | { 673 | Bit_uint<14> codebook_identifier; 674 | ss >> codebook_identifier; 675 | 676 | // B C V 677 | // 4 2 4 3 5 6 678 | // 0100 0010 0100 0011 0101 0110 679 | // \_____|_ _|_______/ 680 | // X 681 | // 01 0101 10 01 0000 682 | if (codebook_identifier == 0x1590) 683 | { 684 | // starts with BCV, probably --full-setup 685 | throw Parse_error_str( 686 | "invalid codebook id 0x342, try --full-setup"); 687 | } 688 | } 689 | 690 | // just an invalid codebook 691 | throw; 692 | } 693 | } 694 | } 695 | 696 | // Time Domain transforms (placeholder) 697 | Bit_uint<6> time_count_less1(0); 698 | os << time_count_less1; 699 | Bit_uint<16> dummy_time_value(0); 700 | os << dummy_time_value; 701 | 702 | if (_full_setup) 703 | { 704 | 705 | while (ss.get_total_bits_read() < setup_packet.size()*8u) 706 | { 707 | Bit_uint<1> bitly; 708 | ss >> bitly; 709 | os << bitly; 710 | } 711 | } 712 | else // _full_setup 713 | { 714 | // floor count 715 | Bit_uint<6> floor_count_less1; 716 | ss >> floor_count_less1; 717 | unsigned int floor_count = floor_count_less1 + 1; 718 | os << floor_count_less1; 719 | 720 | // rebuild floors 721 | for (unsigned int i = 0; i < floor_count; i++) 722 | { 723 | // Always floor type 1 724 | Bit_uint<16> floor_type(1); 725 | os << floor_type; 726 | 727 | Bit_uint<5> floor1_partitions; 728 | ss >> floor1_partitions; 729 | os << floor1_partitions; 730 | 731 | unsigned int * floor1_partition_class_list = new unsigned int [floor1_partitions]; 732 | 733 | unsigned int maximum_class = 0; 734 | for (unsigned int j = 0; j < floor1_partitions; j++) 735 | { 736 | Bit_uint<4> floor1_partition_class; 737 | ss >> floor1_partition_class; 738 | os << floor1_partition_class; 739 | 740 | floor1_partition_class_list[j] = floor1_partition_class; 741 | 742 | if (floor1_partition_class > maximum_class) 743 | maximum_class = floor1_partition_class; 744 | } 745 | 746 | unsigned int * floor1_class_dimensions_list = new unsigned int [maximum_class+1]; 747 | 748 | for (unsigned int j = 0; j <= maximum_class; j++) 749 | { 750 | Bit_uint<3> class_dimensions_less1; 751 | ss >> class_dimensions_less1; 752 | os << class_dimensions_less1; 753 | 754 | floor1_class_dimensions_list[j] = class_dimensions_less1 + 1; 755 | 756 | Bit_uint<2> class_subclasses; 757 | ss >> class_subclasses; 758 | os << class_subclasses; 759 | 760 | if (0 != class_subclasses) 761 | { 762 | Bit_uint<8> masterbook; 763 | ss >> masterbook; 764 | os << masterbook; 765 | 766 | if (masterbook >= codebook_count) 767 | throw Parse_error_str("invalid floor1 masterbook"); 768 | } 769 | 770 | for (unsigned int k = 0; k < (1U< subclass_book_plus1; 773 | ss >> subclass_book_plus1; 774 | os << subclass_book_plus1; 775 | 776 | int subclass_book = static_cast(subclass_book_plus1)-1; 777 | if (subclass_book >= 0 && static_cast(subclass_book) >= codebook_count) 778 | throw Parse_error_str("invalid floor1 subclass book"); 779 | } 780 | } 781 | 782 | Bit_uint<2> floor1_multiplier_less1; 783 | ss >> floor1_multiplier_less1; 784 | os << floor1_multiplier_less1; 785 | 786 | Bit_uint<4> rangebits; 787 | ss >> rangebits; 788 | os << rangebits; 789 | 790 | for (unsigned int j = 0; j < floor1_partitions; j++) 791 | { 792 | unsigned int current_class_number = floor1_partition_class_list[j]; 793 | for (unsigned int k = 0; k < floor1_class_dimensions_list[current_class_number]; k++) 794 | { 795 | Bit_uintv X(rangebits); 796 | ss >> X; 797 | os << X; 798 | } 799 | } 800 | 801 | delete [] floor1_class_dimensions_list; 802 | delete [] floor1_partition_class_list; 803 | } 804 | 805 | // residue count 806 | Bit_uint<6> residue_count_less1; 807 | ss >> residue_count_less1; 808 | unsigned int residue_count = residue_count_less1 + 1; 809 | os << residue_count_less1; 810 | 811 | // rebuild residues 812 | for (unsigned int i = 0; i < residue_count; i++) 813 | { 814 | Bit_uint<2> residue_type; 815 | ss >> residue_type; 816 | os << Bit_uint<16>(residue_type); 817 | 818 | if (residue_type > 2) throw Parse_error_str("invalid residue type"); 819 | 820 | Bit_uint<24> residue_begin, residue_end, residue_partition_size_less1; 821 | Bit_uint<6> residue_classifications_less1; 822 | Bit_uint<8> residue_classbook; 823 | 824 | ss >> residue_begin >> residue_end >> residue_partition_size_less1 >> residue_classifications_less1 >> residue_classbook; 825 | unsigned int residue_classifications = residue_classifications_less1 + 1; 826 | os << residue_begin << residue_end << residue_partition_size_less1 << residue_classifications_less1 << residue_classbook; 827 | 828 | if (residue_classbook >= codebook_count) throw Parse_error_str("invalid residue classbook"); 829 | 830 | unsigned int * residue_cascade = new unsigned int [residue_classifications]; 831 | 832 | for (unsigned int j = 0; j < residue_classifications; j++) 833 | { 834 | Bit_uint<5> high_bits(0); 835 | Bit_uint<3> low_bits; 836 | 837 | ss >> low_bits; 838 | os << low_bits; 839 | 840 | Bit_uint<1> bitflag; 841 | ss >> bitflag; 842 | os << bitflag; 843 | if (bitflag) 844 | { 845 | ss >> high_bits; 846 | os << high_bits; 847 | } 848 | 849 | residue_cascade[j] = high_bits * 8 + low_bits; 850 | } 851 | 852 | for (unsigned int j = 0; j < residue_classifications; j++) 853 | { 854 | for (unsigned int k = 0; k < 8; k++) 855 | { 856 | if (residue_cascade[j] & (1 << k)) 857 | { 858 | Bit_uint<8> residue_book; 859 | ss >> residue_book; 860 | os << residue_book; 861 | 862 | if (residue_book >= codebook_count) throw Parse_error_str("invalid residue book"); 863 | } 864 | } 865 | } 866 | 867 | delete [] residue_cascade; 868 | } 869 | 870 | // mapping count 871 | Bit_uint<6> mapping_count_less1; 872 | ss >> mapping_count_less1; 873 | unsigned int mapping_count = mapping_count_less1 + 1; 874 | os << mapping_count_less1; 875 | 876 | for (unsigned int i = 0; i < mapping_count; i++) 877 | { 878 | // always mapping type 0, the only one 879 | Bit_uint<16> mapping_type(0); 880 | 881 | os << mapping_type; 882 | 883 | Bit_uint<1> submaps_flag; 884 | ss >> submaps_flag; 885 | os << submaps_flag; 886 | 887 | unsigned int submaps = 1; 888 | if (submaps_flag) 889 | { 890 | Bit_uint<4> submaps_less1; 891 | 892 | ss >> submaps_less1; 893 | submaps = submaps_less1 + 1; 894 | os << submaps_less1; 895 | } 896 | 897 | Bit_uint<1> square_polar_flag; 898 | ss >> square_polar_flag; 899 | os << square_polar_flag; 900 | 901 | if (square_polar_flag) 902 | { 903 | Bit_uint<8> coupling_steps_less1; 904 | ss >> coupling_steps_less1; 905 | unsigned int coupling_steps = coupling_steps_less1 + 1; 906 | os << coupling_steps_less1; 907 | 908 | for (unsigned int j = 0; j < coupling_steps; j++) 909 | { 910 | Bit_uintv magnitude(ilog(_channels-1)), angle(ilog(_channels-1)); 911 | 912 | ss >> magnitude >> angle; 913 | os << magnitude << angle; 914 | 915 | if (angle == magnitude || magnitude >= _channels || angle >= _channels) throw Parse_error_str("invalid coupling"); 916 | } 917 | } 918 | 919 | // a rare reserved field not removed by Ak! 920 | Bit_uint<2> mapping_reserved; 921 | ss >> mapping_reserved; 922 | os << mapping_reserved; 923 | if (0 != mapping_reserved) throw Parse_error_str("mapping reserved field nonzero"); 924 | 925 | if (submaps > 1) 926 | { 927 | for (unsigned int j = 0; j < _channels; j++) 928 | { 929 | Bit_uint<4> mapping_mux; 930 | ss >> mapping_mux; 931 | os << mapping_mux; 932 | 933 | if (mapping_mux >= submaps) throw Parse_error_str("mapping_mux >= submaps"); 934 | } 935 | } 936 | 937 | for (unsigned int j = 0; j < submaps; j++) 938 | { 939 | // Another! Unused time domain transform configuration placeholder! 940 | Bit_uint<8> time_config; 941 | ss >> time_config; 942 | os << time_config; 943 | 944 | Bit_uint<8> floor_number; 945 | ss >> floor_number; 946 | os << floor_number; 947 | if (floor_number >= floor_count) throw Parse_error_str("invalid floor mapping"); 948 | 949 | Bit_uint<8> residue_number; 950 | ss >> residue_number; 951 | os << residue_number; 952 | if (residue_number >= residue_count) throw Parse_error_str("invalid residue mapping"); 953 | } 954 | } 955 | 956 | // mode count 957 | Bit_uint<6> mode_count_less1; 958 | ss >> mode_count_less1; 959 | unsigned int mode_count = mode_count_less1 + 1; 960 | os << mode_count_less1; 961 | 962 | mode_blockflag = new bool [mode_count]; 963 | mode_bits = ilog(mode_count-1); 964 | 965 | //cout << mode_count << " modes" << endl; 966 | 967 | for (unsigned int i = 0; i < mode_count; i++) 968 | { 969 | Bit_uint<1> block_flag; 970 | ss >> block_flag; 971 | os << block_flag; 972 | 973 | mode_blockflag[i] = (block_flag != 0); 974 | 975 | // only 0 valid for windowtype and transformtype 976 | Bit_uint<16> windowtype(0), transformtype(0); 977 | os << windowtype << transformtype; 978 | 979 | Bit_uint<8> mapping; 980 | ss >> mapping; 981 | os << mapping; 982 | if (mapping >= mapping_count) throw Parse_error_str("invalid mode mapping"); 983 | } 984 | 985 | Bit_uint<1> framing(1); 986 | os << framing; 987 | 988 | } // _full_setup 989 | 990 | os.flush_page(); 991 | 992 | if ((ss.get_total_bits_read()+7)/8 != setup_packet.size()) throw Parse_error_str("didn't read exactly setup packet"); 993 | 994 | if (setup_packet.next_offset() != _data_offset + static_cast(_first_audio_packet_offset)) throw Parse_error_str("first audio packet doesn't follow setup packet"); 995 | 996 | } 997 | } 998 | 999 | void Wwise_RIFF_Vorbis::generate_wav_header(BinaryData& bd) 1000 | { 1001 | struct wav_header { 1002 | const char riff[4] = {'R', 'I', 'F', 'F'}; 1003 | uint32_t file_size; 1004 | const char wave[4] = {'W', 'A', 'V', 'E'}; 1005 | const char fmt[4] = {'f', 'm', 't', ' '}; 1006 | const uint32_t fmt_length = 0x10; 1007 | const uint16_t fmt_tag = 0x1; 1008 | uint16_t channels; 1009 | uint32_t sample_rate; 1010 | uint32_t avg_bytes_per_second; 1011 | uint16_t block_align; 1012 | uint16_t bits_per_sample; 1013 | }; 1014 | static_assert(sizeof(struct wav_header) == 36); 1015 | bd.data = (uint8_t*) realloc(bd.data, bd.length + 36); 1016 | struct wav_header WavHeader = { 1017 | .file_size = 44 + (uint32_t) _data_size, 1018 | .channels = _channels, 1019 | .sample_rate = _sample_rate, 1020 | .avg_bytes_per_second = _avg_bytes_per_second, 1021 | .block_align = _block_align, 1022 | .bits_per_sample = _bits_per_sample 1023 | }; 1024 | memcpy(&bd.data[bd.length], &WavHeader, sizeof(WavHeader)); 1025 | bd.length += 36; 1026 | } 1027 | 1028 | void Wwise_RIFF_Vorbis::generate_ogg(BinaryData& outputdata) 1029 | { 1030 | Bit_oggstream os(outputdata); 1031 | 1032 | bool * mode_blockflag = NULL; 1033 | int mode_bits = 0; 1034 | bool prev_blockflag = false; 1035 | 1036 | if (_is_wav) 1037 | { 1038 | generate_wav_header(outputdata); 1039 | outputdata.data = (uint8_t*) realloc(outputdata.data, outputdata.length + 8 + _data_size); 1040 | memcpy(&outputdata.data[outputdata.length], "data", 4); 1041 | memcpy(&outputdata.data[outputdata.length + 4], &_data_size, 4); 1042 | memcpy(&outputdata.data[outputdata.length + 8], &_infile_data.data[_data_offset], _data_size); 1043 | outputdata.length += 8 + _data_size; 1044 | return; 1045 | } 1046 | else if (_header_triad_present) 1047 | { 1048 | generate_ogg_header_with_triad(os); 1049 | } 1050 | else 1051 | { 1052 | generate_ogg_header(os, mode_blockflag, mode_bits); 1053 | } 1054 | 1055 | // Audio pages 1056 | { 1057 | long offset = _data_offset + _first_audio_packet_offset; 1058 | 1059 | while (offset < _data_offset + _data_size) 1060 | { 1061 | uint32_t size, granule; 1062 | long packet_header_size, packet_payload_offset, next_offset; 1063 | 1064 | if (_old_packet_headers) 1065 | { 1066 | Packet_8 audio_packet(_infile_data, offset, _little_endian); 1067 | packet_header_size = audio_packet.header_size(); 1068 | size = audio_packet.size(); 1069 | packet_payload_offset = audio_packet.offset(); 1070 | granule = audio_packet.granule(); 1071 | next_offset = audio_packet.next_offset(); 1072 | } 1073 | else 1074 | { 1075 | Packet audio_packet(_infile_data, offset, _little_endian, _no_granule); 1076 | packet_header_size = audio_packet.header_size(); 1077 | size = audio_packet.size(); 1078 | packet_payload_offset = audio_packet.offset(); 1079 | granule = audio_packet.granule(); 1080 | next_offset = audio_packet.next_offset(); 1081 | } 1082 | 1083 | if (offset + packet_header_size > _data_offset + _data_size) { 1084 | throw Parse_error_str("page header truncated"); 1085 | } 1086 | 1087 | offset = packet_payload_offset; 1088 | 1089 | // HACK: don't know what to do here 1090 | if (granule == UINT32_C(0xFFFFFFFF)) 1091 | { 1092 | os.set_granule(1); 1093 | } 1094 | else 1095 | { 1096 | os.set_granule(granule); 1097 | } 1098 | 1099 | // first byte 1100 | if (_mod_packets) 1101 | { 1102 | // need to rebuild packet type and window info 1103 | 1104 | if (!mode_blockflag) 1105 | { 1106 | throw Parse_error_str("didn't load mode_blockflag"); 1107 | } 1108 | 1109 | // OUT: 1 bit packet type (0 == audio) 1110 | Bit_uint<1> packet_type(0); 1111 | os << packet_type; 1112 | 1113 | Bit_uintv * mode_number_p = 0; 1114 | Bit_uintv * remainder_p = 0; 1115 | 1116 | { 1117 | // collect mode number from first byte 1118 | 1119 | Bit_stream ss(_infile_data, offset); 1120 | 1121 | // IN/OUT: N bit mode number (max 6 bits) 1122 | mode_number_p = new Bit_uintv(mode_bits); 1123 | ss >> *mode_number_p; 1124 | os << *mode_number_p; 1125 | 1126 | // IN: remaining bits of first (input) byte 1127 | remainder_p = new Bit_uintv(8-mode_bits); 1128 | ss >> *remainder_p; 1129 | } 1130 | 1131 | if (mode_blockflag[*mode_number_p]) 1132 | { 1133 | // long window, peek at next frame 1134 | 1135 | bool next_blockflag = false; 1136 | if (next_offset + packet_header_size <= _data_offset + _data_size) 1137 | { 1138 | 1139 | // mod_packets always goes with 6-byte headers 1140 | Packet audio_packet(_infile_data, next_offset, _little_endian, _no_granule); 1141 | uint32_t next_packet_size = audio_packet.size(); 1142 | if (next_packet_size > 0) 1143 | { 1144 | Bit_stream ss(_infile_data, audio_packet.offset()); 1145 | Bit_uintv next_mode_number(mode_bits); 1146 | 1147 | ss >> next_mode_number; 1148 | 1149 | next_blockflag = mode_blockflag[next_mode_number]; 1150 | } 1151 | } 1152 | 1153 | // OUT: previous window type bit 1154 | Bit_uint<1> prev_window_type(prev_blockflag); 1155 | os << prev_window_type; 1156 | 1157 | // OUT: next window type bit 1158 | Bit_uint<1> next_window_type(next_blockflag); 1159 | os << next_window_type; 1160 | } 1161 | 1162 | prev_blockflag = mode_blockflag[*mode_number_p]; 1163 | delete mode_number_p; 1164 | 1165 | // OUT: remaining bits of first (input) byte 1166 | os << *remainder_p; 1167 | delete remainder_p; 1168 | } 1169 | else 1170 | { 1171 | // nothing unusual for first byte 1172 | int v = _infile_data.data[offset]; 1173 | if (v < 0) 1174 | { 1175 | throw Parse_error_str("file truncated"); 1176 | } 1177 | Bit_uint<8> c(v); 1178 | os << c; 1179 | } 1180 | 1181 | // remainder of packet 1182 | for (unsigned int i = 1; i < size; i++) 1183 | { 1184 | int v = _infile_data.data[offset + i]; 1185 | if (v < 0) 1186 | { 1187 | throw Parse_error_str("file truncated"); 1188 | } 1189 | Bit_uint<8> c(v); 1190 | os << c; 1191 | } 1192 | 1193 | offset = next_offset; 1194 | os.flush_page( false, (offset == _data_offset + _data_size) ); 1195 | } 1196 | 1197 | if (offset > _data_offset + _data_size) throw Parse_error_str("page truncated"); 1198 | } 1199 | 1200 | delete [] mode_blockflag; 1201 | } 1202 | 1203 | void Wwise_RIFF_Vorbis::generate_ogg_header_with_triad(Bit_oggstream& os) 1204 | { 1205 | // Header page triad 1206 | { 1207 | long offset = _data_offset + _setup_packet_offset; 1208 | 1209 | // copy information packet 1210 | { 1211 | Packet_8 information_packet(_infile_data, offset, _little_endian); 1212 | uint32_t size = information_packet.size(); 1213 | 1214 | if (information_packet.granule() != 0) 1215 | { 1216 | throw Parse_error_str("information packet granule != 0"); 1217 | } 1218 | 1219 | Bit_uint<8> c(_infile_data.data[information_packet.offset()]); 1220 | if (1 != c) 1221 | { 1222 | throw Parse_error_str("wrong type for information packet"); 1223 | } 1224 | 1225 | os << c; 1226 | 1227 | for (unsigned int i = 1; i < size; i++) 1228 | { 1229 | c = _infile_data.data[information_packet.offset() + i]; 1230 | os << c; 1231 | } 1232 | 1233 | // identification packet on its own page 1234 | os.flush_page(); 1235 | 1236 | offset = information_packet.next_offset(); 1237 | } 1238 | 1239 | // copy comment packet 1240 | { 1241 | Packet_8 comment_packet(_infile_data, offset, _little_endian); 1242 | uint16_t size = comment_packet.size(); 1243 | 1244 | if (comment_packet.granule() != 0) 1245 | { 1246 | throw Parse_error_str("comment packet granule != 0"); 1247 | } 1248 | 1249 | Bit_uint<8> c(_infile_data.data[comment_packet.offset()]); 1250 | if (3 != c) 1251 | { 1252 | throw Parse_error_str("wrong type for comment packet"); 1253 | } 1254 | 1255 | os << c; 1256 | 1257 | for (unsigned int i = 1; i < size; i++) 1258 | { 1259 | c = _infile_data.data[comment_packet.offset() + i]; 1260 | os << c; 1261 | } 1262 | 1263 | // identification packet on its own page 1264 | os.flush_page(); 1265 | 1266 | offset = comment_packet.next_offset(); 1267 | } 1268 | 1269 | // copy setup packet 1270 | { 1271 | Packet_8 setup_packet(_infile_data, offset, _little_endian); 1272 | 1273 | if (setup_packet.granule() != 0) throw Parse_error_str("setup packet granule != 0"); 1274 | Bit_stream ss(_infile_data, setup_packet.offset()); 1275 | 1276 | Bit_uint<8> c; 1277 | ss >> c; 1278 | 1279 | // type 1280 | if (5 != c) 1281 | { 1282 | throw Parse_error_str("wrong type for setup packet"); 1283 | } 1284 | os << c; 1285 | 1286 | // 'vorbis' 1287 | for (unsigned int i = 0; i < 6; i++) 1288 | { 1289 | ss >> c; 1290 | os << c; 1291 | } 1292 | 1293 | // codebook count 1294 | Bit_uint<8> codebook_count_less1; 1295 | ss >> codebook_count_less1; 1296 | unsigned int codebook_count = codebook_count_less1 + 1; 1297 | os << codebook_count_less1; 1298 | 1299 | codebook_library cbl; 1300 | 1301 | // rebuild codebooks 1302 | for (unsigned int i = 0; i < codebook_count; i++) 1303 | { 1304 | cbl.copy(ss, os); 1305 | } 1306 | 1307 | while (ss.get_total_bits_read() < setup_packet.size()*8u) 1308 | { 1309 | Bit_uint<1> bitly; 1310 | ss >> bitly; 1311 | os << bitly; 1312 | } 1313 | 1314 | os.flush_page(); 1315 | 1316 | offset = setup_packet.next_offset(); 1317 | } 1318 | 1319 | if (offset != _data_offset + static_cast(_first_audio_packet_offset)) throw Parse_error_str("first audio packet doesn't follow setup packet"); 1320 | 1321 | } 1322 | 1323 | } 1324 | --------------------------------------------------------------------------------