mirror of
https://github.com/rvtr/GodMode9i.git
synced 2025-11-02 00:11:07 -04:00
Add dumping DS saves using GBA cart save data (#138)
* Simplify GBA EEPROM FIFO wait * Add dumping DS saves using a GBA cart's save data * Don't try dump very large DS saves to GBA carts * Don't show DS cart info on regular DS you need to take the cart out to reinit it so info isn't loaded * Change to 'writing save' for DS to GBA dumps * Fix a couple bugs and typos
This commit is contained in:
parent
a9a29204ab
commit
bec149de03
@ -200,7 +200,7 @@ int main() {
|
||||
case 0x44414552: // 'READ'
|
||||
readEeprom((u8 *)fifoGetAddress(FIFO_USER_01), fifoGetValue32(FIFO_USER_01), fifoGetValue32(FIFO_USER_01));
|
||||
break;
|
||||
case 0x54495257: // 'WRITE'
|
||||
case 0x54495257: // 'WRIT'
|
||||
writeEeprom(fifoGetValue32(FIFO_USER_01), (u8 *)fifoGetAddress(FIFO_USER_01), fifoGetValue32(FIFO_USER_01));
|
||||
break;
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ bool flashcardMountSkipped = true;
|
||||
static bool flashcardMountRan = true;
|
||||
static int dmCursorPosition = 0;
|
||||
static std::vector<DriveMenuOperation> dmOperations;
|
||||
static char romTitle[13] = {0};
|
||||
static u32 romSize, romSizeTrimmed;
|
||||
static char romTitle[2][13] = {0};
|
||||
static u32 romSize[2], romSizeTrimmed;
|
||||
|
||||
static u8 gbaFixedValue = 0;
|
||||
static u8 stored_SCFG_MC = 0;
|
||||
@ -111,10 +111,13 @@ void dm_drawTopScreen(void) {
|
||||
font->print(-1, i + 1, true, "[R]", Alignment::right, pal);
|
||||
break;
|
||||
case DriveMenuOperation::gbaCart:
|
||||
font->printf(0, i + 1, true, Alignment::left, pal, STR_GBA_GAMECART.c_str(), romTitle);
|
||||
font->printf(0, i + 1, true, Alignment::left, pal, STR_GBA_GAMECART.c_str(), romTitle[1]);
|
||||
break;
|
||||
case DriveMenuOperation::ndsCard:
|
||||
font->printf(0, i + 1, true, Alignment::left, pal, STR_NDS_GAMECARD.c_str(), romTitle);
|
||||
if(romTitle[0][0] != 0)
|
||||
font->printf(0, i + 1, true, Alignment::left, pal, STR_NDS_GAMECARD.c_str(), romTitle[0]);
|
||||
else
|
||||
font->print(0, i + 1, true, STR_NDS_GAMECARD_NO_TITLE, Alignment::left, pal);
|
||||
break;
|
||||
case DriveMenuOperation::none:
|
||||
break;
|
||||
@ -165,16 +168,20 @@ void dm_drawBottomScreen(void) {
|
||||
font->printf(0, 2, false, Alignment::left, Palette::white, STR_N_FREE.c_str(), getDriveBytes(getBytesFree("fat:/")).c_str());
|
||||
break;
|
||||
case DriveMenuOperation::gbaCart:
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_GBA_GAMECART.c_str(), romTitle);
|
||||
font->printf(0, 1, false, Alignment::left, Palette::white, STR_GBA_GAME.c_str(), getBytes(romSize).c_str());
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_GBA_GAMECART.c_str(), romTitle[1]);
|
||||
font->printf(0, 1, false, Alignment::left, Palette::white, STR_GBA_GAME.c_str(), getBytes(romSize[1]).c_str());
|
||||
break;
|
||||
case DriveMenuOperation::nitroFs:
|
||||
font->print(0, 0, false, STR_NITROFS_LABEL);
|
||||
font->print(0, 1, false, STR_GAME_VIRTUAL);
|
||||
break;
|
||||
case DriveMenuOperation::ndsCard:
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_NDS_GAMECARD.c_str(), romTitle);
|
||||
font->printf(0, 1, false, Alignment::left, Palette::white, STR_NDS_GAME.c_str(), getBytes(romSize).c_str(), getBytes(romSizeTrimmed).c_str());
|
||||
if(romTitle[0][0] != 0) {
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, STR_NDS_GAMECARD.c_str(), romTitle[0]);
|
||||
font->printf(0, 1, false, Alignment::left, Palette::white, STR_NDS_GAME.c_str(), getBytes(romSize[0]).c_str(), getBytes(romSizeTrimmed).c_str());
|
||||
} else {
|
||||
font->print(0, 0, false, STR_NDS_GAMECARD_NO_TITLE);
|
||||
}
|
||||
break;
|
||||
case DriveMenuOperation::ramDrive:
|
||||
font->print(0, 0, false, STR_RAMDRIVE_LABEL);
|
||||
@ -219,24 +226,13 @@ void driveMenu (void) {
|
||||
dmOperations.push_back(DriveMenuOperation::fatImage);
|
||||
if (nitroMounted)
|
||||
dmOperations.push_back(DriveMenuOperation::nitroFs);
|
||||
if ((io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA)
|
||||
|| (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0)))) {
|
||||
dmOperations.push_back(DriveMenuOperation::ndsCard);
|
||||
if(romTitle[0] == 0 && ((io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA) || !flashcardMounted)) {
|
||||
sNDSHeaderExt ndsHeader;
|
||||
cardInit(&ndsHeader);
|
||||
tonccpy(romTitle, ndsHeader.gameTitle, 12);
|
||||
romSize = 0x20000 << ndsHeader.deviceSize;
|
||||
romSizeTrimmed = (isDSiMode() && (ndsHeader.unitCode != 0) && (ndsHeader.twlRomSize > 0))
|
||||
? ndsHeader.twlRomSize : ndsHeader.romSize + 0x88;
|
||||
}
|
||||
} else if (!isDSiMode() && isRegularDS && gbaFixedValue == 0x96) {
|
||||
if (!isDSiMode() && isRegularDS && gbaFixedValue == 0x96) {
|
||||
dmOperations.push_back(DriveMenuOperation::gbaCart);
|
||||
if(romTitle[0] == 0) {
|
||||
tonccpy(romTitle, (char*)0x080000A0, 12);
|
||||
romSize = 0;
|
||||
for (romSize = (1 << 20); romSize < (1 << 25); romSize <<= 1) {
|
||||
vu16 *rompos = (vu16*)(0x08000000 + romSize);
|
||||
if(romTitle[1][0] == 0) {
|
||||
tonccpy(romTitle[1], (char*)0x080000A0, 12);
|
||||
romSize[1] = 0;
|
||||
for (romSize[1] = (1 << 20); romSize[1] < (1 << 25); romSize[1] <<= 1) {
|
||||
vu16 *rompos = (vu16*)(0x08000000 + romSize[1]);
|
||||
bool romend = true;
|
||||
for (int j = 0; j < 0x1000; j++) {
|
||||
if (rompos[j] != j) {
|
||||
@ -247,11 +243,25 @@ void driveMenu (void) {
|
||||
if (romend)
|
||||
break;
|
||||
}
|
||||
romSizeTrimmed = romSize;
|
||||
}
|
||||
} else if (romTitle[0] != 0) {
|
||||
romTitle[0] = 0;
|
||||
romSizeTrimmed = romSize = 0;
|
||||
} else if (romTitle[1][0] != 0) {
|
||||
romTitle[1][0] = 0;
|
||||
romSize[1] = 0;
|
||||
}
|
||||
if (((io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA) || (isRegularDS && !flashcardMounted && romTitle[1][0] != 0))
|
||||
|| (isDSiMode() && !arm7SCFGLocked && !(REG_SCFG_MC & BIT(0)))) {
|
||||
dmOperations.push_back(DriveMenuOperation::ndsCard);
|
||||
if(romTitle[0][0] == 0 && ((io_dldi_data->ioInterface.features & FEATURE_SLOT_GBA) || !flashcardMounted) && !isRegularDS) {
|
||||
sNDSHeaderExt ndsHeader;
|
||||
cardInit(&ndsHeader);
|
||||
tonccpy(romTitle[0], ndsHeader.gameTitle, 12);
|
||||
romSize[0] = 0x20000 << ndsHeader.deviceSize;
|
||||
romSizeTrimmed = (isDSiMode() && (ndsHeader.unitCode != 0) && (ndsHeader.twlRomSize > 0))
|
||||
? ndsHeader.twlRomSize : ndsHeader.romSize + 0x88;
|
||||
}
|
||||
} else if (romTitle[0][0] != 0) {
|
||||
romTitle[0][0] = 0;
|
||||
romSizeTrimmed = romSize[0] = 0;
|
||||
}
|
||||
|
||||
if(dmCursorPosition >= (int)dmOperations.size())
|
||||
@ -339,7 +349,7 @@ void driveMenu (void) {
|
||||
screenMode = 1;
|
||||
break;
|
||||
}
|
||||
} else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ndsCard && (sdMounted || flashcardMounted)) {
|
||||
} else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ndsCard && (sdMounted || flashcardMounted || romTitle[1][0] != 0)) {
|
||||
ndsCardDump();
|
||||
} else if (dmOperations[dmCursorPosition] == DriveMenuOperation::ramDrive && ramdriveMounted) {
|
||||
currentDrive = Drive::ramDrive;
|
||||
|
||||
@ -3,8 +3,10 @@
|
||||
#include "auxspi.h"
|
||||
#include "date.h"
|
||||
#include "driveOperations.h"
|
||||
#include "fileOperations.h"
|
||||
#include "font.h"
|
||||
#include "gba.h"
|
||||
#include "lzss.h"
|
||||
#include "ndsheaderbanner.h"
|
||||
#include "read_card.h"
|
||||
#include "tonccpy.h"
|
||||
@ -29,6 +31,7 @@ enum DumpOption {
|
||||
romTrimmed = 2,
|
||||
save = 4,
|
||||
metadata = 8,
|
||||
ndsSave = 16,
|
||||
all = rom | save | metadata,
|
||||
allTrimmed = romTrimmed | save | metadata
|
||||
};
|
||||
@ -38,7 +41,10 @@ DumpOption dumpMenu(std::vector<DumpOption> allowedOptions, const char *dumpName
|
||||
int optionOffset = 0;
|
||||
|
||||
char dumpToStr[256];
|
||||
snprintf(dumpToStr, sizeof(dumpToStr), STR_DUMP_TO.c_str(), dumpName, sdMounted ? "sd" : "fat");
|
||||
if(sdMounted || flashcardMounted)
|
||||
snprintf(dumpToStr, sizeof(dumpToStr), STR_DUMP_TO.c_str(), dumpName, sdMounted ? "sd" : "fat");
|
||||
else
|
||||
snprintf(dumpToStr, sizeof(dumpToStr), STR_DUMP_TO_GBA.c_str(), dumpName);
|
||||
|
||||
int y = font->calcHeight(dumpToStr) + 1;
|
||||
|
||||
@ -65,6 +71,9 @@ DumpOption dumpMenu(std::vector<DumpOption> allowedOptions, const char *dumpName
|
||||
case DumpOption::save:
|
||||
font->print(3, row++, false, STR_DUMP_SAVE);
|
||||
break;
|
||||
case DumpOption::ndsSave:
|
||||
font->print(3, row++, false, STR_DUMP_DS_SAVE);
|
||||
break;
|
||||
case DumpOption::metadata:
|
||||
font->print(3, row++, false, STR_DUMP_METADATA);
|
||||
break;
|
||||
@ -297,77 +306,270 @@ u32 cardNandGetSaveSize(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ndsCardSaveDump(const char* filename) {
|
||||
FILE *out = fopen(filename, "wb");
|
||||
if(out) {
|
||||
bool writeToGbaSave(const char* fileName, u8* buffer, u32 size) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_COMPRESSING_SAVE);
|
||||
font->update(false);
|
||||
int compressedSize = 0;
|
||||
u8 *compressedBuffer = LZS_Encode(buffer, size, LZS_VFAST, &compressedSize);
|
||||
|
||||
u8 section = 0;
|
||||
u32 bytesWritten = 0;
|
||||
while((int)bytesWritten < compressedSize) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_DUMPING_SAVE);
|
||||
font->print(0, 1, false, STR_DO_NOT_REMOVE_CARD);
|
||||
font->print(0, 0, false, STR_LOADING);
|
||||
font->update(false);
|
||||
saveTypeGBA type = gbaGetSaveType();
|
||||
u32 gbaSize = gbaGetSaveSize(type);
|
||||
|
||||
u32 writeSize = std::min(gbaSize - 0x30, (u32)(compressedSize - bytesWritten));
|
||||
|
||||
font->clear(false);
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, (STR_WRITE_TO_GBA + "\n\n" + STR_A_YES_B_NO).c_str(), getBytes(writeSize).c_str(), getBytes(compressedSize - bytesWritten).c_str());
|
||||
font->update(false);
|
||||
|
||||
int type = cardEepromGetTypeFixed();
|
||||
u16 pressed;
|
||||
do {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
if(type == -1) { // NAND
|
||||
u32 saveSize = cardNandGetSaveSize();
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
pressed = keysDownRepeat();
|
||||
} while (!(pressed & (KEY_A | KEY_B)) && *(u8*)(0x080000B2) == 0x96);
|
||||
|
||||
if(saveSize == 0) {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_SAVE);
|
||||
return;
|
||||
if(pressed & KEY_A) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_WRITING_SAVE);
|
||||
font->update(false);
|
||||
|
||||
u8* writeBuffer = (u8*)memalign(4, gbaSize);
|
||||
// 0x30 byte header
|
||||
tonccpy(writeBuffer, "9i", 3); // Magic
|
||||
writeBuffer[3] = section; // Section of the save
|
||||
tonccpy(writeBuffer + 0x4, &size, 4); // Total original size
|
||||
tonccpy(writeBuffer + 0x8, &compressedSize, 4); // Total compressed size
|
||||
tonccpy(writeBuffer + 0xC, &writeSize, 4); // Size of current section (excluding header)
|
||||
tonccpy(writeBuffer + 0x10, fileName, 0x20); // File name
|
||||
// Actual save data
|
||||
tonccpy(writeBuffer + 0x30, compressedBuffer + bytesWritten, writeSize);
|
||||
|
||||
gbaFormatSave(type);
|
||||
gbaWriteSave(0, writeBuffer, gbaSize, type);
|
||||
free(writeBuffer);
|
||||
|
||||
bytesWritten += writeSize;
|
||||
section++;
|
||||
}
|
||||
|
||||
if(pressed & KEY_B) {
|
||||
free(compressedBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
if((int)bytesWritten < compressedSize) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_SWITCH_CART);
|
||||
font->update(false);
|
||||
|
||||
// Wait for GBA cart to be removed and reinserted
|
||||
if(*(u8*)(0x080000B2) == 0x96)
|
||||
while(*(u8*)(0x080000B2) == 0x96) swiWaitForVBlank();
|
||||
while(*(u8*)(0x080000B2) != 0x96) swiWaitForVBlank();
|
||||
}
|
||||
}
|
||||
|
||||
free(compressedBuffer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readFromGbaCart() {
|
||||
u32 size, compressedSize;
|
||||
char fileName[0x20] = {0};
|
||||
u8 *compressedBuffer = nullptr;
|
||||
|
||||
u8 currentSection = 0;
|
||||
u32 bytesRead = 0;
|
||||
do {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_LOADING);
|
||||
font->update(false);
|
||||
|
||||
saveTypeGBA saveType = gbaGetSaveType();
|
||||
u32 gbaSize = gbaGetSaveSize(saveType);
|
||||
u8 *buffer = new u8[gbaSize];
|
||||
gbaReadSave(buffer, 0, gbaSize, saveType);
|
||||
|
||||
int section = -1;
|
||||
if(memcmp(buffer, "9i", 3) == 0) {
|
||||
// Only load the first time
|
||||
if(fileName[0] == 0) {
|
||||
tonccpy(&size, buffer + 0x4, 4); // Total original size
|
||||
tonccpy(&compressedSize, buffer + 0x8, 4); // Total compressed size
|
||||
tonccpy(fileName, buffer + 0x10, 0x20); // File name
|
||||
|
||||
compressedBuffer = new u8[compressedSize];
|
||||
}
|
||||
|
||||
u32 currentSize = saveSize;
|
||||
FILE* destinationFile = fopen(filename, "wb");
|
||||
if (destinationFile) {
|
||||
u32 compressedSizeTemp;
|
||||
tonccpy(&compressedSizeTemp, buffer + 0x8, 4); // Total compressed size
|
||||
if(compressedSizeTemp == compressedSize) { // Probably matching DS dump
|
||||
section = buffer[3]; // Section of the save
|
||||
|
||||
font->print(0, 4, false, STR_PROGRESS);
|
||||
font->print(0, 5, false, "[");
|
||||
font->print(-1, 5, false, "]");
|
||||
for (u32 src = 0; src < saveSize; src += 0x8000) {
|
||||
if(section == currentSection) {
|
||||
u32 readSize = 0;
|
||||
tonccpy(&readSize, buffer + 0xC, 4); // Size of current section (excluding header)
|
||||
|
||||
// Copy to output buffer
|
||||
tonccpy(compressedBuffer + bytesRead, buffer + 0x30, readSize);
|
||||
|
||||
bytesRead += readSize;
|
||||
currentSection++;
|
||||
}
|
||||
} else {
|
||||
dumpFailMsg(STR_WRONG_DS_SAVE);
|
||||
}
|
||||
} else {
|
||||
dumpFailMsg(STR_NO_DS_SAVE);
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
|
||||
if(bytesRead < compressedSize) {
|
||||
font->clear(false);
|
||||
if(section != -1)
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, (STR_SWITCH_CART_TO_SECTION_THIS_WAS + "\n\n" + STR_B_CANCEL).c_str(), currentSection + 1, section + 1);
|
||||
else
|
||||
font->printf(0, 0, false, Alignment::left, Palette::white, (STR_SWITCH_CART_TO_SECTION + "\n\n" + STR_B_CANCEL).c_str(), currentSection + 1);
|
||||
font->update(false);
|
||||
|
||||
if(*(u8*)(0x080000B2) == 0x96) {
|
||||
while(*(u8*)(0x080000B2) == 0x96) {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
font->print((src / (saveSize / (SCREEN_COLS - 2))) + 1, 5, false, "=");
|
||||
font->printf(0, 6, false, Alignment::left, Palette::white, STR_N_OF_N_BYTES.c_str(), src, saveSize);
|
||||
font->update(false);
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
for (u32 i = 0; i < 0x8000; i += 0x200) {
|
||||
cardRead(cardNandRwStart + src + i, copyBuf + i, true);
|
||||
if(keysDown() & KEY_B) {
|
||||
delete[] compressedBuffer;
|
||||
return false;
|
||||
}
|
||||
if (fwrite(copyBuf, 1, (currentSize >= 0x8000 ? 0x8000 : currentSize), destinationFile) < 1) {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_SAVE);
|
||||
break;
|
||||
}
|
||||
currentSize -= 0x8000;
|
||||
}
|
||||
fclose(destinationFile);
|
||||
} else {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_SAVE);
|
||||
}
|
||||
} else { // SPI
|
||||
unsigned char *buffer;
|
||||
auxspi_extra card_type = auxspi_has_extra();
|
||||
if(card_type == AUXSPI_INFRARED) {
|
||||
int size = auxspi_save_size_log_2(card_type);
|
||||
int size_blocks;
|
||||
int type = auxspi_save_type(card_type);
|
||||
if(size < 16)
|
||||
size_blocks = 1;
|
||||
else
|
||||
size_blocks = 1 << (size - 16);
|
||||
u32 LEN = std::min(1 << size, 1 << 16);
|
||||
buffer = new unsigned char[LEN*size_blocks];
|
||||
auxspi_read_data(0, buffer, LEN*size_blocks, type, card_type);
|
||||
fwrite(buffer, 1, LEN*size_blocks, out);
|
||||
} else {
|
||||
int size = cardEepromGetSizeFixed();
|
||||
buffer = new unsigned char[size];
|
||||
cardReadEeprom(0, buffer, size, type);
|
||||
while(*(u8*)(0x080000B2) != 0x96) {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
swiWaitForVBlank();
|
||||
scanKeys();
|
||||
|
||||
if(keysDown() & KEY_B) {
|
||||
delete[] compressedBuffer;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
u8 *finalBuffer = new u8[size];
|
||||
decompress(compressedBuffer, finalBuffer, LZ77);
|
||||
|
||||
char destPath[256];
|
||||
sprintf(destPath, "%s:/gm9i/out/%s.sav", (sdMounted ? "sd" : "fat"), fileName);
|
||||
FILE *destinationFile = fopen(destPath, "wb");
|
||||
if(destinationFile) {
|
||||
fwrite(finalBuffer, 1, size, destinationFile);
|
||||
fclose(destinationFile);
|
||||
}
|
||||
|
||||
delete[] finalBuffer;
|
||||
}
|
||||
} while(bytesRead < compressedSize);
|
||||
|
||||
delete[] compressedBuffer;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ndsCardSaveDump(const char* filename) {
|
||||
font->clear(false);
|
||||
font->print(0, 0, false, STR_DUMPING_SAVE);
|
||||
font->print(0, 1, false, STR_DO_NOT_REMOVE_CARD);
|
||||
font->update(false);
|
||||
|
||||
int type = cardEepromGetTypeFixed();
|
||||
|
||||
if(type == -1) { // NAND
|
||||
u32 saveSize = cardNandGetSaveSize();
|
||||
|
||||
if(saveSize == 0) {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_SAVE);
|
||||
return;
|
||||
}
|
||||
|
||||
u32 currentSize = saveSize;
|
||||
FILE* destinationFile = fopen(filename, "wb");
|
||||
if (destinationFile) {
|
||||
|
||||
font->print(0, 4, false, STR_PROGRESS);
|
||||
font->print(0, 5, false, "[");
|
||||
font->print(-1, 5, false, "]");
|
||||
for (u32 src = 0; src < saveSize; src += 0x8000) {
|
||||
// Print time
|
||||
font->print(-1, 0, true, RetTime(), Alignment::right, Palette::blackGreen);
|
||||
font->update(true);
|
||||
|
||||
font->print((src / (saveSize / (SCREEN_COLS - 2))) + 1, 5, false, "=");
|
||||
font->printf(0, 6, false, Alignment::left, Palette::white, STR_N_OF_N_BYTES.c_str(), src, saveSize);
|
||||
font->update(false);
|
||||
|
||||
for (u32 i = 0; i < 0x8000; i += 0x200) {
|
||||
cardRead(cardNandRwStart + src + i, copyBuf + i, true);
|
||||
}
|
||||
if (fwrite(copyBuf, 1, (currentSize >= 0x8000 ? 0x8000 : currentSize), destinationFile) < 1) {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_SAVE);
|
||||
break;
|
||||
}
|
||||
currentSize -= 0x8000;
|
||||
}
|
||||
fclose(destinationFile);
|
||||
} else {
|
||||
dumpFailMsg(STR_FAILED_TO_DUMP_SAVE);
|
||||
}
|
||||
} else { // SPI
|
||||
unsigned char *buffer;
|
||||
auxspi_extra card_type = auxspi_has_extra();
|
||||
int size = 0;
|
||||
if(card_type == AUXSPI_INFRARED) {
|
||||
int sizeLog2 = auxspi_save_size_log_2(card_type);
|
||||
int size_blocks;
|
||||
int type = auxspi_save_type(card_type);
|
||||
if(sizeLog2 < 16)
|
||||
size_blocks = 1;
|
||||
else
|
||||
size_blocks = 1 << (sizeLog2 - 16);
|
||||
u32 LEN = std::min(1 << sizeLog2, 1 << 16);
|
||||
size = LEN * size_blocks;
|
||||
buffer = new unsigned char[size];
|
||||
auxspi_read_data(0, buffer, size, type, card_type);
|
||||
} else {
|
||||
size = cardEepromGetSizeFixed();
|
||||
buffer = new unsigned char[size];
|
||||
cardReadEeprom(0, buffer, size, type);
|
||||
}
|
||||
if(sdMounted || flashcardMounted) {
|
||||
FILE *out = fopen(filename, "wb");
|
||||
if(out) {
|
||||
fwrite(buffer, 1, size, out);
|
||||
}
|
||||
delete[] buffer;
|
||||
fclose(out);
|
||||
} else {
|
||||
writeToGbaSave(filename, buffer, size);
|
||||
}
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@ -561,7 +763,7 @@ void ndsCardDump(void) {
|
||||
font->update(false);
|
||||
|
||||
std::vector allowedOptions = {DumpOption::all};
|
||||
u8 allowedBitfield = DumpOption::metadata;
|
||||
u8 allowedBitfield = 0;
|
||||
char gameTitle[13] = {0};
|
||||
char gameCode[7] = {0};
|
||||
char fileName[32] = {0};
|
||||
@ -570,19 +772,24 @@ void ndsCardDump(void) {
|
||||
|
||||
int cardInited = cardInit(&ndsCardHeader);
|
||||
if(cardInited == 0) {
|
||||
allowedOptions.push_back(DumpOption::allTrimmed);
|
||||
allowedOptions.push_back(DumpOption::rom);
|
||||
allowedOptions.push_back(DumpOption::romTrimmed);
|
||||
allowedBitfield |= DumpOption::rom | DumpOption::romTrimmed;
|
||||
if(sdMounted || flashcardMounted) {
|
||||
allowedOptions.push_back(DumpOption::allTrimmed);
|
||||
allowedOptions.push_back(DumpOption::rom);
|
||||
allowedOptions.push_back(DumpOption::romTrimmed);
|
||||
allowedBitfield |= DumpOption::rom | DumpOption::romTrimmed;
|
||||
}
|
||||
|
||||
nandSave = cardNandGetSaveSize() != 0;
|
||||
|
||||
if(spiSave || nandSave) {
|
||||
if((spiSave && cardEepromGetSizeFixed() <= (1 << 20)) || (nandSave && (sdMounted || flashcardMounted))) {
|
||||
allowedOptions.push_back(DumpOption::save);
|
||||
allowedBitfield |= DumpOption::save;
|
||||
}
|
||||
}
|
||||
allowedOptions.push_back(DumpOption::metadata);
|
||||
if(sdMounted || flashcardMounted) {
|
||||
allowedBitfield |= DumpOption::metadata;
|
||||
allowedOptions.push_back(DumpOption::metadata);
|
||||
}
|
||||
|
||||
tonccpy(gameTitle, ndsCardHeader.gameTitle, 12);
|
||||
tonccpy(gameCode, ndsCardHeader.gameCode, 6);
|
||||
@ -615,7 +822,7 @@ void ndsCardDump(void) {
|
||||
strcat(fileName, "_trim");
|
||||
|
||||
// Ensure directories exist
|
||||
if((dumpOption & allowedBitfield) != DumpOption::none) {
|
||||
if((dumpOption & allowedBitfield) != DumpOption::none && (sdMounted || flashcardMounted)) {
|
||||
char folderPath[2][256];
|
||||
sprintf(folderPath[0], "%s:/gm9i", (sdMounted ? "sd" : "fat"));
|
||||
sprintf(folderPath[1], "%s:/gm9i/out", (sdMounted ? "sd" : "fat"));
|
||||
@ -686,7 +893,7 @@ void ndsCardDump(void) {
|
||||
if ((dumpOption & allowedBitfield) & DumpOption::save) {
|
||||
char destPath[256];
|
||||
sprintf(destPath, "%s:/gm9i/out/%s.sav", (sdMounted ? "sd" : "fat"), fileName);
|
||||
ndsCardSaveDump(destPath);
|
||||
ndsCardSaveDump((sdMounted || flashcardMounted) ? destPath : fileName);
|
||||
}
|
||||
|
||||
// Dump metadata
|
||||
@ -834,6 +1041,15 @@ void gbaCartDump(void) {
|
||||
if(saveType != saveTypeGBA::SAVE_GBA_NONE) {
|
||||
allowedOptions.push_back(DumpOption::save);
|
||||
allowedBitfield |= DumpOption::save;
|
||||
|
||||
u32 size = gbaGetSaveSize(saveType);
|
||||
u8 *buffer = new u8[size];
|
||||
gbaReadSave(buffer, 0, size, saveType);
|
||||
if(memcmp(buffer, "9i", 3) == 0) {
|
||||
allowedOptions.push_back(DumpOption::ndsSave);
|
||||
allowedBitfield |= DumpOption::ndsSave;
|
||||
}
|
||||
delete[] buffer;
|
||||
}
|
||||
allowedOptions.push_back(DumpOption::metadata);
|
||||
|
||||
@ -990,6 +1206,11 @@ void gbaCartDump(void) {
|
||||
gbaCartSaveDump(destPath);
|
||||
}
|
||||
|
||||
// Dump NDS save previously saved to this cart
|
||||
if ((dumpOption & allowedBitfield) & DumpOption::ndsSave) {
|
||||
readFromGbaCart();
|
||||
}
|
||||
|
||||
// Dump metadata
|
||||
if ((dumpOption & allowedBitfield) & DumpOption::metadata) {
|
||||
font->clear(false);
|
||||
|
||||
@ -98,9 +98,8 @@ void writeEeprom(u32 dst, u8 *src, u32 len) {
|
||||
fifoSendValue32(FIFO_USER_01, len);
|
||||
|
||||
// Wait for it to finish
|
||||
while(!(fifoCheckValue32(FIFO_USER_02) && fifoGetValue32(FIFO_USER_02) == 0x454E4F44 /* 'DONE' */)) {
|
||||
swiWaitForVBlank();
|
||||
}
|
||||
fifoWaitValue32(FIFO_USER_02);
|
||||
fifoGetValue32(FIFO_USER_02);
|
||||
|
||||
sysSetCartOwner(BUS_OWNER_ARM9);
|
||||
}
|
||||
|
||||
@ -45,6 +45,7 @@ STRING(NITROFS_LABEL, "[nitro:] NDS GAME IMAGE")
|
||||
STRING(FAT_LABEL, "[img:] FAT IMAGE (%s)")
|
||||
STRING(GBA_GAMECART, "GBA GAMECART (%s)")
|
||||
STRING(NDS_GAMECARD, "NDS GAMECARD (%s)")
|
||||
STRING(NDS_GAMECARD_NO_TITLE, "NDS GAMECARD")
|
||||
|
||||
// Drive bottom screen labels
|
||||
STRING(SD_FAT, "(SD FAT, %s)")
|
||||
@ -129,11 +130,13 @@ STRING(EOF_NO_RESULTS, "Reached end of file\nwith no results")
|
||||
// Dumping
|
||||
STRING(FLASHCARD_WILL_UNMOUNT, "Flashcard will be unmounted.\nIs this okay?")
|
||||
STRING(DUMP_TO, "Dump \"%s\" to\n\"%s:/gm9i/out\"?")
|
||||
STRING(DUMP_TO_GBA, "Dump \"%s\" to GBA cart?")
|
||||
STRING(DUMP_ALL, "All")
|
||||
STRING(DUMP_ALL_TRIMMED, "All (Trimmed ROM)")
|
||||
STRING(DUMP_ROM, "ROM")
|
||||
STRING(DUMP_ROM_TRIMMED, "ROM (Trimmed)")
|
||||
STRING(DUMP_SAVE, "Save")
|
||||
STRING(DUMP_DS_SAVE, "DS Save")
|
||||
STRING(DUMP_METADATA, "Metadata")
|
||||
STRING(DO_NOT_REMOVE_CARD, "Do not remove the NDS card.")
|
||||
STRING(DO_NOT_REMOVE_CART, "Do not remove the GBA cart.")
|
||||
@ -154,11 +157,20 @@ STRING(PROGRESS, "Progress:")
|
||||
STRING(N_OF_N_BYTES, "%d/%d Bytes")
|
||||
STRING(NDS_IS_DUMPING, "%s.nds\nis dumping...")
|
||||
STRING(GBA_IS_DUMPING, "%s.gba\nis dumping...")
|
||||
STRING(COMPRESSING_SAVE, "Compressing save...")
|
||||
STRING(WRITING_SAVE, "Writing save...")
|
||||
STRING(WRITE_TO_GBA, "Write %s to GBA cart? (%s remaining)\n\nMake sure to back up your GBA save first!")
|
||||
STRING(SWITCH_CART, "Please switch to a different GBA cart.")
|
||||
STRING(SWITCH_CART_TO_SECTION, "Please switch to the GBA cart containing section %d.")
|
||||
STRING(SWITCH_CART_TO_SECTION_THIS_WAS, "Please switch to the GBA cart containing section %d. (This was section %d)")
|
||||
STRING(WRONG_DS_SAVE, "This cart contains a save file from a different DS game.")
|
||||
STRING(NO_DS_SAVE, "This cart doesn't contain a DS save.")
|
||||
|
||||
// Confirmation/option button info
|
||||
STRING(A_OK, "(\\A OK)")
|
||||
STRING(A_YES_B_NO, "(\\A yes, \\B no)")
|
||||
STRING(A_CONTINUE, "(\\A continue)")
|
||||
STRING(B_CANCEL, "(\\B cancel)")
|
||||
STRING(A_SELECT_B_CANCEL, "(\\A select, \\B cancel)")
|
||||
STRING(START_CANCEL, "(START cancel)")
|
||||
STRING(UDLR_CHANGE_ATTRIBUTES, "(\\D change attributes)")
|
||||
|
||||
@ -1,34 +1,361 @@
|
||||
#include <nds.h>
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*-- lzss.c - LZSS coding for Nintendo GBA/DS --*/
|
||||
/*-- Copyright (C) 2011 CUE --*/
|
||||
/*-- --*/
|
||||
/*-- This program is free software: you can redistribute it and/or modify --*/
|
||||
/*-- it under the terms of the GNU General Public License as published by --*/
|
||||
/*-- the Free Software Foundation, either version 3 of the License, or --*/
|
||||
/*-- (at your option) any later version. --*/
|
||||
/*-- --*/
|
||||
/*-- This program is distributed in the hope that it will be useful, --*/
|
||||
/*-- but WITHOUT ANY WARRANTY; without even the implied warranty of --*/
|
||||
/*-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the --*/
|
||||
/*-- GNU General Public License for more details. --*/
|
||||
/*-- --*/
|
||||
/*-- You should have received a copy of the GNU General Public License --*/
|
||||
/*-- along with this program. If not, see <http://www.gnu.org/licenses/>. --*/
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "lzss.h"
|
||||
|
||||
#define __itcm __attribute__((section(".itcm")))
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#define CMD_DECODE 0x00 // decode
|
||||
#define CMD_CODE_10 0x10 // LZSS magic number
|
||||
|
||||
void __itcm
|
||||
LZ77_Decompress(u8* source, u8* destination){
|
||||
u32 leng = (source[1] | (source[2] << 8) | (source[3] << 16));
|
||||
int Offs = 4;
|
||||
int dstoffs = 0;
|
||||
while (true)
|
||||
{
|
||||
u8 header = source[Offs++];
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if ((header & 0x80) == 0) destination[dstoffs++] = source[Offs++];
|
||||
else
|
||||
{
|
||||
u8 a = source[Offs++];
|
||||
u8 b = source[Offs++];
|
||||
int offs = (((a & 0xF) << 8) | b) + 1;
|
||||
int length = (a >> 4) + 3;
|
||||
for (int j = 0; j < length; j++)
|
||||
{
|
||||
destination[dstoffs] = destination[dstoffs - offs];
|
||||
dstoffs++;
|
||||
}
|
||||
}
|
||||
if (dstoffs >= (int)leng) return;
|
||||
header <<= 1;
|
||||
}
|
||||
}
|
||||
#define LZS_NORMAL 0x00 // normal mode, (0)
|
||||
#define LZS_FAST 0x80 // fast mode, (1 << 7)
|
||||
#define LZS_BEST 0x40 // best mode, (1 << 6)
|
||||
|
||||
#define LZS_WRAM 0x00 // VRAM not compatible (LZS_WRAM | LZS_NORMAL)
|
||||
#define LZS_VRAM 0x01 // VRAM compatible (LZS_VRAM | LZS_NORMAL)
|
||||
#define LZS_WFAST 0x80 // LZS_WRAM fast (LZS_WRAM | LZS_FAST)
|
||||
#define LZS_VFAST 0x81 // LZS_VRAM fast (LZS_VRAM | LZS_FAST)
|
||||
#define LZS_WBEST 0x40 // LZS_WRAM best (LZS_WRAM | LZS_BEST)
|
||||
#define LZS_VBEST 0x41 // LZS_VRAM best (LZS_VRAM | LZS_BEST)
|
||||
|
||||
#define LZS_SHIFT 1 // bits to shift
|
||||
#define LZS_MASK 0x80 // bits to check:
|
||||
// ((((1 << LZS_SHIFT) - 1) << (8 - LZS_SHIFT)
|
||||
|
||||
#define LZS_THRESHOLD 2 // max number of bytes to not encode
|
||||
#define LZS_N 0x1000 // max offset (1 << 12)
|
||||
#define LZS_F 0x12 // max coded ((1 << 4) + LZS_THRESHOLD)
|
||||
#define LZS_NIL LZS_N // index for root of binary search trees
|
||||
|
||||
#define RAW_MINIM 0x00000000 // empty file, 0 bytes
|
||||
#define RAW_MAXIM 0x00FFFFFF // 3-bytes length, 16MB - 1
|
||||
|
||||
#define LZS_MINIM 0x00000004 // header only (empty RAW file)
|
||||
#define LZS_MAXIM 0x01400000 // 0x01200003, padded to 20MB:
|
||||
// * header, 4
|
||||
// * length, RAW_MAXIM
|
||||
// * flags, (RAW_MAXIM + 7) / 8
|
||||
// 4 + 0x00FFFFFF + 0x00200000 + padding
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
unsigned char ring[LZS_N + LZS_F - 1];
|
||||
int dad[LZS_N + 1], lson[LZS_N + 1], rson[LZS_N + 1 + 256];
|
||||
int pos_ring, len_ring, lzs_vram;
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
#define BREAK(text) { printf(text); return; }
|
||||
#define EXIT(text) { printf(text); exit(-1); }
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
unsigned char *Memory(int length, int size);
|
||||
|
||||
unsigned char *LZS_Encode(unsigned char *raw_buffer, int raw_len, int mode, int *pak_len);
|
||||
unsigned char *LZS_Code(unsigned char *raw_buffer, int raw_len, unsigned int *new_len, int best);
|
||||
|
||||
unsigned char *LZS_Fast(unsigned char *raw_buffer, int raw_len, unsigned int *new_len);
|
||||
void LZS_InitTree(void);
|
||||
void LZS_InsertNode(int r);
|
||||
void LZS_DeleteNode(int p);
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
unsigned char *Memory(int length, int size) {
|
||||
unsigned char *fb;
|
||||
|
||||
fb = (unsigned char *) calloc(length, size);
|
||||
if (fb == NULL) EXIT("\nMemory error\n");
|
||||
|
||||
return(fb);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
unsigned char *LZS_Encode(unsigned char *raw_buffer, int raw_len, int mode, int *pak_len) {
|
||||
unsigned char *pak_buffer, *new_buffer;
|
||||
unsigned int new_len;
|
||||
|
||||
lzs_vram = mode & 0xF;
|
||||
|
||||
// printf("- encoding '%s'", filename);
|
||||
|
||||
// raw_buffer = Load(filename, &raw_len, RAW_MINIM, RAW_MAXIM);
|
||||
|
||||
pak_buffer = NULL;
|
||||
*pak_len = LZS_MAXIM + 1;
|
||||
|
||||
if (!(mode & LZS_FAST)) {
|
||||
mode = mode & LZS_BEST ? 1 : 0;
|
||||
new_buffer = LZS_Code(raw_buffer, raw_len, &new_len, mode);
|
||||
} else {
|
||||
new_buffer = LZS_Fast(raw_buffer, raw_len, &new_len);
|
||||
}
|
||||
if (new_len < *pak_len) {
|
||||
if (pak_buffer != NULL) free(pak_buffer);
|
||||
pak_buffer = new_buffer;
|
||||
if(pak_len)
|
||||
*pak_len = new_len;
|
||||
}
|
||||
|
||||
// Save(filename, pak_buffer, pak_len);
|
||||
|
||||
// free(pak_buffer);
|
||||
// free(raw_buffer);
|
||||
|
||||
// printf("\n");
|
||||
|
||||
return pak_buffer;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
unsigned char *LZS_Code(unsigned char *raw_buffer, int raw_len, unsigned int *new_len, int best) {
|
||||
unsigned char *pak_buffer, *pak, *raw, *raw_end, *flg;
|
||||
unsigned int pak_len, len, pos, len_best, pos_best;
|
||||
unsigned int len_next, pos_next, len_post, pos_post;
|
||||
unsigned char mask;
|
||||
|
||||
#define SEARCH(l,p) { \
|
||||
l = LZS_THRESHOLD; \
|
||||
\
|
||||
pos = raw - raw_buffer >= LZS_N ? LZS_N : raw - raw_buffer; \
|
||||
for ( ; pos > lzs_vram; pos--) { \
|
||||
for (len = 0; len < LZS_F; len++) { \
|
||||
if (raw + len == raw_end) break; \
|
||||
if (*(raw + len) != *(raw + len - pos)) break; \
|
||||
} \
|
||||
\
|
||||
if (len > l) { \
|
||||
p = pos; \
|
||||
if ((l = len) == LZS_F) break; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
pak_len = 4 + raw_len + ((raw_len + 7) / 8);
|
||||
pak_buffer = (unsigned char *) Memory(pak_len, sizeof(char));
|
||||
|
||||
*(unsigned int *)pak_buffer = CMD_CODE_10 | (raw_len << 8);
|
||||
|
||||
pak = pak_buffer + 4;
|
||||
raw = raw_buffer;
|
||||
raw_end = raw_buffer + raw_len;
|
||||
|
||||
mask = 0;
|
||||
|
||||
while (raw < raw_end) {
|
||||
if (!(mask >>= LZS_SHIFT)) {
|
||||
*(flg = pak++) = 0;
|
||||
mask = LZS_MASK;
|
||||
}
|
||||
|
||||
SEARCH(len_best, pos_best);
|
||||
|
||||
// LZ-CUE optimization start
|
||||
if (best) {
|
||||
if (len_best > LZS_THRESHOLD) {
|
||||
if (raw + len_best < raw_end) {
|
||||
raw += len_best;
|
||||
SEARCH(len_next, pos_next);
|
||||
raw -= len_best - 1;
|
||||
SEARCH(len_post, pos_post);
|
||||
raw--;
|
||||
|
||||
if (len_next <= LZS_THRESHOLD) len_next = 1;
|
||||
if (len_post <= LZS_THRESHOLD) len_post = 1;
|
||||
|
||||
if (len_best + len_next <= 1 + len_post) len_best = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// LZ-CUE optimization end
|
||||
|
||||
if (len_best > LZS_THRESHOLD) {
|
||||
raw += len_best;
|
||||
*flg |= mask;
|
||||
*pak++ = ((len_best - (LZS_THRESHOLD + 1)) << 4) | ((pos_best - 1) >> 8);
|
||||
*pak++ = (pos_best - 1) & 0xFF;
|
||||
} else {
|
||||
*pak++ = *raw++;
|
||||
}
|
||||
}
|
||||
|
||||
*new_len = pak - pak_buffer;
|
||||
|
||||
return(pak_buffer);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
unsigned char *LZS_Fast(unsigned char *raw_buffer, int raw_len, unsigned int *new_len) {
|
||||
unsigned char *pak_buffer, *pak, *raw, *raw_end, *flg;
|
||||
unsigned int pak_len, len, r, s, len_tmp, i;
|
||||
unsigned char mask;
|
||||
|
||||
pak_len = 4 + raw_len + ((raw_len + 7) / 8);
|
||||
pak_buffer = (unsigned char *) Memory(pak_len, sizeof(char));
|
||||
|
||||
*(unsigned int *)pak_buffer = CMD_CODE_10 | (raw_len << 8);
|
||||
|
||||
pak = pak_buffer + 4;
|
||||
raw = raw_buffer;
|
||||
raw_end = raw_buffer + raw_len;
|
||||
|
||||
LZS_InitTree();
|
||||
|
||||
r = s = 0;
|
||||
|
||||
len = raw_len < LZS_F ? raw_len : LZS_F;
|
||||
while (r < LZS_N - len) ring[r++] = 0;
|
||||
|
||||
for (i = 0; i < len; i++) ring[r + i] = *raw++;
|
||||
|
||||
LZS_InsertNode(r);
|
||||
|
||||
mask = 0;
|
||||
|
||||
while (len) {
|
||||
if (!(mask >>= LZS_SHIFT)) {
|
||||
*(flg = pak++) = 0;
|
||||
mask = LZS_MASK;
|
||||
}
|
||||
|
||||
if (len_ring > len) len_ring = len;
|
||||
|
||||
if (len_ring > LZS_THRESHOLD) {
|
||||
*flg |= mask;
|
||||
pos_ring = ((r - pos_ring) & (LZS_N - 1)) - 1;
|
||||
*pak++ = ((len_ring - LZS_THRESHOLD - 1) << 4) | (pos_ring >> 8);
|
||||
*pak++ = pos_ring & 0xFF;
|
||||
} else {
|
||||
len_ring = 1;
|
||||
*pak++ = ring[r];
|
||||
}
|
||||
|
||||
len_tmp = len_ring;
|
||||
for (i = 0; i < len_tmp; i++) {
|
||||
if (raw == raw_end) break;
|
||||
LZS_DeleteNode(s);
|
||||
ring[s] = *raw++;
|
||||
if (s < LZS_F - 1) ring[s + LZS_N] = ring[s];
|
||||
s = (s + 1) & (LZS_N - 1);
|
||||
r = (r + 1) & (LZS_N - 1);
|
||||
LZS_InsertNode(r);
|
||||
}
|
||||
while (i++ < len_tmp) {
|
||||
LZS_DeleteNode(s);
|
||||
s = (s + 1) & (LZS_N - 1);
|
||||
r = (r + 1) & (LZS_N - 1);
|
||||
if (--len) LZS_InsertNode(r);
|
||||
}
|
||||
}
|
||||
|
||||
*new_len = pak - pak_buffer;
|
||||
|
||||
return(pak_buffer);
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
void LZS_InitTree(void) {
|
||||
int i;
|
||||
|
||||
for (i = LZS_N + 1; i <= LZS_N + 256; i++)
|
||||
rson[i] = LZS_NIL;
|
||||
|
||||
for (i = 0; i < LZS_N; i++)
|
||||
dad[i] = LZS_NIL;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
void LZS_InsertNode(int r) {
|
||||
unsigned char *key;
|
||||
int i, p, cmp, prev;
|
||||
|
||||
prev = (r - 1) & (LZS_N - 1);
|
||||
|
||||
cmp = 1;
|
||||
len_ring = 0;
|
||||
|
||||
key = &ring[r];
|
||||
p = LZS_N + 1 + key[0];
|
||||
|
||||
rson[r] = lson[r] = LZS_NIL;
|
||||
|
||||
for ( ; ; ) {
|
||||
if (cmp >= 0) {
|
||||
if (rson[p] != LZS_NIL) p = rson[p];
|
||||
else { rson[p] = r; dad[r] = p; return; }
|
||||
} else {
|
||||
if (lson[p] != LZS_NIL) p = lson[p];
|
||||
else { lson[p] = r; dad[r] = p; return; }
|
||||
}
|
||||
|
||||
for (i = 1; i < LZS_F; i++)
|
||||
if ((cmp = key[i] - ring[p + i])) break;
|
||||
|
||||
if (i > len_ring) {
|
||||
if (!lzs_vram || (p != prev)) {
|
||||
pos_ring = p;
|
||||
if ((len_ring = i) == LZS_F) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dad[r] = dad[p]; lson[r] = lson[p]; rson[r] = rson[p];
|
||||
|
||||
dad[lson[p]] = r; dad[rson[p]] = r;
|
||||
|
||||
if (rson[dad[p]] == p) rson[dad[p]] = r;
|
||||
else lson[dad[p]] = r;
|
||||
|
||||
dad[p] = LZS_NIL;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
void LZS_DeleteNode(int p) {
|
||||
int q;
|
||||
|
||||
if (dad[p] == LZS_NIL) return;
|
||||
|
||||
if (rson[p] == LZS_NIL) {
|
||||
q = lson[p];
|
||||
} else if (lson[p] == LZS_NIL) {
|
||||
q = rson[p];
|
||||
} else {
|
||||
q = lson[p];
|
||||
if (rson[q] != LZS_NIL) {
|
||||
do {
|
||||
q = rson[q];
|
||||
} while (rson[q] != LZS_NIL);
|
||||
|
||||
rson[dad[q]] = lson[q]; dad[lson[q]] = dad[q];
|
||||
lson[q] = lson[p]; dad[lson[p]] = q;
|
||||
}
|
||||
|
||||
rson[q] = rson[p]; dad[rson[p]] = q;
|
||||
}
|
||||
|
||||
dad[q] = dad[p];
|
||||
|
||||
if (rson[dad[p]] == p) rson[dad[p]] = q;
|
||||
else lson[dad[p]] = q;
|
||||
|
||||
dad[p] = LZS_NIL;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------*/
|
||||
/*-- EOF Copyright (C) 2011 CUE --*/
|
||||
/*----------------------------------------------------------------------------*/
|
||||
|
||||
@ -1,12 +1,22 @@
|
||||
#ifndef LZ77_DECOMPRESS_H
|
||||
#define LZ77_DECOMPRESS_H
|
||||
#ifndef LZ77_COMPRESS_H
|
||||
#define LZ77_COMPRESS_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void LZ77_Decompress(u8* source, u8* destination);
|
||||
|
||||
#define LZS_WRAM 0x00 // VRAM not compatible (LZS_WRAM | LZS_NORMAL)
|
||||
#define LZS_VRAM 0x01 // VRAM compatible (LZS_VRAM | LZS_NORMAL)
|
||||
#define LZS_WFAST 0x80 // LZS_WRAM fast (LZS_WRAM | LZS_FAST)
|
||||
#define LZS_VFAST 0x81 // LZS_VRAM fast (LZS_VRAM | LZS_FAST)
|
||||
#define LZS_WBEST 0x40 // LZS_WRAM best (LZS_WRAM | LZS_BEST)
|
||||
#define LZS_VBEST 0x41 // LZS_VRAM best (LZS_VRAM | LZS_BEST)
|
||||
|
||||
// Returned buffer must be freed manually
|
||||
// pak_len will be the length of the compressed output
|
||||
unsigned char *LZS_Encode(unsigned char *raw_buffer, int raw_len, int mode, int *pak_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* DECOMPRESS_H */
|
||||
#endif /* LZ77_COMPRESS_H */
|
||||
|
||||
@ -44,6 +44,7 @@ NITROFS_LABEL=[nitro:] NDS GAME IMAGE
|
||||
FAT_LABEL=[img:] FAT IMAGE (%s)
|
||||
GBA_GAMECART=GBA GAMECART (%s)
|
||||
NDS_GAMECARD=NDS GAMECARD (%s)
|
||||
NDS_GAMECARD_NO_TITLE=NDS GAMECARD
|
||||
|
||||
SD_FAT=(SD FAT, %s)
|
||||
N_FREE=%s free
|
||||
@ -121,11 +122,13 @@ EOF_NO_RESULTS=Reached end of file\nwith no results
|
||||
|
||||
FLASHCARD_WILL_UNMOUNT=Flashcard will be unmounted.\nIs this okay?
|
||||
DUMP_TO=Dump "%s" to\n"%s:/gm9i/out"?
|
||||
DUMP_TO_GBA=Dump "%s" to GBA cart?
|
||||
DUMP_ALL=All
|
||||
DUMP_ALL_TRIMMED=All (Trimmed ROM)
|
||||
DUMP_ROM=ROM
|
||||
DUMP_ROM_TRIMMED=ROM (Trimmed)
|
||||
DUMP_SAVE=Save
|
||||
DUMP_DS_SAVE=DS Save
|
||||
DUMP_METADATA=Metadata
|
||||
DO_NOT_REMOVE_CARD=Do not remove the NDS card.
|
||||
DO_NOT_REMOVE_CART=Do not remove the GBA cart.
|
||||
@ -146,10 +149,19 @@ PROGRESS=Progress:
|
||||
N_OF_N_BYTES=%d/%d Bytes
|
||||
NDS_IS_DUMPING=%s.nds\nis dumping...
|
||||
GBA_IS_DUMPING=%s.gba\nis dumping...
|
||||
COMPRESSING_SAVE=Compressing save...
|
||||
WRITING_SAVE=Writing save...
|
||||
WRITE_TO_GBA=Write %s to GBA cart? (%s remaining)\n\nMake sure to back up your GBA save first!
|
||||
SWITCH_CART=Please switch to a different GBA cart.
|
||||
SWITCH_CART_TO_SECTION=Please switch to the GBA cart containing section %d.
|
||||
SWITCH_CART_TO_SECTION_THIS_WAS=Please switch to the GBA cart containing section %d. (This was section %d)
|
||||
WRONG_DS_SAVE=This cart contains a save file from a different DS game.
|
||||
NO_DS_SAVE=This cart doesn't contain a DS save.
|
||||
|
||||
A_OK=(\A OK)
|
||||
A_YES_B_NO=(\A yes, \B no)
|
||||
A_CONTINUE=(\A continue)
|
||||
B_CANCEL=(\B cancel)
|
||||
A_SELECT_B_CANCEL=(\A select, \B cancel)
|
||||
START_CANCEL=(START cancel)
|
||||
UDLR_CHANGE_ATTRIBUTES=(\D change attributes)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user