diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5acb669 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +.vscode diff --git a/Makefile b/Makefile index fbd4bd0..e1da464 100644 --- a/Makefile +++ b/Makefile @@ -1,28 +1,51 @@ -TARGET = vita-mcr2vmp +ifeq ($(OS),Windows_NT) + TARGET_EXEC ?= vita-mcr2vmp-win +else + UNAME_S := $(shell uname -s) + ifeq ($(UNAME_S),Linux) + TARGET_EXEC ?= vita-mcr2vmp-linux + endif + ifeq ($(UNAME_S),Darwin) + TARGET_EXEC ?= vita-mcr2vmp-macos + endif +endif -OBJS = aes.o main.o sha1.o +BUILD_DIR ?= ./build +SRC_DIRS ?= ./src ./include +SRCS := $(shell find $(SRC_DIRS) -name *.cpp -or -name *.c -or -name *.s) +OBJS := $(SRCS:%=$(BUILD_DIR)/%.o) +DEPS := $(OBJS:.o=.d) -LIBS = -lz +INC_DIRS := $(shell find $(SRC_DIRS) -type d) +INC_FLAGS := $(addprefix -I,$(INC_DIRS)) -CFLAGS = -s -static -Wall -Wextra -std=c99 -all: $(TARGET) +CPPFLAGS ?= $(INC_FLAGS) -s -static -Wall -Wextra -std=c99 -$(TARGET): $(OBJS) - @echo "Creating binary $(TARGET)" - $(CXX) $(OBJS) -o $@ $(LIBS) -static -static-libgcc +$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS) + $(CC) $(OBJS) -o $@ $(LDFLAGS) -%.o: %.cpp - @echo "Compiling $^" - $(CXX) $(CFLAGS) -c $^ -o $@ -static -static-libgcc +# assembly +$(BUILD_DIR)/%.s.o: %.s + $(MKDIR_P) $(dir $@) + $(AS) $(ASFLAGS) -c $< -o $@ + +# c source +$(BUILD_DIR)/%.c.o: %.c + $(MKDIR_P) $(dir $@) + $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@ + +# c++ source +$(BUILD_DIR)/%.cpp.o: %.cpp + $(MKDIR_P) $(dir $@) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@ + + +.PHONY: clean clean: - @echo "Removing all the .o files" - @$(RM) $(OBJS) + $(RM) -r $(BUILD_DIR) -mrproper: clean - @echo "Removing binary" - @$(RM) $(TARGET) +-include $(DEPS) -install: all - @cp $(TARGET) ../bin +MKDIR_P ?= mkdir -p \ No newline at end of file diff --git a/README.md b/README.md index 6585e28..ac312c2 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,24 @@ # vita-mcr2vmp -by @dots_tb - signs psx mcr files for use with the vita's psp emulator +by [@dots_tb](https://github.com/dots-tb) - signs PSOne MCR files to create VMP files for use with Sony Vita/PSP and exports MCR files from VMP With help from the CBPS (https://discord.gg/2nDCbxJ) , especially: - - @AnalogMan151 - - @teakhanirons + [@AnalogMan151](https://github.com/AnalogMan151) + [@teakhanirons](https://github.com/teakhanirons) ## Usage: -Drag and drop a PSX MCR save file onto the exe. It will generate a VMP that you may use with your Vita's PSP emulator. +Drag and drop a PSOne MCR save file onto the program. It will generate a VMP that you may use with your Vita/PSP. +You may also drag and drop a VMP file and extract the contained MCR file for editing or sharing. Or use CMD: - - ./vita-mcr2vmp mem.mcr + ./vita-mcr2vmp ## Further Instructions: ### I wanna edit my PSX Save Data from the vita! 1. Grab your save data from here: ux0:/pspemu/PSP/SAVEDATA//SCEVMC<0|1>.VMP - 2. Open your save data on the computer with a hexeditor and delete the first 0x80, this will convert it to an MCR. - 3. Save it as .MCR. - 4. You may now use it with your save editor or emulators(I think) of choice. - 5. Follow the USAGE section to convert it back to a VMP. Make sure you place it back to the same location with the same file name. - 6. ENJOY! + 2. Run VMP file through program to export MCR. + 3. You may now use it with your save editor or emulators of choice. + 4. Follow the USAGE section to convert it back to a VMP. Make sure you place it back to the same location with the same file name. + 5. ENJOY! diff --git a/aes.h b/include/aes.h similarity index 100% rename from aes.h rename to include/aes.h diff --git a/sha1.h b/include/sha1.h similarity index 96% rename from sha1.h rename to include/sha1.h index 96bb008..c406d2a 100644 --- a/sha1.h +++ b/include/sha1.h @@ -39,6 +39,6 @@ void SHA1Final( void SHA1( char *hash_out, const char *str, - int len); + unsigned int len); #endif /* SHA1_H */ diff --git a/main.c b/main.c deleted file mode 100644 index c1c2ace..0000000 --- a/main.c +++ /dev/null @@ -1,138 +0,0 @@ -//vita-mcr2vmp by @dots_tb - signs psx mcr files for use with the vita's psp emulator -//With help from the CBPS (https://discord.gg/2nDCbxJ) , especially: -// Analog Man -// @teakhanirons - -#include -#include -#include -#include - -#define CBC 1 - -#include "aes.h" -#include "sha1.h" - -uint8_t key[0x10] = {0xAB, 0x5A, 0xBC, 0x9F, 0xC1, 0xF4, 0x9D, 0xE6, 0xA0, 0x51, 0xDB, 0xAE, 0xFA, 0x51, 0x88, 0x59}; -uint8_t iv[0x10] = {0xB3, 0x0F, 0xFE, 0xED, 0xB7, 0xDC, 0x5E, 0xB7, 0x13, 0x3D, 0xA6, 0x0D, 0x1B, 0x6B, 0x2C, 0xDC}; - -#define SEED_OFFSET 0xC -#define HASH_OFFSET 0x20 -#define MCR_OFFSET 0x80 -#define PMV_MAGIC 0x564D5000 -#define FILE_SZ 0x20080 - - void XorWithByte(uint8_t* buf, uint8_t byte, int length) -{ - for (int i = 0; i < length; ++i) - { - buf[i] ^= byte; - } -} - - -static void usage(char *argv[]) -{ - printf("vita-mcr2vmp by @dots_tb\n"); - printf("With CBPS help especially: Analog Man and @teakhanirons\n"); - printf("%s memorycard.mcr",argv[0]); -} - - -int main(int argc, const char **argv) -{ - if (argc != 2) { - usage(argv); - return 1; - } - FILE *fin, *fout; - fin = fopen(argv[1], "rb"); - if (!fin) { - perror("Failed to open input file"); - goto error; - } - - size_t sz = FILE_SZ; - uint8_t *input = (unsigned char*) calloc (1, sz); - uint32_t *input_ptr = (uint32_t*) input; - input_ptr[0] = PMV_MAGIC; - input_ptr[1] = MCR_OFFSET; - - fseek(fin, 0, SEEK_SET); - fread(input + MCR_OFFSET,sz,1,fin); - - struct AES_ctx aes_ctx; - AES_init_ctx_iv(&aes_ctx, key, iv); - - - uint8_t salt[0x40]; - uint8_t work_buf[0x14]; - - uint8_t *salt_seed = input + SEED_OFFSET; - - memcpy(work_buf, salt_seed, 0x10); - AES_ECB_decrypt(&aes_ctx, work_buf); - memcpy(salt, work_buf, 0x10); - - memcpy(work_buf, salt_seed, 0x10); - AES_ECB_encrypt(&aes_ctx, work_buf); - memcpy(salt + 0x10, work_buf, 0x10); - - - XorWithIv(salt, iv); - - memset(work_buf, 0xFF, sizeof(work_buf)); - memcpy(work_buf, salt_seed + 0x10, 0x4); - XorWithIv(salt + 0x10, work_buf); - - - memset(salt + 0x14, 0, sizeof(salt) - 0x14); - XorWithByte(salt, 0x36, 0x40); - - SHA1_CTX sha1_ctx_1; - SHA1Init(&sha1_ctx_1); - - - - SHA1Update( &sha1_ctx_1, salt, 0x40); - - - memset(input + 0x20, 0, 0x14); - SHA1Update( &sha1_ctx_1, input, sz); - - - XorWithByte(salt, 0x6A, 0x40); - - SHA1Final(work_buf, &sha1_ctx_1); - - SHA1_CTX sha1_ctx_2; - SHA1Init(&sha1_ctx_2); - SHA1Update( &sha1_ctx_2, salt, 0x40); - SHA1Update( &sha1_ctx_2, work_buf, 0x14); - - - - SHA1Final(input + HASH_OFFSET, &sha1_ctx_2); - - printf("Generated key: "); - for(int i = 0; i < 0x14; i++ ) { - printf("%02x ", work_buf[i]); - } - printf("\n"); - - char output_path[128]; - sprintf(output_path,"%s.vmp",argv[1]); - fout = fopen(output_path, "wb"); - if (!fout) { - perror("Failed to open output file"); - goto error; - } - fwrite(input, 1, FILE_SZ, fout); -error: - if (fin) - fclose(fin); - if (fout) - fclose(fout); - exit(0); - return 0; -} diff --git a/aes.c b/src/aes.c similarity index 100% rename from aes.c rename to src/aes.c diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..93c5995 --- /dev/null +++ b/src/main.c @@ -0,0 +1,168 @@ +//vita-mcr2vmp by @dots_tb - signs PSOne MCR files to create VMP files for use with Sony Vita/PSP and exports MCR files from VMP +//With help from the CBPS (https://discord.gg/2nDCbxJ) , especially: +// @AnalogMan151 +// @teakhanirons + +#include +#include +#include +#include +#include + +#include "aes.h" +#include "sha1.h" + +uint8_t key[0x10] = {0xAB, 0x5A, 0xBC, 0x9F, 0xC1, 0xF4, 0x9D, 0xE6, 0xA0, 0x51, 0xDB, 0xAE, 0xFA, 0x51, 0x88, 0x59}; +uint8_t iv[0x10] = {0xB3, 0x0F, 0xFE, 0xED, 0xB7, 0xDC, 0x5E, 0xB7, 0x13, 0x3D, 0xA6, 0x0D, 0x1B, 0x6B, 0x2C, 0xDC}; + +#define SEED_OFFSET 0xC +#define HASH_OFFSET 0x20 +#define MCR_OFFSET 0x80 +#define PMV_MAGIC 0x564D5000 +#define VMP_SZ 0x20080 +#define MC_SZ 0x20000 + +void XorWithByte(uint8_t* buf, uint8_t byte, int length) +{ + for (int i = 0; i < length; ++i) { + buf[i] ^= byte; + } +} + +static void usage(char *argv[]) +{ + printf("\nvita-mcr2vmp by @dots_tb\nWith CBPS help especially: @AnalogMan151 and @teakhanirons\n"); + printf("Converts PSOne MCRs into signed VMPs for use on PSP/Vita and also\n"); + printf("extracts MCR files from Sony's PSP VMP save file format.\n\n"); + printf("Usage: %s \n",argv[0]); +} + +int main(int argc, char **argv) +{ + printf("\n=====Vita MCR2VMP by @dots_tb=====\n\n"); + if (argc != 2) { + usage(argv); + return 1; + } + + FILE *fin, *fout; + char mc_magic[4] = {0x4D, 0x43, 0x00, 0x00}; + char vmp_magic[4] = {0x00, 0x50, 0x4D, 0x56}; + char magic_buf[4]; + bool mcr, vmp = false; + fin = fopen(argv[1], "rb"); + if (!fin) { + perror("Failed to open input file"); + goto error; + } + + // Check MAGIC + fseek(fin, 0, SEEK_SET); + fread(magic_buf, 1, 4, fin); + if (memcmp(magic_buf, mc_magic, 4) == 0) { + mcr = true; + } else if (memcmp(magic_buf, vmp_magic, 4) == 0) { + vmp = true; + } else { + perror("File is not supported"); + usage(argv); + goto error; + } + + // Passes check, strips VMP header and writes MCR + if (vmp) { + char mcbuf[MC_SZ]; + fseek(fin, MCR_OFFSET, SEEK_SET); + fread(mcbuf, 1, MC_SZ, fin); + fclose(fin); + char output_path[128]; + sprintf(output_path,"%s.mcr",argv[1]); + fout = fopen(output_path, "wb"); + if (!fout) { + perror("Failed to open output file"); + goto error; + } + fwrite(mcbuf, 1, MC_SZ, fout); + printf("MCR file successfully extracted.\n"); + } + + // Passes check, writes MCR with signed VMP header + if (mcr) { + uint8_t *input = (unsigned char*) calloc (1, VMP_SZ); + uint32_t *input_ptr = (uint32_t*) input; + input_ptr[0] = PMV_MAGIC; + input_ptr[1] = MCR_OFFSET; + + fseek(fin, 0, SEEK_SET); + fread(input + MCR_OFFSET, MC_SZ, 1, fin); + + struct AES_ctx aes_ctx; + AES_init_ctx_iv(&aes_ctx, key, iv); + + uint8_t salt[0x40]; + uint8_t work_buf[0x14]; + + uint8_t *salt_seed = input + SEED_OFFSET; + + memcpy(work_buf, salt_seed, 0x10); + AES_ECB_decrypt(&aes_ctx, work_buf); + memcpy(salt, work_buf, 0x10); + + memcpy(work_buf, salt_seed, 0x10); + AES_ECB_encrypt(&aes_ctx, work_buf); + memcpy(salt + 0x10, work_buf, 0x10); + + XorWithIv(salt, iv); + + memset(work_buf, 0xFF, sizeof(work_buf)); + memcpy(work_buf, salt_seed + 0x10, 0x4); + XorWithIv(salt + 0x10, work_buf); + + memset(salt + 0x14, 0, sizeof(salt) - 0x14); + XorWithByte(salt, 0x36, 0x40); + + SHA1_CTX sha1_ctx_1; + SHA1Init(&sha1_ctx_1); + + SHA1Update(&sha1_ctx_1, salt, 0x40); + + memset(input + HASH_OFFSET, 0, 0x14); + SHA1Update(&sha1_ctx_1, input, VMP_SZ); + + XorWithByte(salt, 0x6A, 0x40); + + SHA1Final(work_buf, &sha1_ctx_1); + + SHA1_CTX sha1_ctx_2; + SHA1Init(&sha1_ctx_2); + SHA1Update(&sha1_ctx_2, salt, 0x40); + SHA1Update(&sha1_ctx_2, work_buf, 0x14); + + SHA1Final(input + HASH_OFFSET, &sha1_ctx_2); + + printf("Generated key: "); + for(int i = 0; i < 0x14; i++ ) { + printf("%02X ", input[HASH_OFFSET + i]); + } + printf("\n"); + + char output_path[128]; + sprintf(output_path,"%s.VMP",argv[1]); + fout = fopen(output_path, "wb"); + if (!fout) { + perror("Failed to open output file"); + goto error; + } + fwrite(input, 1, VMP_SZ, fout); + free(input); + printf("VMP created successfully.\n"); + } + +error: + if (fin) + fclose(fin); + if (fout) + fclose(fout); + + return 0; +} diff --git a/sha1.c b/src/sha1.c similarity index 99% rename from sha1.c rename to src/sha1.c index c2bf174..75fd1af 100644 --- a/sha1.c +++ b/src/sha1.c @@ -282,7 +282,7 @@ void SHA1Final( void SHA1( char *hash_out, const char *str, - int len) + unsigned int len) { SHA1_CTX ctx; unsigned int ii;