Created
September 11, 2022 15:33
-
-
Save MajsterTynek/7afb88ee52a0e59b1fbcb83025eba198 to your computer and use it in GitHub Desktop.
Program that demonstrates conversion of SNF file into a WAV file. SNF file format comes from Dispel game. Visit our [reddit](https://www.reddit.com/r/DispelRPG/) and [discord](https://discord.gg/Kz23q2qb7c) server!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// PROTOTYPE PROGRAM THAT DEMONSTRATES | |
// CONVERTING SNF TO WAV FORMAT | |
#include <string> | |
#include <fstream> | |
#include <iostream> | |
#include <filesystem> | |
namespace fs = std::filesystem; | |
typedef unsigned char BYTE; // 1 byte | |
typedef short WORD; // 2 bytes | |
typedef int DWORD; // 4 bytes | |
typedef struct wavformat_tag { | |
WORD wFormatTag; // audio_format | |
WORD nChannels; // num_channels | |
DWORD nSamplesPerSec; // sample_rate | |
DWORD nAvgBytesPerSec; // byte_rate | |
WORD nBlockAlign; // block_align | |
WORD wBitsPerSample; // bits_per_sample | |
} PCMWAVEFORMAT; | |
// SNF is just shortened WAV | |
typedef struct snfformat_tag { // 22 bytes | |
DWORD sound_bytes; // 4 lenght of sound_data | |
WORD audio_format; // 2 wFormatTag | |
WORD num_channels; // 2 nChannels | |
DWORD sample_rate; // 4 nSamplesPerSec | |
DWORD byte_rate; // 4 nAvgBytesPerSec | |
WORD block_align; // 2 nBlockAlign | |
WORD bits_per_sample; // 2 wBitsPerSample | |
// "da" // 2 leftover of "data" | |
// sound_data // n encoded samples | |
} SNFFORMAT; | |
// stram read overload | |
template<typename T> | |
std::ifstream& operator>>(std::ifstream& str, T& var){ | |
return static_cast<std::ifstream&>(str.read((char*)&(var),sizeof(var))); | |
} | |
// simple macros for stream io | |
#define READ(var) read((char*)&(var),sizeof(var)) | |
#define WRITE(var) write((char*)&(var),sizeof(var)) | |
void load_snf(SNFFORMAT &snd, std::ifstream &s, char *&sound_data) | |
{ | |
WORD dummy; // for "da" skip | |
s >> snd.sound_bytes >> snd.audio_format >> snd.num_channels | |
>> snd.sample_rate >> snd.byte_rate >> snd.block_align | |
>> snd.bits_per_sample >> dummy; | |
sound_data = new char[snd.sound_bytes]; | |
s.read( sound_data, snd.sound_bytes ); | |
} | |
int main(int argc, char* argv[]) | |
{ | |
// we need file to convert | |
if ( argc != 2 ) return 1; | |
fs::path path(argv[1]); | |
// open snf file for conversion | |
std::ifstream snf( path, std::ios::binary ); | |
if ( !snf.is_open() ) return 2; | |
SNFFORMAT snd; // load SNF header structure | |
char* sound_data = nullptr; | |
load_snf( snd, snf, sound_data ); | |
if ( snf.fail() ) return 3; | |
snf.close(); | |
PCMWAVEFORMAT wav; // fit in WAV structure | |
wav.wFormatTag = snd.audio_format; | |
wav.nChannels = snd.num_channels; | |
wav.nSamplesPerSec = snd.sample_rate; | |
wav.nAvgBytesPerSec = snd.byte_rate; | |
wav.nBlockAlign = snd.block_align; | |
wav.wBitsPerSample = snd.bits_per_sample; | |
DWORD res_size = 44 + snd.sound_bytes; | |
DWORD pcmwav_size = 16; | |
path.replace_extension( "wav" ); | |
std::ofstream wav_file( path, std::ios::binary | std::ios::trunc ); | |
wav_file.write( "RIFF", 4 ); // build RIFF WAVE file | |
wav_file.WRITE( res_size ); // size of WAVE resource | |
wav_file.write( "WAVE", 4 ); // begin WAVE resource | |
wav_file.write( "fmt ", 4 ); // format of stored sound | |
wav_file.WRITE( pcmwav_size ); // PCMWAVEFORMAT | |
wav_file.WRITE( wav.wFormatTag ); | |
wav_file.WRITE( wav.nChannels ); | |
wav_file.WRITE( wav.nSamplesPerSec ); | |
wav_file.WRITE( wav.nAvgBytesPerSec ); | |
wav_file.WRITE( wav.nBlockAlign ); | |
wav_file.WRITE( wav.wBitsPerSample ); | |
wav_file.write( "data", 4 ); // sound samples | |
wav_file.write( sound_data, snd.sound_bytes ); | |
wav_file.close(); // go listen to it! | |
delete sound_data; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment