Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Pegmode committed Nov 23, 2020
1 parent f2ffae1 commit 5f951ae
Show file tree
Hide file tree
Showing 10 changed files with 555 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
outputwaves/
*.exe
138 changes: 138 additions & 0 deletions LSDJ2DMW.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
#include "LSDJ2DMW.h"

/*
instrument names (1e7a-1fb9)
names are 5 bytes long
pay attention to wave instrument param length!!!
indexed from 0
*/



int main(int argc, char* argv[]){
if(argc <= 1){
printf(HELPSTRING);
exit(0);
}
if(strcmp("-h",argv[1]) == 0){
printf(HELPSTRING);
exit(0);
}
if(strcmp(argv[1],"") == 0){
printf("ERROR: input filename not entered\n");
printf(HELPSTRING);
exit(1);
}
if(access(argv[1], F_OK ) == -1){
printf("ERROR: file '%s' does not exist\n",argv[1]);
printf(HELPSTRING);
exit(1);
}
strcpy(GLOBALINFILE,argv[1]);
int runcode = 0;
for(int i = 2; i < argc; i++){
if(strcmp("-p",argv[i]) == 0){//print ripped waves and all songs
GLOBALWAVEPRINTFLAG = 1;
GLOBALFILEPRINTFLAG = 1;
}
else if(strcmp("-pw",argv[i]) == 0){//print ripped waves
GLOBALWAVEPRINTFLAG = 1;
}
else if(strcmp("-pf",argv[i]) == 0){//print all songs
GLOBALFILEPRINTFLAG = 1;
}

else if(strcmp("-l",argv[i]) == 0){//rip all wavetables in loaded song
runcode = 5;
}
else if(strcmp("-lw",argv[i]) == 0){//rip single wave in loaded song
GLOBALRIPWAVENUM = atoi(argv[++i]);
runcode = 6;
}
else if(strcmp("-lwr",argv[i]) == 0){//rip range of wavetables from loaded song
GLOBALRIPWAVENUM = atoi(argv[++i]);
GLOBALENDWAVENUM = atoi(argv[++i]);
if (GLOBALRIPWAVENUM>GLOBALENDWAVENUM){
printf("ERROR: first value in range cannot be larger than the second value in range\n");
exit(1);
}
runcode = 7;
}

else if(strcmp("-a",argv[i]) == 0){//rip all waves in all songs
runcode = 4;
}
else if(strcmp("-s",argv[i]) == 0){//rip single song
strcpy(GLOBALSONGNAME,argv[++i]);
runcode = 1;
}
else if(strcmp("-sw",argv[i]) == 0){//rip single wave in song
strcpy(GLOBALSONGNAME,argv[++i]);
GLOBALRIPWAVENUM = atoi(argv[++i]);
//GLOBALRIPWAVENUM = argv[++i];
runcode = 2;
}
else if(strcmp("-swr",argv[i]) == 0){//rip range of waves in song
strcpy(GLOBALSONGNAME,argv[++i]);
GLOBALRIPWAVENUM = atoi(argv[++i]);
GLOBALENDWAVENUM = atoi(argv[++i]);
if (GLOBALRIPWAVENUM>GLOBALENDWAVENUM){
printf("ERROR: first value in range cannot be larger than the second value in range\n");
exit(1);
}
//GLOBALRIPWAVENUM = argv[++i];
//GLOBALENDWAVENUM = argv[++i];
runcode = 3;
}
else if(strcmp("-x",argv[i]) == 0){
GLOBALOUTPUTINDEXMODE = 1;
}
else if(strcmp("-nw",argv[i]) == 0){
runcode = 10;
}
else if(strcmp("-h",argv[i]) == 0){
printf(HELPSTRING);
exit(0);
}
else if(strcmp("-d",argv[i]) == 0){
test();
exit(0);
}
}
switch (runcode){
case 0:// rip all wavetables in loaded song(default)
ripWaveRangeFromLoaded(GLOBALINFILE,0,255);
break;
case 1://single file
ripSingleSong(GLOBALINFILE,GLOBALSONGNAME);
break;
case 2://single wavetable in single song
ripWavetableRangeFromSong(GLOBALINFILE,GLOBALSONGNAME,GLOBALRIPWAVENUM,GLOBALRIPWAVENUM);
break;
case 3://rip range of wavetables in single song
ripWavetableRangeFromSong(GLOBALINFILE,GLOBALSONGNAME,GLOBALRIPWAVENUM,GLOBALENDWAVENUM);
break;
case 4://rip all in all songs
ripAllWavetables(GLOBALINFILE);
break;
case 5://rip all wavetables in loaded song
ripWaveRangeFromLoaded(GLOBALINFILE,0,255);
break;
case 6://rip single wavetable in loaded song
ripWaveRangeFromLoaded(GLOBALINFILE,GLOBALRIPWAVENUM,GLOBALRIPWAVENUM);
break;
case 7://rip wavetables in given range of song
ripWaveRangeFromLoaded(GLOBALINFILE,GLOBALRIPWAVENUM,GLOBALENDWAVENUM);
break;
case 10://do nothing -nw
break;
default://something went wrong
break;
}
if(GLOBALFILEPRINTFLAG){
printAllFilenames("test1.sav");
}
}

79 changes: 79 additions & 0 deletions LSDJ2DMW.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

#ifndef SYSTEMOS
#define SYSTEMOS 0//windows
#endif
int GLOBALWAVEPRINTFLAG = 0;//print waves
int GLOBALFILEPRINTFLAG = 0;//print scanned files
int GLOBALOUTFILEFLAG = 1;//output .dmw's

#if SYSTEMOS == 0//windows
char* GLOBALFILEDIRECTORY = ".\\outputwaves\\";//directory to dump files into (windows)
#endif
#if SYSTEMOS == 1//linux
char* GLOBALFILEDIRECTORY = "./outputwaves/";//directory to dump files into (linux)
#endif
char GLOBALINFILE[50] = "";
char GLOBALSONGNAME[50] = "";
int GLOBALRIPWAVENUM = 0;//lazy solution
int GLOBALENDWAVENUM = 0;
int GLOBALOUTPUTINDEXMODE = 0;//1 = dec, 2 = hex
#define GBWAVESIZE 32
#define GBBITDEPTH 4
#define LSDJWAVESTARTADDRESS 0x6000
#define LSDJWAVEENDADDRESS 0x6FFF
#define LSDJVERSIONADDRESS 0x7FFF
#define FILENAMES_BASEADDRESS 0x8000
#define FILENAME_LENGTH 8

char* HELPSTRING = \
"==LSDJ2DMW 1.0==\n\
by Pegmode (Dan Chu)\n\n\
Rips wavetables from LSDJ into .dmw for use in Deflemask\n\
USAGE: LSDJ2SDMW <inputSavFilename> [options]\n\
OPTIONS: \n\
-l: rip all wavetables in loaded module (default)\n\
-lw <wavenumber>: rips a single wavetable from the loaded module\n\
-lwr <startWaveNumber>: rip a range of wavetables from the loaded module\n\
-a: rips all wavetables from all saved songs\n\
-s: <songname>: rip all wavetables from a single song\n\
-sw: <songname> <wavenumber>: rips a specific wavetable from a given song (0-255)\n\
-swr: <songname> <startWaveNumber> <endWaveNumber>: rips wavetables in a given range in a specific song (0-255)\n\
-p: print ripped waves and files\n\
-pw: print ripped waves\n\
-pf: print songs\n\
-x: format outputted wavetable indices as hex\n\
-nw: don't write files or parse files. Useful in combination with -p/-pf for reading songnames\n\
\n\
Example usage: LSDJ2SFMW test.sav -p -lwr 0 5\n\
default behavior rips all wavetables in loaded song \n\
for all behavior involving the 'loaded song', the 'loaded song' means what you have loaded in LSDj before saving\n\
by default waves are dumped into directory ./outputwaves/\n\
LSDJ versions less than 6.7.0 are not guaranteed to function properly with this program\n\
";

#include "LSDJDecompress.c"
#include "utils.c"
#include "ripTools.c"
#include "debug.c"



/*
Version Codes
* 0: < 3.6.0
* 1: 3.6.0
* 2: 3.6.1
* 3: 4.4.0
* 4: 5.7.0
* 5: 6.3.0
* 6: 6.7.0
* 7: 6.8.0
* 8: 7.1.0
* 9: 7.5.0
* 10: 7.9.8
*/
131 changes: 131 additions & 0 deletions LSDJDecompress.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include "LSDJDecompress.h"

//info https://littlesounddj.fandom.com/wiki/File_Management_Structure
//https://littlesounddj.fandom.com/wiki/.sav_structure
//WARNING: If comparing outputted binaries to .sav, timers may be different
uint8_t defaultInstrument[] = {0xa8,0x0,0x0,0xff,0x0,0x0,0x3,0x0,0x0,0xd0,0x0,0x0,0x0,0xf3,0x0,0x0};//length 16
uint8_t defaultWave[] = {0x8e,0xcd,0xcc,0xbb,0xaa,0xa9,0x99,0x88,0x87,0x76,0x66,0x55,0x54,0x43,0x32,0x31};//length 16


int calculateBlockBase(int blockNumber){//base address in .sav memory map
return (0x8000 + 0x200 * blockNumber);
}

int findFirstBlockFromFileIndex(int fileIndex ,char* filePath){
FILE* f = fopen(filePath,"rb");
uint8_t fileAllocationTable[FILE_ALLOC_SIZE] = {0xff};
fseek(f,FILE_ALLOC_ADDRESSBASE,SEEK_SET);
fread(fileAllocationTable,FILE_ALLOC_SIZE,1,f);
fclose(f);

for(int i = 0; i < FILE_ALLOC_SIZE; i++){//search filealloc table for file index
//block indexed from 1
//printf("%i\n",fileAllocationTable[i]);
if(fileAllocationTable[i] == fileIndex){
return i+1;
}
}
return -1;
}

int calculateRealAddress(int cPos){
return 0x8200 + cPos;
}

void createFileBlocksBuffer(char* filePath, uint8_t* blockBuffer){
FILE* f = fopen(filePath,"rb");
fseek(f,BLOCK_BASE_ADDRESS,SEEK_SET);
fread(blockBuffer,FILE_BLOCKS_SIZE,1,f);
fclose(f);

FILE* fz = fopen("debugout2.bin","wb");
fwrite(blockBuffer,1,FILE_BLOCKS_SIZE,fz);
fclose(fz);
}
int uncompressDefault(uint8_t* uncompressedFile,uint8_t* defaultContent,int uPos, int cPos, uint8_t* compressedFile){
int loopLength = compressedFile[cPos];
for(int i = 0; i < loopLength; i++){
memcpy(&uncompressedFile[uPos],defaultContent,DEFAULT_DECOMPRESS_LENGTH);
uPos += 16;
}
return uPos;
}

int calculateBlockOffsetFromBlockIndex(int blockIndex){//base offset
return (blockIndex-1) * 0x200;
}


void decompressBlocks(int startBlockIndex,char* filePath,uint8_t* uncompressedFile){
uint8_t compressedFile[FILE_BLOCKS_SIZE] = {0};
createFileBlocksBuffer(filePath,compressedFile);
int uPos = 0;//uncompressed position
int cPos = calculateBlockOffsetFromBlockIndex(startBlockIndex);//compressed positon
for(;uPos < 0x8000;){
//printf("%d\n",uPos );
switch(compressedFile[cPos]){
case 0xC0://RLE?s
cPos++;
if(compressedFile[cPos] != 0xC0){//IS RLE
uint8_t byteValue = compressedFile[cPos++];
uint8_t rleLength = compressedFile[cPos++];
for(int j = 0; j < (int)rleLength; j++){
//compressedFile[uPos++] = byteValue;
uncompressedFile[uPos++] = byteValue;
}
//printf("RLE val: %X len: %d c adr: %X u adr: %X\n",byteValue,rleLength,calculateRealAddress(cPos - 3),uPos);
}
else{//IS NOT RLE
uncompressedFile[uPos++] = 0xC0;
cPos++;
}
break;
case 0xE0://command
cPos++;
switch (compressedFile[cPos]){
case 0xE0://not a command
uncompressedFile[uPos++] = 0xE0;
cPos++;
break;
case 0xF1://default instrument
//printf("\nPasting Default instrument c adr:%X u adr:%X\n",calculateRealAddress(cPos),uPos);
cPos++;
uPos = uncompressDefault(uncompressedFile,defaultInstrument,uPos,cPos,compressedFile);
cPos++;
break;
case 0xF0://default wavetable
//printf("\nPasting Default WT c adr:%X u adr:%X\n",calculateRealAddress(cPos),uPos);
cPos++;
uPos = uncompressDefault(uncompressedFile,defaultWave,uPos,cPos,compressedFile);
cPos++;
break;
case 0xFF://end of file
//printf("\nFinished Decompression c adr:%X u adr:%X\n",calculateRealAddress(cPos),uPos);
return;
break;
default://block Switch
//printf("\nSwitching Blocks c adr:%X u adr:%X\n",calculateRealAddress(cPos),uPos);
cPos = calculateBlockOffsetFromBlockIndex(compressedFile[cPos]);
//printf(" new c adr:%X\n",calculateRealAddress(cPos));
//printf("next: %X\n",compressedFile[cPos]);
break;
}

break;
default://write byte
uncompressedFile[uPos++] = compressedFile[cPos++];
break;
}
}
}

void decompressLSDJFile(int fileIndex,char* fileName, uint8_t* uncompressedFile){
int blockIndex = findFirstBlockFromFileIndex(fileIndex,fileName);
//printf("BLOCK INDEX: %i\n",blockIndex);
if(blockIndex == -1){
//BLOCK INDEX NOT FOUND
printf("Block Index not found\n");
}
else decompressBlocks(blockIndex,fileName,uncompressedFile);
}

27 changes: 27 additions & 0 deletions LSDJDecompress.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdint.h>
#include <string.h>

#define FILE_ALLOC_ADDRESSBASE 0x8141
#define FILE_ALLOC_SIZE 191
#define FILE_BLOCK_LENGTH 0x200
#define DEFAULT_DECOMPRESS_LENGTH 16
#define MAX_FILE_NUMBER 0x20
#define BLOCK_BASE_ADDRESS 0x8200
#define FILE_BLOCKS_SIZE 0x17DFF


/*
* 0: < 3.6.0
* 1: 3.6.0
* 2: 3.6.1
* 3: 4.4.0
* 4: 5.7.0
* 5: 6.3.0
* 6: 6.7.0
* 7: 6.8.0
* 8: 7.1.0
* 9: 7.5.0
* 10: 7.9.8
* C: 8.7.7?
*/
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
C = gcc
outname = LSDJ2DMW
dependencies = LSDJ2DMW.c LSDJ2DMW.h utils.c utils.h ripTools.c ripTools.h LSDJDecompress.c LSDJDecompress.h debug.c debug.h
windows: $(dependencies)
$(C) LSDJ2DMW.c -o $(outname).exe
linux: $(dependencies) #untested
$(C) LSDJ2DMW.c -o $(outname) -D SYSTEMOS = 1
Loading

0 comments on commit 5f951ae

Please sign in to comment.