mirror of
https://github.com/mon/2dxTools.git
synced 2024-11-23 22:10:57 +01:00
Bout time I made a repo for this
This commit is contained in:
commit
9ec75bd24f
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
sox/
|
||||
*.dll
|
||||
*.wav
|
||||
*.exe
|
||||
*.o
|
19
2dx.h
Normal file
19
2dx.h
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
char name[16];
|
||||
uint32_t headerSize; // this + offsets table
|
||||
uint32_t fileCount;
|
||||
char unknown[48]; // contains rest of title, some random flags
|
||||
} fileHeader_t;
|
||||
|
||||
typedef struct{
|
||||
char dx[4]; // should be "2DX9";
|
||||
uint32_t headerSize; // always 24, includes dx chars
|
||||
uint32_t wavSize;
|
||||
int16_t unk1; // always 0x3231
|
||||
int16_t trackId; // always -1 for previews, 0-7 for song + effected versions, 9 to 11 used for a few effects
|
||||
int16_t unk2; // all 64, except song selection change 'click' is 40
|
||||
int16_t attenuation; // 0-127 for varying quietness
|
||||
int32_t loopPoint; // sample to loop at * 4
|
||||
} dxHeader_t;
|
88
2dxBuild.c
Normal file
88
2dxBuild.c
Normal file
@ -0,0 +1,88 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "2dx.h"
|
||||
#include "shared.h"
|
||||
|
||||
int count_wav(void) {
|
||||
int ret = 0;
|
||||
char filename[256];
|
||||
while(snprintf(filename, 256, "%d.wav", ret), file_exists(filename))
|
||||
ret++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void build_2dx(char* path) {
|
||||
int wavCount = count_wav();
|
||||
printf("Writing %d wavs to %s\n",wavCount, path);
|
||||
|
||||
FILE* outFile = fopen(path, "wb");
|
||||
|
||||
if(!outFile) {
|
||||
printf("Could not open %s\n", path);
|
||||
return;
|
||||
}
|
||||
|
||||
fileHeader_t fileHeader;
|
||||
dxHeader_t dxHeader;
|
||||
char wavPath[256];
|
||||
|
||||
// +1 to ignore null terminator, clobbers headerSize but we write that next
|
||||
strncpy(fileHeader.name, path, sizeof(fileHeader.name)+1);
|
||||
fileHeader.headerSize = sizeof(fileHeader) + sizeof(uint32_t)*wavCount;
|
||||
fileHeader.fileCount = wavCount;
|
||||
memset(fileHeader.unknown, 0, sizeof(fileHeader.unknown));
|
||||
fwrite(&fileHeader, sizeof(fileHeader), 1, outFile);
|
||||
|
||||
// file offsets
|
||||
uint32_t offset = fileHeader.headerSize;
|
||||
for(int i = 0; i < wavCount; i++) {
|
||||
snprintf(wavPath, 256, "%d.wav", i);
|
||||
FILE* wav = fopen(wavPath, "rb");
|
||||
|
||||
fseek(wav, 0L, SEEK_END);
|
||||
size_t wavSize = ftell(wav);
|
||||
rewind(wav);
|
||||
|
||||
fwrite(&offset, sizeof(uint32_t), 1, outFile);
|
||||
offset += wavSize + sizeof(dxHeader_t);
|
||||
fclose(wav);
|
||||
}
|
||||
// the actual wavs
|
||||
for(int i = 0; i < wavCount; i++) {
|
||||
snprintf(wavPath, 256, "%d.wav", i);
|
||||
FILE* wav = fopen(wavPath, "rb");
|
||||
|
||||
fseek(wav, 0L, SEEK_END);
|
||||
size_t wavSize = ftell(wav);
|
||||
rewind(wav);
|
||||
|
||||
memcpy(dxHeader.dx, "2DX9", 4);
|
||||
dxHeader.headerSize = 24;
|
||||
dxHeader.wavSize = wavSize;
|
||||
dxHeader.unk1 = 0x3231;
|
||||
// these match preview files for convenience
|
||||
dxHeader.trackId = -1; // I may regret this
|
||||
dxHeader.unk2 = 64;
|
||||
dxHeader.attenuation = 1;
|
||||
dxHeader.loopPoint = 0;
|
||||
fwrite(&dxHeader, sizeof(dxHeader), 1, outFile);
|
||||
transfer_file(wav, outFile, wavSize);
|
||||
|
||||
fclose(wav);
|
||||
}
|
||||
|
||||
fclose(outFile);
|
||||
printf("Done!\n");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if(argc != 2) {
|
||||
printf("Usage: 2dxbuild output\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
build_2dx(argv[1]);
|
||||
return 0;
|
||||
}
|
55
2dxDump.c
Normal file
55
2dxDump.c
Normal file
@ -0,0 +1,55 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "2dx.h"
|
||||
#include "shared.h"
|
||||
|
||||
void extract_2dx(char* path) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
|
||||
if(!f) {
|
||||
printf("Could not open %s, skipping\n", path);
|
||||
return;
|
||||
}
|
||||
|
||||
fileHeader_t fileHeader;
|
||||
uint32_t *fileOffsets;
|
||||
dxHeader_t dxHeader;
|
||||
char outPath[256];
|
||||
FILE* outFile;
|
||||
|
||||
fread(&fileHeader, sizeof(fileHeader), 1, f);
|
||||
//printf("2dx contains %d file(s)\n", fileHeader.fileCount);
|
||||
fileOffsets = malloc(sizeof(uint32_t) * fileHeader.fileCount);
|
||||
fread(fileOffsets, sizeof(uint32_t), fileHeader.fileCount, f);
|
||||
|
||||
for(int i = 0; i < fileHeader.fileCount; i++) {
|
||||
fseek(f, fileOffsets[i], SEEK_SET);
|
||||
|
||||
// TODO verify 2DX9
|
||||
fread(&dxHeader, sizeof(dxHeader), 1, f);
|
||||
snprintf(outPath, 256, "%d.wav", i);
|
||||
outFile = fopen(outPath, "wb");
|
||||
// seek to RIFF start
|
||||
fseek(f, fileOffsets[i]+dxHeader.headerSize, SEEK_SET);
|
||||
fprintf(stderr, "Extracting %s...\n", outPath);
|
||||
transfer_file(f, outFile, dxHeader.wavSize);
|
||||
fclose(outFile);
|
||||
}
|
||||
|
||||
free(fileOffsets);
|
||||
//printf("Done!\n");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if(argc < 2) {
|
||||
printf("Usage: 2dxdump file1 [file2 ...]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for(int i = 1; i < argc; i++) {
|
||||
extract_2dx(argv[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
84
2dxMerge.c
Normal file
84
2dxMerge.c
Normal file
@ -0,0 +1,84 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "2dx.h"
|
||||
#include "shared.h"
|
||||
|
||||
void update_2dx(char* inPath, char* outPath) {
|
||||
size_t wavSize;
|
||||
fileHeader_t fileHeader;
|
||||
uint32_t *fileOffsets;
|
||||
uint32_t *newOffsets;
|
||||
dxHeader_t dxHeader;
|
||||
char wavPath[256];
|
||||
|
||||
FILE* inFile = fopen(inPath, "rb");
|
||||
if(!inFile) {
|
||||
printf("Could not open %s for reading\n", inPath);
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* outFile = fopen(outPath, "wb");
|
||||
if(!outFile) {
|
||||
printf("Could not open %s for writing\n", outPath);
|
||||
return;
|
||||
}
|
||||
|
||||
fread(&fileHeader, sizeof(fileHeader), 1, inFile);
|
||||
fileOffsets = malloc(sizeof(uint32_t) * fileHeader.fileCount);
|
||||
newOffsets = malloc(sizeof(uint32_t) * fileHeader.fileCount);
|
||||
fread(fileOffsets, sizeof(uint32_t), fileHeader.fileCount, inFile);
|
||||
memcpy(newOffsets, fileOffsets, sizeof(uint32_t) * fileHeader.fileCount);
|
||||
|
||||
// update offsets for new wavs, start at 1 cause it has same offset
|
||||
for(int i = 1; i < fileHeader.fileCount; i++) {
|
||||
snprintf(wavPath, 256, "%d.wav", i-1);
|
||||
// we have a new wav!
|
||||
if((wavSize = file_size(wavPath)) == 0) {
|
||||
// calculate size from old offsets
|
||||
wavSize = fileOffsets[i] - fileOffsets[i-1] - sizeof(dxHeader);
|
||||
}
|
||||
newOffsets[i] = newOffsets[i-1] + sizeof(dxHeader) + wavSize;
|
||||
}
|
||||
|
||||
rewind(inFile);
|
||||
// unchanged
|
||||
fwrite(&fileHeader, sizeof(fileHeader), 1, outFile);
|
||||
// new offsets
|
||||
fwrite(newOffsets, sizeof(uint32_t), fileHeader.fileCount, outFile);
|
||||
// new wavs
|
||||
for(int i = 0; i < fileHeader.fileCount; i++) {
|
||||
// load existing header
|
||||
fseek(inFile, fileOffsets[i], SEEK_SET);
|
||||
fread(&dxHeader, sizeof(dxHeader), 1, inFile);
|
||||
|
||||
snprintf(wavPath, 256, "%d.wav", i);
|
||||
// new wav to insert
|
||||
if((wavSize = file_size(wavPath))) {
|
||||
printf("Updating %s\n", wavPath);
|
||||
dxHeader.wavSize = wavSize;
|
||||
FILE* newWav = fopen(wavPath, "rb");
|
||||
fwrite(&dxHeader, sizeof(dxHeader), 1, outFile);
|
||||
transfer_file(newWav, outFile, wavSize);
|
||||
fclose(newWav);
|
||||
} else {
|
||||
fwrite(&dxHeader, sizeof(dxHeader), 1, outFile);
|
||||
transfer_file(inFile, outFile, dxHeader.wavSize);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(outFile);
|
||||
fclose(inFile);
|
||||
printf("Done!\n");
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if(argc != 3) {
|
||||
printf("Usage: 2dxupdate input output\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
update_2dx(argv[1], argv[2]);
|
||||
return 0;
|
||||
}
|
170
2dxWav.c
Normal file
170
2dxWav.c
Normal file
@ -0,0 +1,170 @@
|
||||
#include "sox/sox.h"
|
||||
#include "2dxWav.h"
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
// the assert in assert.h produces a crash, this just returns
|
||||
#define assert(cond) if(!(cond)) {printf("Something went wrong: " #cond "\n"); return 1;}
|
||||
|
||||
/* Private data for .wav file, taken from wav.c so we can change
|
||||
certain attributes to the 2dx specific format */
|
||||
typedef struct {
|
||||
uint64_t numSamples;
|
||||
size_t dataLength;
|
||||
unsigned short formatTag;
|
||||
unsigned short samplesPerBlock;
|
||||
unsigned short blockAlign;
|
||||
size_t dataStart;
|
||||
char * comment;
|
||||
int ignoreSize;
|
||||
|
||||
unsigned short nCoefs; /* ADPCM: number of coef sets */
|
||||
short *lsx_ms_adpcm_i_coefs; /* ADPCM: coef sets */
|
||||
unsigned char *packet; /* Temporary buffer for packets */
|
||||
short *samples; /* interleaved samples buffer */
|
||||
short *samplePtr; /* Pointer to current sample */
|
||||
short *sampleTop; /* End of samples-buffer */
|
||||
unsigned short blockSamplesRemaining;/* Samples remaining per channel */
|
||||
int state[16]; /* step-size info for *ADPCM writes */
|
||||
|
||||
/* there are more things in here but they're related to GSM encoding */
|
||||
} priv_t;
|
||||
|
||||
/* For debugging */
|
||||
void printwav(priv_t* wav) {
|
||||
printf("\t numSamples: %d\n" , wav->numSamples );
|
||||
printf("\t dataLength: %zu\n" , wav->dataLength );
|
||||
printf("\t formatTag: %hu\n" , wav->formatTag );
|
||||
printf("\t samplesPerBlock: %hu\n" , wav->samplesPerBlock );
|
||||
printf("\t blockAlign: %hu\n" , wav->blockAlign );
|
||||
printf("\t dataStart: %zu\n" , wav->dataStart );
|
||||
printf("\t ignoreSize: %d\n" , wav->ignoreSize );
|
||||
printf("\t nCoefs: %hu\n" , wav->nCoefs );
|
||||
printf("\t lsx_ms_adpcm_i_coefs: %d\n" , wav->lsx_ms_adpcm_i_coefs );
|
||||
printf("\t packet: %d\n:" , wav->packet );
|
||||
printf("\t samples: %d\n" , wav->samples );
|
||||
printf("\t samplePtr: %d\n" , wav->samplePtr );
|
||||
printf("\t sampleTop: %d\n" , wav->sampleTop );
|
||||
printf("\t blockSamplesRemaining: %d\n" , wav->blockSamplesRemaining );
|
||||
}
|
||||
|
||||
int convert_wav(char* inFile, char* outWav, int trimPreview) {
|
||||
static sox_format_t *in, *out;
|
||||
sox_effects_chain_t *chain;
|
||||
sox_effect_t *e;
|
||||
sox_signalinfo_t interm_signal;
|
||||
char *args[10];
|
||||
|
||||
assert(sox_init() == SOX_SUCCESS);
|
||||
assert(in = sox_open_read(inFile, NULL, NULL, NULL));
|
||||
|
||||
// 2dx specific format
|
||||
sox_signalinfo_t out_signal = {
|
||||
.rate = 44100,
|
||||
.channels = 2, // have to be 2 for later hacks to work
|
||||
.precision = 0,
|
||||
.length = 0,
|
||||
.mult = NULL
|
||||
};
|
||||
|
||||
sox_encodinginfo_t out_encoding;
|
||||
memset(&out_encoding, 0, sizeof(out_encoding));
|
||||
out_encoding.encoding = SOX_ENCODING_MS_ADPCM;
|
||||
out_encoding.bits_per_sample = 4;
|
||||
|
||||
assert(out = sox_open_write(outWav, &out_signal, &out_encoding, "wav", NULL, NULL));
|
||||
// Forcibly set the values we want
|
||||
priv_t *wav = (priv_t *) out->priv;
|
||||
wav->samplesPerBlock = 244;
|
||||
wav->blockAlign = 256;
|
||||
// taken from wav.c, with locked 2 channels
|
||||
// The buffers become the wrong size, but we use smaller buffers so it's ok
|
||||
size_t sbsize = 2 * wav->samplesPerBlock;
|
||||
wav->sampleTop = wav->samples + sbsize;
|
||||
|
||||
// we'll need these later to fix the headers
|
||||
uint16_t blockAlign = wav->blockAlign;
|
||||
uint16_t samplesPerBlock = wav->samplesPerBlock;
|
||||
uint32_t avgBytes = (double)blockAlign*out_signal.rate / (double)samplesPerBlock + 0.5;
|
||||
|
||||
// Debugging
|
||||
/*printf("\n\nOutput data:\n");
|
||||
printwav(wav);
|
||||
|
||||
wav = (priv_t *) in->priv;
|
||||
printf("\n\nInput data:\n");
|
||||
printwav(wav);*/
|
||||
|
||||
chain = sox_create_effects_chain(&in->encoding, &out->encoding);
|
||||
interm_signal = in->signal; /* NB: deep copy */
|
||||
e = sox_create_effect(sox_find_effect("input"));
|
||||
args[0] = (char *)in;
|
||||
assert(sox_effect_options(e, 1, args) == SOX_SUCCESS);
|
||||
assert(sox_add_effect(chain, e, &in->signal, &in->signal) == SOX_SUCCESS);
|
||||
free(e);
|
||||
|
||||
if (in->signal.rate != out->signal.rate) {
|
||||
e = sox_create_effect(sox_find_effect("rate"));
|
||||
assert(sox_effect_options(e, 0, NULL) == SOX_SUCCESS);
|
||||
assert(sox_add_effect(chain, e, &interm_signal, &out->signal) == SOX_SUCCESS);
|
||||
free(e);
|
||||
}
|
||||
|
||||
if (in->signal.channels != out->signal.channels) {
|
||||
e = sox_create_effect(sox_find_effect("channels"));
|
||||
assert(sox_effect_options(e, 0, NULL) == SOX_SUCCESS);
|
||||
assert(sox_add_effect(chain, e, &interm_signal, &out->signal) == SOX_SUCCESS);
|
||||
free(e);
|
||||
}
|
||||
|
||||
// Only use 10 seconds of audio for previews
|
||||
if(trimPreview) {
|
||||
e = sox_create_effect(sox_find_effect("trim"));
|
||||
args[0] = "0";
|
||||
args[1] = "10";
|
||||
assert(sox_effect_options(e, 1, args) == SOX_SUCCESS);
|
||||
assert(sox_add_effect(chain, e, &interm_signal, &in->signal) == SOX_SUCCESS);
|
||||
free(e);
|
||||
}
|
||||
|
||||
e = sox_create_effect(sox_find_effect("output"));
|
||||
args[0] = (char *)out;
|
||||
assert(sox_effect_options(e, 1, args) == SOX_SUCCESS);
|
||||
assert(sox_add_effect(chain, e, &in->signal, &in->signal) == SOX_SUCCESS);
|
||||
free(e);
|
||||
|
||||
/* Flow samples through the effects processing chain until EOF is reached */
|
||||
sox_flow_effects(chain, NULL, NULL);
|
||||
/* All done; tidy up: */
|
||||
sox_delete_effects_chain(chain);
|
||||
sox_close(out);
|
||||
sox_close(in);
|
||||
sox_quit();
|
||||
|
||||
// Fix the header, since sox wrote it before we changed our block stuff
|
||||
FILE* outFile = fopen(outWav, "r+b");
|
||||
// AvgBytesPerSec
|
||||
fseek(outFile, 28, SEEK_SET);
|
||||
fwrite(&avgBytes, sizeof(uint32_t), 1, outFile);
|
||||
fwrite(&blockAlign, sizeof(uint16_t), 1, outFile);
|
||||
// samples per block
|
||||
fseek(outFile, 38, SEEK_SET);
|
||||
fwrite(&samplesPerBlock, sizeof(uint16_t), 1, outFile);
|
||||
|
||||
// RIFF size
|
||||
// minus 4 byte "RIFF" and 4 bytes for this number
|
||||
size_t wavSize = file_size(outWav) - 8;
|
||||
fseek(outFile, 4, SEEK_SET);
|
||||
fwrite(&wavSize, sizeof(uint32_t), 1, outFile);
|
||||
|
||||
//// data size
|
||||
// minus the RIFF header size
|
||||
wavSize -= 82;
|
||||
fseek(outFile, 86, SEEK_SET);
|
||||
fwrite(&wavSize, sizeof(uint32_t), 1, outFile);
|
||||
|
||||
fclose(outFile);
|
||||
|
||||
return 0;
|
||||
}
|
9
2dxWav.h
Normal file
9
2dxWav.h
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef _2DX_WAV_H
|
||||
#define _2DX_WAV_H
|
||||
|
||||
#include "shared.h"
|
||||
|
||||
// Converts a wav to a file
|
||||
int convert_wav(char* inFile, char* outWav, int trimPreview);
|
||||
|
||||
#endif
|
20
2dxWavConvert.c
Normal file
20
2dxWavConvert.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "shared.h"
|
||||
#include "2dxWav.h"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
if(argc != 3) {
|
||||
printf("2dxWavConvert infile outwav\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(convert_wav(argv[1], argv[2], 0)) {
|
||||
printf("Conversion failed!\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
26
Makefile
Normal file
26
Makefile
Normal file
@ -0,0 +1,26 @@
|
||||
CC=gcc
|
||||
CFLAGS=-std=c99 -Wall -pedantic
|
||||
|
||||
all: 2dxWavConvert 2dxDump 2dxBuild 2dxMerge
|
||||
|
||||
shared.o: shared.c shared.h
|
||||
$(CC) $(CFLAGS) -c shared.c -o shared.o
|
||||
|
||||
2dxWav.o: 2dxWav.c 2dxWav.h shared.o
|
||||
$(CC) $(CFLAGS) -c 2dxWav.c -o 2dxWav.o
|
||||
|
||||
2dxDump: 2dxDump.c 2dx.h shared.o
|
||||
$(CC) $(CFLAGS) 2dxDump.c shared.o -o 2dxDump
|
||||
|
||||
2dxBuild: 2dxBuild.c 2dx.h shared.o
|
||||
$(CC) $(CFLAGS) 2dxBuild.c shared.o -o 2dxBuild
|
||||
|
||||
2dxMerge: 2dxMerge.c 2dx.h shared.o
|
||||
$(CC) $(CFLAGS) 2dxMerge.c shared.o -o 2dxMerge
|
||||
|
||||
2dxWavConvert: 2dxWavConvert.c shared.o 2dxWav.o
|
||||
$(CC) $(CFLAGS) 2dxWavConvert.c shared.o 2dxWav.o -o 2dxWavConvert libsox-3.dll
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *.o 2dxDump 2dxBuild 2dxMerge
|
44
README.md
Normal file
44
README.md
Normal file
@ -0,0 +1,44 @@
|
||||
#2dxTools
|
||||
|
||||
A set of tools for working with 2dx audio containers.
|
||||
|
||||
###2dxDump
|
||||
|
||||
`2dxDump infile.2dx`
|
||||
|
||||
Takes a 2dx and dumps all its wavs. Names them sequentially with no leading zeros.
|
||||
|
||||
###2dxBuild
|
||||
|
||||
`2dxBuild outfile.2dx`
|
||||
|
||||
Builds a 2dx with default paramaters for loop point/volume etc.
|
||||
|
||||
###2dxMerge
|
||||
|
||||
`2dxMerge infile.2dx outfile.2dx`
|
||||
|
||||
For each file in `infile.2dx`, if there is a `.wav` present in the directory,
|
||||
it will replace it in the new file. Useful for extracting a single audio file,
|
||||
editing it, then adding it back.
|
||||
|
||||
###2dxWavConvert
|
||||
|
||||
`2dxWavConvert infile outfile.wav [preview]`
|
||||
|
||||
Takes any file that `sox` supports (mp3, flac, wav, ogg, etc) and converts it to
|
||||
the specific format required for 2dx files (MS-ADPCM wav with a block size of 256).
|
||||
If the third argument is "preview", the file is clipped to exactly 10 seconds
|
||||
to comply with preview wav requirements.
|
||||
|
||||
##Tools to come:
|
||||
|
||||
2dxTransfer - will work like 2dxMerge, but will take two input files, an output,
|
||||
and a list of tracks to transfer. Metadata (such as loop points) will also be
|
||||
transferred.
|
||||
|
||||
Enhancements to 2dxDump and 2dxBuild to generate and load xml files so track
|
||||
parameters can be modified.
|
||||
|
||||
If you have a burning need for one of these unfinished tools, please get in
|
||||
contact. I appreciate motivation.
|
35
shared.c
Normal file
35
shared.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include "shared.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// TODO: buffering for speed
|
||||
void transfer_file(FILE* in, FILE* out, size_t bytes) {
|
||||
char byte;
|
||||
for(size_t i = 0; i < bytes; i++) {
|
||||
if(!fread(&byte, 1, 1, in)) {
|
||||
printf("Could not read all bytes from input!\n");
|
||||
exit(1);
|
||||
}
|
||||
fwrite(&byte, 1, 1, out);
|
||||
}
|
||||
}
|
||||
|
||||
// returns 0 if file is empty or noexistant, filesize otherwise
|
||||
size_t file_size(const char* path) {
|
||||
FILE* f = fopen(path, "rb");
|
||||
if(!f)
|
||||
return 0;
|
||||
fseek(f, 0L, SEEK_END);
|
||||
size_t size = ftell(f);
|
||||
fclose(f);
|
||||
return size;
|
||||
}
|
||||
|
||||
// platform agnostic and proves we can read a file too
|
||||
int file_exists(const char *path) {
|
||||
FILE *file;
|
||||
if ((file = fopen(path, "rb"))) {
|
||||
fclose(file);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user