New SEGAGAGA translation project in the works!

Place for discussing homebrew games, development, new releases and emulation.

Moderators: pcwzrd13, deluxux, VasiliyRS

Post Reply
User avatar
megavolt85
Developer
Posts: 1859
Joined: Wed Jan 31, 2018 4:14 pm

Re: New SEGAGAGA translation project in the works!

Post by megavolt85 »

ateam wrote: Are the .MRG container/archive files using compression for any of their assets?
yep
ateam wrote: If so, have you identified the algorithm?
This is some kind of LZSS. I removed the pseudo -cod through the dizassembler and corrected him that he would compile.

Code: Select all

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

uint8_t dictonary[40] =
{
	0x0C, 0x03, 0x0B, 0x03, 0x0A, 0x03, 0x09, 0x03, 
	0x06, 0x03, 0x05, 0x03, 0x06, 0x02, 0x05, 0x02,
	0x08, 0x04, 0x07, 0x04, 0x80, 0x40, 0x20, 0x10,
	0x08, 0x04, 0x02, 0x01, 0xF0, 0x0F, 0x00, 0x00,
	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};

uint32_t BE2LE16(uint8_t *src)
{
	return ((src[0] << 8) | src[1]);
}

uint32_t BE2LE32(uint8_t *src)
{
	return ((src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]);
}

uint32_t FUN_8c01a45a(uint8_t *buffer, uint32_t *offset)
{
	uint32_t ret;
	
	if (!(*offset & 1)) 
	{
		ret = buffer[*offset >> 1] >> 4;
	}
	else 
	{
		ret = buffer[*offset >> 1];
	}
	
	*offset = *offset + 1;
	
	return ret & 0xf;
}

uint32_t FUN_8c01a480(uint8_t *buffer, uint32_t *offset)
{
	uint32_t tmp;
	uint32_t ret;
	
	if ((*offset & 1) == 0) 
	{
		ret = buffer[*offset >> 1];
		*offset = *offset + 2;
	}
	else 
	{
		tmp = FUN_8c01a45a(buffer, offset);
		ret = FUN_8c01a45a(buffer, offset);
		ret = tmp << 4 | ret;
	}
	
	return ret;
}

void FUN_8c01a4c0(uint8_t orval, uint8_t *dst, uint32_t *offset)
{
	uint8_t val = dst[*offset >> 1];
	
	if (!(*offset & 1)) 
	{
		val = (val & 0x0F) | ((orval & 0x0F) << 4);
	}
	else 
	{
		val = (val & 0xF0) | (orval & 0xf);
	}
	
	dst[*offset >> 1] = val;
	*offset = *offset + 1;
}

uint32_t mrg_algo0(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint8_t bVar2;
	uint8_t bVar3;
	uint32_t uVar7;
	uint32_t uVar8;
	int iVar9;
	uint8_t *pbVar10;
	uint16_t control_byte;
	uint32_t i;
	
	control_byte = 0x7F80;
	bVar1 = dictonary[param_4 << 1];
	bVar3 = dictonary[(param_4 << 1) + 1];
	i = 0;
	
	do 
	{
		control_byte <<= 1;
		
		if (control_byte == 0xFF00) 
		{
			control_byte =  (*src++ << 8) | 0xFF;
		}
		
		if (!(control_byte & 0x8000)) 
		{
			uVar7 = BE2LE16(src);
			uVar8 = -(uint)bVar1;
			
			if (bVar1 == 0) 
			{
				uVar8 = (uVar7 & 0xffff) << (uVar8 & 0x1f);
			}
			else 
			{
				uVar8 = (uVar7 & 0xffff) >> ((~uVar8 & 0x1f) + 1);
			}
			
			uVar8 = uVar8 + (int)(short)(uint16_t)bVar3;
			iVar9 = i - (uVar7 & ((1 << (bVar1 & 0x1f)) + 0xFFFF) & 0xffff);
			src += 2;
			
			while (iVar9 < 0) 
			{
				iVar9++;
				dst[i] = 0;
				uVar8--;
				i++;
			}
			
			pbVar10 = dst + iVar9;
			
			while ((uVar8 & 0xffff) != 0) 
			{
				uVar8--;
				bVar2 = *pbVar10;
				pbVar10++;
				dst[i] = bVar2;
				i++;
			}
		}
		else 
		{
			dst[i++] = *src++;
		}
	} 
	while (i < len);
	
	return i;
}

uint32_t mrg_algo1(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint8_t bVar2;
	uint8_t bVar3;
	uint32_t uVar7;
	int n;
	uint32_t i;
	uint32_t uVar10;
	int iVar11;
	uint8_t *pbVar12;
	
	uVar10 = 0x7F80;
	bVar1 = dictonary[(param_4 << 1) + 8];
	bVar3 = dictonary[(param_4 << 1) + 9];
	n = 0;
	
	do 
	{
		uVar10 = uVar10 << 1;
		
		if ((uVar10 & 0xFFFF) == 0xFF00) 
		{
			bVar2 = *src;
			src++;
			uVar10 = 0xFF | (uint)bVar2 << 8;
		}
		
		if (!(uVar10 & 0x8000)) 
		{
			bVar2 = *src;
			iVar11 = n - ((uint)bVar2 & (int)(short)((short)(1 << ((uint)bVar1 & 0x1f)) + 0xFFFF));
			uVar7 = -(uint)bVar1;
			
			if (bVar1 == 0) 
			{
				uVar7 = (uint)bVar2 << (uVar7 & 0x1f);
			}
			else 
			{
				uVar7 = (uint)(bVar2 >> ((~uVar7 & 0x1f) + 1));
			}
			
			uVar7 = uVar7 + (int)(short)(uint16_t)bVar3;
			i = n;
			
			while (iVar11 < 0) 
			{
				iVar11++;
				dst[i] = 0;
				uVar7--;
				i++;
			}
			
			pbVar12 = dst + iVar11;
			
			while ((uVar7 & 0xffff) != 0) 
			{
				uVar7--;
				bVar2 = *pbVar12;
				pbVar12++;
				dst[i] = bVar2;
				i++;
			}
		}
		else 
		{
			i = n + 1;
			dst[n] = *src;
		}
		
		src++;
		n = i;
	} 
	while (i < len);
	
	return i;
}

uint32_t mrg_algo2(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint32_t uVar3;
	uint32_t uVar4;
	uint32_t uVar5;
	uint32_t i;
	uint32_t local_30;
	uint32_t local_2c;
	uint16_t local_28;
	short local_24;
	
	bVar1 = dictonary[(param_4 << 1) + 12];
	uVar5 = 0x7F80;
	local_24 = (short)(1 << ((uint)bVar1 & 0x1f)) + 0xFFFF;
	local_28 = dictonary[(param_4 << 1) + 13];
	local_2c = 0;
	i = 0;
	
	do 
	{
		uVar5 = uVar5 << 1;
		
		if ((uVar5 & 0xffff) == 0xFF00) 
		{
			uVar5 = FUN_8c01a480(src, &local_2c);
			uVar5 = 0xFF | (uVar5 & 0xff) << 8;
		}
		
		if (!(uVar5 & 0x8000)) 
		{
			uVar3 = FUN_8c01a480(src, &local_2c);
			uVar3 = uVar3 & 0xff;
			uVar4 = -(uint)bVar1;
			
			if (bVar1 == 0) 
			{
				uVar4 = uVar3 << (uVar4 & 0x1f);
			}
			else 
			{
				uVar4 = uVar3 >> ((~uVar4 & 0x1f) + 1);
			}
			
			uVar4 = uVar4 + local_28;
			local_30 = i - (uVar3 & (int)local_24);
			
			while ((int)local_30 < 0) 
			{
				FUN_8c01a4c0(0, dst, &i);
				uVar4--;
				local_30++;
			}
			
			while ((uVar4 & 0xffff) != 0) 
			{
				uVar3 = FUN_8c01a45a(dst, &local_30);
				FUN_8c01a4c0(uVar3, dst, &i);
				uVar4--;
			}
		}
		else 
		{
			uVar3 = FUN_8c01a45a(src, &local_2c);
			FUN_8c01a4c0(uVar3, dst, &i);
		}
	} 
	while (i < len * 2);
	
	i++;
	
	if ((int)(i + 1) < 0)
	{
		i++;
	}
	
	return i >> 1;
}

uint32_t mrg_algo3(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint32_t uVar2;
	uint32_t uVar3;
	uint32_t uVar4;
	uint32_t i;
	uint32_t local_30;
	uint32_t local_2c;
	short local_28;
	uint16_t local_24;
	
	uVar4 = 0x7F80;
	bVar1 = dictonary[(param_4 << 1) + 16];
	local_28 = (short)(1 << ((uint)bVar1 & 0x1f)) + 0xFFFF;
	local_24 = dictonary[(param_4 << 1) + 17];
	local_2c = 0;
	i = 0;
	
	do 
	{
		uVar4 = uVar4 << 1;
		
		if ((uVar4 & 0xFFFF) == 0xFF00) 
		{
			uVar4 = FUN_8c01a480(src, &local_2c);
			uVar4 = 0xFF | (uVar4 & 0xFF) << 8;
		}
		
		if (!(uVar4 & 0x8000)) 
		{
			uVar2 = FUN_8c01a45a(src, &local_2c);
			uVar3 = FUN_8c01a480(src, &local_2c);
			uVar3 = ((uVar2 & 0xFF) << 8) | (uVar3 & 0xFF);
			uVar2 = -(uint)bVar1;
			
			if (bVar1 == 0) 
			{
				uVar2 = uVar3 << (uVar2 & 0x1F);
			}
			else 
			{
				uVar2 = uVar3 >> ((~uVar2 & 0x1F) + 1);
			}
			
			uVar2 = uVar2 + (int)(short)local_24;
			local_30 = i - (uVar3 & (int)local_28);
			
			while ((int)local_30 < 0) 
			{
				FUN_8c01a4c0(0, dst, &i);
				uVar2--;
				local_30++;
			}
			
			while ((uVar2 & 0xFFFF) != 0) 
			{
				uVar3 = FUN_8c01a45a(dst, &local_30);
				FUN_8c01a4c0(uVar3, dst, &i);
				uVar2--;
			}
		}
		else 
		{
			uVar2 = FUN_8c01a45a(src, &local_2c);
			FUN_8c01a4c0(uVar2, dst, &i);
		}
	} 
	while (i < len * 2);
	
	i++;
	
	if ((int)(i + 1) < 0)
	{
		i++;
	}
	
	return i >> 1;
}

int decompress_mrg(uint8_t *dst, uint8_t *src)
{
	uint32_t encrypt_algo = (src[0] & 0x70) >> 4;

	if (!(src[0] & 0x80)) 
	{
		memset(dst, 0, BE2LE32(&src[4]));
		
		switch (encrypt_algo)
		{
			case 0:
				return (int) mrg_algo0(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
			
			case 1:
				return (int) mrg_algo1(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
			
			case 2:
				return (int) mrg_algo2(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
			
			case 3:
				return (int) mrg_algo3(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
		}
	}
	else 
	{
		memset(dst, 0, BE2LE32(src) & 0xFFFFFF);
		
		switch (encrypt_algo)
		{
			case 0:
				return (int) mrg_algo0(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
			
			case 1:
				return (int) mrg_algo1(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
			
			case 2:
				return (int) mrg_algo2(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
			
			case 3:
				return (int) mrg_algo3(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
		}
	}
	
	return -1;
}

typedef struct
{
	uint32_t offset;
	uint32_t size;
} vFILE_t;

typedef struct
{
	uint32_t f_cnt;
	vFILE_t vFile[1024];
} mrg_s;

off_t fsize(const char *filename) 
{
    struct stat st; 

    if (stat(filename, &st) == 0)
    {
        return st.st_size;
	}

    return -1; 
}

int main(int argc, char *argv[])
{
	FILE *fo, *fi;
	int i, i_sz, o_sz;
	uint8_t *in_buf, *out_buf;
	mrg_s *iMRG = NULL;
	char name[256];
	uint32_t tmp_sz;
	
	if (argc != 2)
	{
		printf("no input file\n");
		return 1;
	}
	
	if ((i_sz = fsize(argv[1])) == -1 || !(fi = fopen(argv[1], "rb")))
	{
		printf("ERROR: can't open %s\n", argv[1]);
		return 1;
	}
	
	if (!(in_buf = calloc(i_sz, 1)))
	{
		printf("ERROR: can't allocate %d bytes\n", i_sz);
		fclose(fi);
		return 1;
	}
	
	if (!(out_buf = calloc(i_sz, 1)))
	{
		printf("ERROR: can't allocate %d bytes\n", i_sz);
		fclose(fi);
		return 1;
	}
	
	fread(in_buf, 1, i_sz, fi);
	fclose(fi);
	
	iMRG = (mrg_s *) in_buf;
	
	for (i = 0; i < iMRG->f_cnt; i++)
	{
		tmp_sz = !(in_buf[iMRG->vFile[i].offset] & 0x80) ?   BE2LE32(&in_buf[iMRG->vFile[i].offset+4]) : 
															(BE2LE32(&in_buf[iMRG->vFile[i].offset]) & 0xFFFFFF);
		out_buf = realloc(out_buf, tmp_sz);
		
		if ((o_sz = decompress_mrg(out_buf, &in_buf[iMRG->vFile[i].offset])) == -1)
		{
			printf("ERROR: can't decrypt file %d of %u\n", i, iMRG->f_cnt);
			continue;
		}
		
		
		
		
		uint32_t offset = !(strncmp((char *) out_buf, "PVRX", 4)) ? 0x24 : 0;
		
		if (offset == 0x24 || (!offset && !(strncmp((char *) out_buf, "GBIX", 4))))
		{
			sprintf(name, "%s_%d.pvr", argv[1], i);
		}
		else
		{
			sprintf(name, "%s_%d.bin", argv[1], i);
		}
		
		
		if (!(fo = fopen(name, "wb")))
		{
			printf("ERROR: can't open for write %s\n", name);
			continue;
		}
		
		fwrite(out_buf + offset, 1, o_sz - offset, fo);
		fclose(fo);
	}
	
	free(in_buf);
	free(out_buf);
	
	return 0;
}
User avatar
ateam
Animated Violence
Posts: 490
Joined: Mon Dec 02, 2013 8:06 am

Re: New SEGAGAGA translation project in the works!

Post by ateam »

megavolt85 wrote:This is some kind of LZSS. I removed the pseudo -cod through the dizassembler and corrected him that he would compile.
It's always some kind of LZ implementation (or so it seems) :lol:

I'd have to analyze the code, but I wonder if it's the same or similar to the one used with Rent-A-Hero and Cool Cool Toon. Both were very, very similar, the only difference being that 2-byte symbols were byte-swapped.

EDIT: Tested and works!

Image
Find me on...

DreamcastForever.com
GitHub
Reddit
SegaXtreme
Twitter
YouTube
• Discord: derek.ateam
madsheep
rebel
Posts: 20
Joined: Sun Dec 17, 2017 3:59 pm

Re: New SEGAGAGA translation project in the works!

Post by madsheep »

ateam wrote:
megavolt85 wrote:This is some kind of LZSS. I removed the pseudo -cod through the dizassembler and corrected him that he would compile.
It's always some kind of LZ implementation (or so it seems) :lol:

I'd have to analyze the code, but I wonder if it's the same or similar to the one used with Rent-A-Hero and Cool Cool Toon. Both were very, very similar, the only difference being that 2-byte symbols were byte-swapped.

EDIT: Tested and works!

Image
So you have the tool ready for decompression/compression?
User avatar
ateam
Animated Violence
Posts: 490
Joined: Mon Dec 02, 2013 8:06 am

Re: New SEGAGAGA translation project in the works!

Post by ateam »

madsheep wrote:So you have the tool ready for decompression/compression?
All I did was compiled the C shared by megavolt85 here. As he said though, there's no compressor written yet. Here are the compiled binaries for the MRG archive extractor/decompressor.

mrg_extract.exe (Windows)
https://drive.google.com/uc?export=down ... w6lKjcKmSl

Code: Select all

.\mrg_extract.exe THE_FILE.MRG
mrg_extract (Linux)
https://drive.google.com/uc?export=down ... SToZD9bZIE

Code: Select all

./mrg_extract THE_FILE.MRG
All files from archive will be extracted and decompressed within the same working directory from which you launch the executable.

EDIT: As per new code shared by megavolt85 here, here are the newer binaries.

mrg_extract_latest.exe (Windows)
https://drive.google.com/uc?export=down ... Ea-3wYoBjX

Code: Select all

.\mrg_extract_latest.exe THE_FILE.MRG
mrg_extract_latest (Linux)
https://drive.google.com/uc?export=down ... 2BIsvCvAVA

Code: Select all

./mrg_extract_latest THE_FILE.MRG
Last edited by ateam on Tue Jan 17, 2023 9:35 am, edited 1 time in total.
Find me on...

DreamcastForever.com
GitHub
Reddit
SegaXtreme
Twitter
YouTube
• Discord: derek.ateam
madsheep
rebel
Posts: 20
Joined: Sun Dec 17, 2017 3:59 pm

Re: New SEGAGAGA translation project in the works!

Post by madsheep »

i thought you have your code from Rent-A-Hero and Cool Cool Toon
nico7550
shadow
Posts: 10
Joined: Tue Oct 20, 2020 1:00 pm

Re: New SEGAGAGA translation project in the works!

Post by nico7550 »

Hi, I'm a (old) newbie but I take a look into the files and there is a lot of them to work on, but I would like to help and I can handle some basic and repetive tasks.
  • 1. the MRG extract tool seems to have no effetc to the file located in the SOUND folder ?

    2. Using PVR Viewer (https://www.romhacking.net/utilities/1458/) to extract files, I use google lens to translate some images (but lens make a literal translation (kanji to prononciation), but within google lens when switching to google translate, it import the kanji and the translation to english works. (I try with the files in DATA\SHOOT\_SHT.MRG)

    3. Some kanji refere to a long word, example a simple symbol is translated by the word "Explosion" and it will be hard/not good looking to put the word explosion in the square used by the kanji but it's possible.

    4. I try some online tools to auto subtitle the video after extracting them (https://dreamcast-talk.com/forum/viewtopic.php?t=14382), it works but it will need:
    • someone with japanese to english knowledge to correct the auto subtitles
      someone with timing knowdledge to correct the subtitles position
      someone with video/audio compression knowledge to repack all
      some money to have full access to the functionnality... (I try veed.io)
    5. How to deal with audio song in japanese ? SOUND/AKIBAM.ADX

    6. Is there a list of tools that show what to use for each type of extension ? (MAP, MLT, MOT, VMI, TTF, NJ, EFC, ARC, MES).
The GDI I use to test:
ABBC3_SPOILER_SHOW
User avatar
ateam
Animated Violence
Posts: 490
Joined: Mon Dec 02, 2013 8:06 am

Re: New SEGAGAGA translation project in the works!

Post by ateam »

madsheep wrote:i thought you have your code from Rent-A-Hero and Cool Cool Toon
Sorry, I haven't yet taken the time to compare.
Find me on...

DreamcastForever.com
GitHub
Reddit
SegaXtreme
Twitter
YouTube
• Discord: derek.ateam
User avatar
megavolt85
Developer
Posts: 1859
Joined: Wed Jan 31, 2018 4:14 pm

Re: New SEGAGAGA translation project in the works!

Post by megavolt85 »

MRG containers contain both compressed and uncompressed files, sometimes even other MRG containers.

this code allows you to extract any MRG container.

Code: Select all

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>

uint8_t dictonary[40] =
{
	0x0C, 0x03, 0x0B, 0x03, 0x0A, 0x03, 0x09, 0x03, 
	0x06, 0x03, 0x05, 0x03, 0x06, 0x02, 0x05, 0x02,
	0x08, 0x04, 0x07, 0x04, 0x80, 0x40, 0x20, 0x10,
	0x08, 0x04, 0x02, 0x01, 0xF0, 0x0F, 0x00, 0x00,
	0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
};

uint32_t BE2LE16(uint8_t *src)
{
	return ((src[0] << 8) | src[1]);
}

uint32_t BE2LE32(uint8_t *src)
{
	return ((src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]);
}

uint32_t FUN_8c01a45a(uint8_t *buffer, uint32_t *offset)
{
	uint32_t ret;
	
	if (!(*offset & 1)) 
	{
		ret = buffer[*offset >> 1] >> 4;
	}
	else 
	{
		ret = buffer[*offset >> 1];
	}
	
	*offset = *offset + 1;
	
	return ret & 0xf;
}

uint32_t FUN_8c01a480(uint8_t *buffer, uint32_t *offset)
{
	uint32_t tmp;
	uint32_t ret;
	
	if ((*offset & 1) == 0) 
	{
		ret = buffer[*offset >> 1];
		*offset = *offset + 2;
	}
	else 
	{
		tmp = FUN_8c01a45a(buffer, offset);
		ret = FUN_8c01a45a(buffer, offset);
		ret = tmp << 4 | ret;
	}
	
	return ret;
}

void FUN_8c01a4c0(uint8_t orval, uint8_t *dst, uint32_t *offset)
{
	uint8_t val = dst[*offset >> 1];
	
	if (!(*offset & 1)) 
	{
		val = (val & 0x0F) | ((orval & 0x0F) << 4);
	}
	else 
	{
		val = (val & 0xF0) | (orval & 0xf);
	}
	
	dst[*offset >> 1] = val;
	*offset = *offset + 1;
}

uint32_t mrg_algo0(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint8_t bVar2;
	uint8_t bVar3;
	uint16_t uVar7;
	uint16_t uVar8;
	int iVar9;
	uint8_t *pbVar10;
	uint16_t control_byte;
	uint32_t i;
	
	control_byte = 0x7F80;
	bVar1 = dictonary[param_4 << 1];
	bVar3 = dictonary[(param_4 << 1) + 1];
	i = 0;
	
	do 
	{
		control_byte <<= 1;
		
		if (control_byte == 0xFF00) 
		{
			control_byte =  (*src++ << 8) | 0xFF;
		}
		
		if (!(control_byte & 0x8000)) 
		{
			uVar7 = BE2LE16(src);
			uVar8 = -bVar1;
			
			if (bVar1 == 0) 
			{
				uVar8 = uVar7 << (uVar8 & 0x1f);
			}
			else 
			{
				uVar8 = uVar7 >> ((~uVar8 & 0x1f) + 1);
			}
			
			uVar8 += bVar3;
			iVar9 = i - (uVar7 & ((1 << (bVar1 & 0x1f)) + 0xFFFF));
			src += 2;
			
			while (iVar9 < 0) 
			{
				iVar9++;
				dst[i] = 0;
				uVar8--;
				i++;
			}
			
			pbVar10 = dst + iVar9;
			
			while (uVar8) 
			{
				uVar8--;
				bVar2 = *pbVar10;
				pbVar10++;
				dst[i] = bVar2;
				i++;
			}
		}
		else 
		{
			dst[i++] = *src++;
		}
	} 
	while (i < len);
	
	return i;
}

uint32_t mrg_algo1(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint8_t bVar2;
	uint8_t bVar3;
	uint32_t uVar7;
	int n;
	uint32_t i;
	uint32_t uVar10;
	int iVar11;
	uint8_t *pbVar12;
	
	uVar10 = 0x7F80;
	bVar1 = dictonary[(param_4 << 1) + 8];
	bVar3 = dictonary[(param_4 << 1) + 9];
	n = 0;
	
	do 
	{
		uVar10 = uVar10 << 1;
		
		if ((uVar10 & 0xFFFF) == 0xFF00) 
		{
			bVar2 = *src;
			src++;
			uVar10 = 0xFF | (uint)bVar2 << 8;
		}
		
		if (!(uVar10 & 0x8000)) 
		{
			bVar2 = *src;
			iVar11 = n - ((uint)bVar2 & (int)(short)((short)(1 << ((uint)bVar1 & 0x1f)) + 0xFFFF));
			uVar7 = -(uint)bVar1;
			
			if (bVar1 == 0) 
			{
				uVar7 = (uint)bVar2 << (uVar7 & 0x1f);
			}
			else 
			{
				uVar7 = (uint)(bVar2 >> ((~uVar7 & 0x1f) + 1));
			}
			
			uVar7 = uVar7 + (int)(short)(uint16_t)bVar3;
			i = n;
			
			while (iVar11 < 0) 
			{
				iVar11++;
				dst[i] = 0;
				uVar7--;
				i++;
			}
			
			pbVar12 = dst + iVar11;
			
			while ((uVar7 & 0xffff) != 0) 
			{
				uVar7--;
				bVar2 = *pbVar12;
				pbVar12++;
				dst[i] = bVar2;
				i++;
			}
		}
		else 
		{
			i = n + 1;
			dst[n] = *src;
		}
		
		src++;
		n = i;
	} 
	while (i < len);
	
	return i;
}

uint32_t mrg_algo2(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint32_t uVar3;
	uint32_t uVar4;
	uint32_t uVar5;
	uint32_t i;
	uint32_t local_30;
	uint32_t local_2c;
	uint16_t local_28;
	short local_24;
	
	bVar1 = dictonary[(param_4 << 1) + 12];
	uVar5 = 0x7F80;
	local_24 = (short)(1 << ((uint)bVar1 & 0x1f)) + 0xFFFF;
	local_28 = dictonary[(param_4 << 1) + 13];
	local_2c = 0;
	i = 0;
	
	do 
	{
		uVar5 = uVar5 << 1;
		
		if ((uVar5 & 0xffff) == 0xFF00) 
		{
			uVar5 = FUN_8c01a480(src, &local_2c);
			uVar5 = 0xFF | (uVar5 & 0xff) << 8;
		}
		
		if (!(uVar5 & 0x8000)) 
		{
			uVar3 = FUN_8c01a480(src, &local_2c);
			uVar3 = uVar3 & 0xff;
			uVar4 = -(uint)bVar1;
			
			if (bVar1 == 0) 
			{
				uVar4 = uVar3 << (uVar4 & 0x1f);
			}
			else 
			{
				uVar4 = uVar3 >> ((~uVar4 & 0x1f) + 1);
			}
			
			uVar4 = uVar4 + local_28;
			local_30 = i - (uVar3 & (int)local_24);
			
			while ((int)local_30 < 0) 
			{
				FUN_8c01a4c0(0, dst, &i);
				uVar4--;
				local_30++;
			}
			
			while ((uVar4 & 0xffff) != 0) 
			{
				uVar3 = FUN_8c01a45a(dst, &local_30);
				FUN_8c01a4c0(uVar3, dst, &i);
				uVar4--;
			}
		}
		else 
		{
			uVar3 = FUN_8c01a45a(src, &local_2c);
			FUN_8c01a4c0(uVar3, dst, &i);
		}
	} 
	while (i < len * 2);
	
	i++;
	
	if ((int)(i + 1) < 0)
	{
		i++;
	}
	
	return i >> 1;
}

uint32_t mrg_algo3(uint8_t *dst, uint8_t *src, uint32_t len, int param_4)
{
	uint8_t bVar1;
	uint32_t uVar2;
	uint32_t uVar3;
	uint32_t uVar4;
	uint32_t i;
	uint32_t local_30;
	uint32_t local_2c;
	short local_28;
	uint16_t local_24;
	
	uVar4 = 0x7F80;
	bVar1 = dictonary[(param_4 << 1) + 16];
	local_28 = (short)(1 << ((uint)bVar1 & 0x1f)) + 0xFFFF;
	local_24 = dictonary[(param_4 << 1) + 17];
	local_2c = 0;
	i = 0;
	
	do 
	{
		uVar4 = uVar4 << 1;
		
		if ((uVar4 & 0xFFFF) == 0xFF00) 
		{
			uVar4 = FUN_8c01a480(src, &local_2c);
			uVar4 = 0xFF | (uVar4 & 0xFF) << 8;
		}
		
		if (!(uVar4 & 0x8000)) 
		{
			uVar2 = FUN_8c01a45a(src, &local_2c);
			uVar3 = FUN_8c01a480(src, &local_2c);
			uVar3 = ((uVar2 & 0xFF) << 8) | (uVar3 & 0xFF);
			uVar2 = -(uint)bVar1;
			
			if (bVar1 == 0) 
			{
				uVar2 = uVar3 << (uVar2 & 0x1F);
			}
			else 
			{
				uVar2 = uVar3 >> ((~uVar2 & 0x1F) + 1);
			}
			
			uVar2 = uVar2 + (int)(short)local_24;
			local_30 = i - (uVar3 & (int)local_28);
			
			while ((int)local_30 < 0) 
			{
				FUN_8c01a4c0(0, dst, &i);
				uVar2--;
				local_30++;
			}
			
			while ((uVar2 & 0xFFFF) != 0) 
			{
				uVar3 = FUN_8c01a45a(dst, &local_30);
				FUN_8c01a4c0(uVar3, dst, &i);
				uVar2--;
			}
		}
		else 
		{
			uVar2 = FUN_8c01a45a(src, &local_2c);
			FUN_8c01a4c0(uVar2, dst, &i);
		}
	} 
	while (i < len * 2);
	
	i++;
	
	if ((int)(i + 1) < 0)
	{
		i++;
	}
	
	return i >> 1;
}

int decompress_mrg(uint8_t *dst, uint8_t *src)
{
	uint32_t encrypt_algo = (src[0] & 0x70) >> 4;

	if (!(src[0] & 0x80)) 
	{
		memset(dst, 0, BE2LE32(&src[4]));
		
		switch (encrypt_algo)
		{
			case 0:
				return (int) mrg_algo0(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
			
			case 1:
				return (int) mrg_algo1(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
			
			case 2:
				return (int) mrg_algo2(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
			
			case 3:
				return (int) mrg_algo3(dst, &src[8], BE2LE32(&src[4]), src[0] & 0x0F);
				break;
		}
	}
	else 
	{
		memset(dst, 0, BE2LE32(src) & 0xFFFFFF);
		
		switch (encrypt_algo)
		{
			case 0:
				return (int) mrg_algo0(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
			
			case 1:
				return (int) mrg_algo1(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
			
			case 2:
				return (int) mrg_algo2(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
			
			case 3:
				return (int) mrg_algo3(dst, &src[4], BE2LE32(src) & 0xFFFFFF, src[0] & 0x0F);
				break;
		}
	}
	
	return -1;
}

typedef struct
{
	uint32_t offset;
	uint32_t size;
} vFILE_t;

typedef struct
{
	uint32_t f_cnt;
	vFILE_t vFile[1024];
} mrg_s;

off_t fsize(const char *filename) 
{
    struct stat st; 

    if (stat(filename, &st) == 0)
    {
        return st.st_size;
	}

    return -1; 
}

int main(int argc, char *argv[])
{
	FILE *fo, *fi;
	int i, i_sz, o_sz;
	uint8_t *in_buf, *out_buf;
	mrg_s *iMRG = NULL;
	char name[256];
	uint32_t tmp_sz;
	
	if (argc != 2)
	{
		printf("no input file\n");
		return 1;
	}
	
	if ((i_sz = fsize(argv[1])) == -1 || !(fi = fopen(argv[1], "rb")))
	{
		printf("ERROR: can't open %s\n", argv[1]);
		return 1;
	}
	
	if (!(in_buf = calloc(i_sz, 1)))
	{
		printf("ERROR: can't allocate %d bytes\n", i_sz);
		fclose(fi);
		return 1;
	}
	
	if (!(out_buf = calloc(i_sz, 1)))
	{
		printf("ERROR: can't allocate %d bytes\n", i_sz);
		fclose(fi);
		return 1;
	}
	
	fread(in_buf, 1, i_sz, fi);
	fclose(fi);
	
	iMRG = (mrg_s *) in_buf;
	
	for (i = 0; i < iMRG->f_cnt; i++)
	{
		tmp_sz = 0;
		uint32_t offset = 0;
		
		if((in_buf[iMRG->vFile[i].offset+1] == 'S' || in_buf[iMRG->vFile[i].offset+1] == 's') &&
		   (in_buf[iMRG->vFile[i].offset+2] == '8' || in_buf[iMRG->vFile[i].offset+2] == '4'))
		{
			tmp_sz = !(in_buf[iMRG->vFile[i].offset] & 0x80) ?   BE2LE32(&in_buf[iMRG->vFile[i].offset+4]) : 
																(BE2LE32(&in_buf[iMRG->vFile[i].offset]) & 0xFFFFFF);
			out_buf = realloc(out_buf, tmp_sz);
			
			if ((o_sz = decompress_mrg(out_buf, &in_buf[iMRG->vFile[i].offset])) == -1)
			{
				printf("ERROR: can't decrypt file %d of %u\n", i+1, iMRG->f_cnt);
				continue;
			}
			
			offset = !(strncmp((char *) out_buf, "PVRX", 4)) ? 0x24 : 0;
			
			if (offset == 0x24 || (!offset && !(strncmp((char *) out_buf, "GBIX", 4))))
			{
				sprintf(name, "%s_%d.pvr", argv[1], i);
			}
			else if (!strncmp((char *) out_buf, "NJCM", 4))
			{
				sprintf(name, "%s_%d.njcm", argv[1], i);
			}
			else
			{
				sprintf(name, "%s_%d.bin", argv[1], i);
			}
		}
		else if (!strncmp((char *) &in_buf[iMRG->vFile[i].offset+33], "CRI", 3))
		{
			sprintf(name, "%s_%d.adx", argv[1], i);
		}
		else if (!strncmp((char *) &in_buf[iMRG->vFile[i].offset], "NJTL", 4))
		{
			sprintf(name, "%s_%d.nj", argv[1], i);
		}
		else if (!strncmp((char *) &in_buf[iMRG->vFile[i].offset], "NMDM", 4))
		{
			sprintf(name, "%s_%d.nm", argv[1], i);
		}
		else if (!strncmp((char *) &in_buf[iMRG->vFile[i].offset], "SMSB", 4))
		{
			sprintf(name, "%s_%d.smsb", argv[1], i);
		}
		else if (!strncmp((char *) &in_buf[iMRG->vFile[i].offset], "SOSB", 4))
		{
			sprintf(name, "%s_%d.sosb", argv[1], i);
		}
		else
		{	
			sprintf(name, "%s_%d.mrg", argv[1], i);
		}
		
		if (!(fo = fopen(name, "wb")))
		{
			printf("ERROR: can't open for write %s\n", name);
			continue;
		}
		
		if (tmp_sz)
		{
			fwrite(out_buf + offset, 1, o_sz - offset, fo);
		}
		else
		{
			fwrite(&in_buf[iMRG->vFile[i].offset], 1, iMRG->vFile[i].size, fo);
		}
		
		fclose(fo);
	}
	
	free(in_buf);
	free(out_buf);
	
	return 0;
}
I'm afraid to lose all the work done again, so here is the executable file with the current progress.
1_SGGG.7z
(793.09 KiB) Downloaded 125 times
yzb
Developer
Posts: 130
Joined: Wed Dec 23, 2015 9:32 pm
Dreamcast Games you play Online: pso

Re: New SEGAGAGA translation project in the works!

Post by yzb »

Now that the decompression is completed, the compression can be replaced by other methods, as long as the identifier is inserted in the entry.

For example, the Chinese version is

r4 = Decompress data memory address

r5 = compressed data memory address

Code: Select all


8C019F78:
	   !In the compressed data definition, the first byte is 0xff to indicate custom compression
            mov.b   @r5, r0            
            cmp/eq  #-1, r0          
            bt      custom_decompression 
            jmp   Original_game_decompression
            nop

custom_decompression:
!You can use the often encountered prs decompression program

		add	#0x1, r5                !skip the first byte identifier
		mov.l	r8, @-r15
		mov.l	r4, @-r15
		mov     r5, r4
		mov.l	@r15, r5                  !r4 r5 swap positions
		mov.w   v_ff00, r3!  FFFFFF00
		mov.w   v_e000, r8!  FFFFE000
		mov.b	@r4+, r6
		bra	loc_002
		mov	#0x9, r7

loc_001:
		mov.b	@r4+, r1
		mov.b	r1, @r5
		add	#0x1, r5

loc_002:
		dt	r7
		bf/s	loc_003
		shlr	r6
		mov.b	@r4+, r6
		mov	#0x8, r7
		shlr	r6

loc_003:
		bt	loc_001
		dt	r7
		bf/s	loc_004
		shlr	r6
		mov.b	@r4+, r6
		mov	#0x8, r7
		shlr	r6

loc_004:
		bt	loc_009
		mov	#0x0, r0
		dt	r7
		bf/s	loc_005
		shlr	r6
		mov.b	@r4+, r6
		mov	#0x8, r7
		shlr	r6

loc_005:
		rotcl	r0
		dt	r7
		bf/s	loc_006
		shlr	r6
		mov.b	@r4+, r6
		mov	#0x8, r7
		shlr	r6

loc_006:
		mov.b	@r4+, r2
		rotcl	r0
		or	r3, r2

loc_007:
		add	#0x2, r0
		add	r5, r2

loc_008:
		mov.b	@r2+, r1
		dt	r0
		mov.b	r1, @r5
		bf/s	loc_008
		add	#0x1, r5
		bra	loc_002
		nop


loc_009:
		mov.b	@r4+, r0
		mov.b	@r4+, r1
		extu.b	r0, r2
		shll8	r1
		or	r1, r2
		tst	r2, r2
		bt	loc_end
		shlr2	r2
		shlr	r2
		and	#0x7, r0
		tst	r0, r0
		bf/s	loc_007
		or	r8, r2
		mov.b	@r4+, r0
		add	r5, r2
		extu.b	r0, r0
		bra	loc_008
		add	#0x1, r0

loc_end:				
		mov.l	@r15+, r1
		mov.l	@r15+, r8
		sub     r1, r5
		rts
		mov		r5, r0               !      Need to return the decompressed file size r0
		
		
v_ff00:
	.short	0xff00
v_e000:
	.short	0xe000

madsheep
rebel
Posts: 20
Joined: Sun Dec 17, 2017 3:59 pm

Re: New SEGAGAGA translation project in the works!

Post by madsheep »

megavolt85 wrote:current progress:
+ used half wide font 12x24
+ supported only ASCII charset
+ translated few strings in main binary
- text pocessor have limit, 32 character per line, need fix it
any news on 32 char limit?
Post Reply