refactor decathlete protection a bit, hook up 'doa' to the table uploads for it too (nw)

This commit is contained in:
mamehaze 2015-01-28 01:48:37 +00:00
parent 8b43aaea32
commit 948ee3f714
2 changed files with 228 additions and 250 deletions

View File

@ -10,7 +10,8 @@
dumb security check, decompressing a single string.
Each channel appears to be connected to a different set of ROMs, however there is
defintiely only 315-5838 single chip.
defintiely only a single 315-5838 chip. (could the different channels actually just
be mirror addresses, with part of the address determining the ROMs to use?)
Dead of Alive only uses a single channel, and has the source data in RAM, not ROM.
This is similar to how some 5881 games were set up, with the ST-V versions decrypting
@ -18,7 +19,7 @@
Looking at the values read I don't think there is any address based encryption, for
example many blocks where you'd expect a zero fill start with repeating patterns
of 8f708f70 (different lengths) which would appear to relate to compressed 0x00 data
of 8f708f70 (different lengths) channel would appear to relate to compressed 0x00 data
read addr 0071253c, blah_r 8f708f70 - read count count 00000004
read addr 00712540, blah_r 8f708f70 - read count count 00000008
@ -46,21 +47,25 @@ sega_315_5838_comp_device::sega_315_5838_comp_device(const machine_config &mconf
void sega_315_5838_comp_device::device_start()
{
m_decathlt_lastcount = 0;
m_decathlt_prot_uploadmode = 0;
m_decathlt_prot_uploadoffset = 0;
for (int i = 0; i < 2; i++)
{
m_channel[i].m_decathlt_lastcount = 0;
m_channel[i].m_decathlt_prot_uploadmode = 0;
m_channel[i].m_decathlt_prot_uploadoffset = 0;
m_channel[i].m_read_ch.bind_relative_to(*owner());
m_read_ch1.bind_relative_to(*owner());
m_read_ch2.bind_relative_to(*owner());
}
}
void sega_315_5838_comp_device::device_reset()
{
memset(m_decathlt_protregs, 0, sizeof(m_decathlt_protregs));
m_decathlt_lastcount = 0;
m_decathlt_prot_uploadmode = 0;
m_decathlt_prot_uploadoffset = 0;
m_decathlt_part = 1;
for (int i = 0; i < 2; i++)
{
m_channel[i].m_srcoffset = 0;
m_channel[i].m_decathlt_lastcount = 0;
m_channel[i].m_decathlt_prot_uploadmode = 0;
m_channel[i].m_decathlt_prot_uploadoffset = 0;
}
m_protstate = 0;
}
@ -77,281 +82,235 @@ FILE* tempfile;
#endif
READ32_MEMBER(sega_315_5838_comp_device::decathlt_prot_r)
READ32_MEMBER(sega_315_5838_comp_device::decathlt_prot1_r)
{
return genericdecathlt_prot_r(offset, mem_mask, 0);
return genericdecathlt_prot_r(mem_mask, 0);
}
READ32_MEMBER(sega_315_5838_comp_device::decathlt_prot_ch2_r)
READ32_MEMBER(sega_315_5838_comp_device::decathlt_prot2_r)
{
return genericdecathlt_prot_r(offset, mem_mask, 1);
return genericdecathlt_prot_r(mem_mask, 1);
}
UINT32 sega_315_5838_comp_device::genericdecathlt_prot_r(UINT32 offset, UINT32 mem_mask, int which)
UINT32 sega_315_5838_comp_device::genericdecathlt_prot_r(UINT32 mem_mask, int channel)
{
// UINT32 *fake0 = (UINT32*)memregion( ":fake0" )->base();
// UINT32 retvalue = 0xffff;
if (offset==2)
switch (m_channel[channel].m_srcoffset)
{
// UINT32 retvalue = 0xffff;
default:
switch (m_decathlt_protregs[0])
{
default:
m_channel[channel].m_decathlt_lastcount++;
m_decathlt_lastcount++;
UINT32 tempdata = 0;
tempdata |= m_channel[channel].m_read_ch(m_channel[channel].m_srcoffset) << 0;
m_channel[channel].m_srcoffset++;
tempdata |= m_channel[channel].m_read_ch(m_channel[channel].m_srcoffset) << 16;
m_channel[channel].m_srcoffset++;
UINT32 tempdata = 0;
if (which == 0)
{
tempdata |= m_read_ch1(m_decathlt_protregs[0]) << 16;
m_decathlt_protregs[0]++;
tempdata |= m_read_ch1(m_decathlt_protregs[0]) << 0;
m_decathlt_protregs[0]++;
}
else
{
tempdata |= m_read_ch2(m_decathlt_protregs[0]) << 16;
m_decathlt_protregs[0]++;
tempdata |= m_read_ch2(m_decathlt_protregs[0]) << 0;
m_decathlt_protregs[0]++;
}
#ifdef DEBUG_DATA_DUMP
//printf("read addr %08x, blah_r %08x - read count count %08x\n", m_channel[channel].m_srcoffset*2, tempdata, m_channel[channel].m_decathlt_lastcount*4);
fwrite(&tempdata, 1, 4, tempfile);
#else
logerror("read addr %08x, blah_r %08x - read count count %08x\n", m_channel[channel].m_srcoffset*2, tempdata, m_channel[channel].m_decathlt_lastcount*4);
#endif
#ifdef DEBUG_DATA_DUMP
//printf("read addr %08x, blah_r %08x - read count count %08x\n", m_decathlt_protregs[0]*2, tempdata, m_decathlt_lastcount*4);
fwrite(&tempdata, 1, 4, tempfile);
#else
logerror("read addr %08x, blah_r %08x - read count count %08x\n", m_decathlt_protregs[0]*2, tempdata, m_decathlt_lastcount*4);
#endif
return tempdata;
return tempdata;
#if 0
case 0x03228e4:
if (fake0) retvalue = fake0[(((0x20080/4)+m_decathlt_lastcount))];
m_decathlt_lastcount++;
return retvalue;
case 0x03228e4:
if (fake0) retvalue = fake0[(((0x20080/4)+m_channel[channel].m_decathlt_lastcount))];
m_channel[channel].m_decathlt_lastcount++;
return retvalue;
case 0x00a9f3a:
if (fake0) retvalue = fake0[(((0x00000/4)+m_decathlt_lastcount))];
m_decathlt_lastcount++;
return retvalue;
case 0x00a9f3a:
if (fake0) retvalue = fake0[(((0x00000/4)+m_channel[channel].m_decathlt_lastcount))];
m_channel[channel].m_decathlt_lastcount++;
return retvalue;
case 0x0213ab4:
if (fake0) retvalue = fake0[(((0x40000/4)+m_decathlt_lastcount))];
m_decathlt_lastcount++;
return retvalue;
case 0x0213ab4:
if (fake0) retvalue = fake0[(((0x40000/4)+m_channel[channel].m_decathlt_lastcount))];
m_channel[channel].m_decathlt_lastcount++;
return retvalue;
case 0x01efaf0:
if (fake0) retvalue = fake0[(((0x60000/4)+m_decathlt_lastcount))];
m_decathlt_lastcount++;
return retvalue;
case 0x01efaf0:
if (fake0) retvalue = fake0[(((0x60000/4)+m_channel[channel].m_decathlt_lastcount))];
m_channel[channel].m_decathlt_lastcount++;
return retvalue;
case 0x033f16c:
case 0x038929c:
case 0x033f16c:
case 0x038929c:
case 0x00de05a:
case 0x0334258:
case 0x019fb82:
case 0x033dbf6:
case 0x0011ac6:
case 0x00060dc:
case 0x0000002:
case 0x0008c90:
case 0x035cdc8:
case 0x0327960:
case 0x0329b8c:
case 0x00d6e92:
case 0x000081e:
case 0x00035d6:
case 0x00089a6:
case 0x03315f4:
case 0x0023fe0:
case 0x001e290:
case 0x0026e86:
case 0x0012494:
case 0x001b35a:
case 0x0018424:
case 0x00de05a:
case 0x0334258:
case 0x019fb82:
case 0x033dbf6:
case 0x0011ac6:
case 0x00060dc:
case 0x0000002:
case 0x0008c90:
case 0x035cdc8:
case 0x0327960:
case 0x0329b8c:
case 0x00d6e92:
case 0x000081e:
case 0x00035d6:
case 0x00089a6:
case 0x03315f4:
case 0x0023fe0:
case 0x001e290:
case 0x0026e86:
case 0x0012494:
case 0x001b35a:
case 0x0018424:
return retvalue;
return retvalue;
#endif
}
return 0xffffffff;
}
void sega_315_5838_comp_device::set_prot_addr(UINT32 data, UINT32 mem_mask, int channel)
{
// printf("set_prot_addr\n");
COMBINE_DATA(&m_channel[channel].m_srcoffset);
//if (m_decathlt_part==0) logerror("%d, last read count was %06x\n",channel, m_channel[channel].m_decathlt_lastcount*4);
m_channel[channel].m_decathlt_lastcount = 0;
if (mem_mask == 0x0000ffff)
{
printf("set source address to %08x (channel %d)\n", m_channel[channel].m_srcoffset, channel);
}
#ifdef DEBUG_DATA_DUMP
if (mem_mask == 0x0000ffff)
{
if (tempfile)
fclose(tempfile);
char filename[256];
sprintf(filename, "%d_compressed_%08x", channel, m_channel[channel].m_srcoffset * 2);
tempfile = fopen(filename, "w+b");
// the table and dictionary are uploaded repeatedly, usually before groups of data transfers but
// it's always the same tables (one pair for each channel)
{
FILE* fp;
sprintf(filename, "%d_compressed_table1", channel);
fp = fopen(filename, "w+b");
fwrite(&m_channel[channel].m_decathlt_prottable1, 24, 2, fp);
fclose(fp);
}
{
FILE* fp;
sprintf(filename, "%d_compressed_dictionary", channel);
fp = fopen(filename, "w+b");
fwrite(&m_channel[channel].m_decathlt_dictionary, 128, 2, fp);
fclose(fp);
}
}
#endif
}
void sega_315_5838_comp_device::set_upload_mode(UINT16 data, int channel)
{
if ((data == 0x8000) || (data == 0x0000))
{
// logerror("changed to upload mode 1\n");
m_channel[channel].m_decathlt_prot_uploadmode = 1;
m_channel[channel].m_decathlt_prot_uploadoffset = 0;
}
else if ((data == 0x8080) || (data == 0x0080))
{
m_channel[channel].m_decathlt_prot_uploadmode = 2;
m_channel[channel].m_decathlt_prot_uploadoffset = 0;
}
else
{
logerror("%06x Decathlete prot R offset %04x mask %08x regs %08x, %08x, %08x, %08x\n", safe_pc(), offset, mem_mask, m_decathlt_protregs[0], m_decathlt_protregs[1], m_decathlt_protregs[2], m_decathlt_protregs[3]);
fatalerror("unknown upload mode\n");
}
return m_decathlt_protregs[offset];
}
void sega_315_5838_comp_device::write_prot_data(UINT32 data, UINT32 mem_mask, int offset, int which)
void sega_315_5838_comp_device::upload_table_data(UINT16 data, int channel)
{
printf("write_prot_data %08x %08x %08x\n", offset, data, mem_mask);
m_decathlt_protregs[offset] = (data&mem_mask)|(m_decathlt_protregs[offset]&~mem_mask);
// m_decathlt_protregs[0] = 0x0c00000/4;
if (offset==0) // seems to set a source address
if (m_channel[channel].m_decathlt_prot_uploadmode == 1)
{
m_decathlt_part ^=1;
//if (m_decathlt_part==0) logerror("%d, last read count was %06x\n",which, m_decathlt_lastcount*4);
m_decathlt_lastcount = 0;
if (m_decathlt_part==1) logerror("%d Decathlete prot W offset %04x data %08x, %08x, >>> regs %08x <<<<, %08x, %08x, %08x\n",which, offset, data, m_decathlt_protregs[0], m_decathlt_protregs[0]*4, m_decathlt_protregs[1], m_decathlt_protregs[2], m_decathlt_protregs[3]);
#ifdef DEBUG_DATA_DUMP
if (mem_mask == 0x0000ffff)
if (m_channel[channel].m_decathlt_prot_uploadoffset >= 24)
{
if (tempfile)
fclose(tempfile);
char filename[256];
sprintf(filename, "%d_compressed_%08x", which, m_decathlt_protregs[0] );
tempfile = fopen(filename, "w+b");
fatalerror("upload mode 1 error, too big\n");
return;
}
#endif
//logerror("uploading table 1 %04x %04x\n",m_channel[channel].m_decathlt_prot_uploadoffset, data&0xffff);
m_channel[channel].m_decathlt_prottable1[m_channel[channel].m_decathlt_prot_uploadoffset] = data & 0xffff;
m_channel[channel].m_decathlt_prot_uploadoffset++;
printf("unk table 1 %04x (channel %d)\n", data & 0xffff, channel);
}
if (offset==1) // uploads 2 tables...
else if (m_channel[channel].m_decathlt_prot_uploadmode == 2)
{
if (mem_mask==0xffff0000)
if (m_channel[channel].m_decathlt_prot_uploadoffset >= 128)
{
if (data == 0x80000000)
{
// logerror("changed to upload mode 1\n");
m_decathlt_prot_uploadmode = 1;
m_decathlt_prot_uploadoffset = 0;
}
else if (data == 0x80800000)
{
// logerror("changed to upload mode 2\n");
m_decathlt_prot_uploadmode = 2;
m_decathlt_prot_uploadoffset = 0;
}
else
{
// logerror("unknown upload mode\n");
m_decathlt_prot_uploadmode = 2;
m_decathlt_prot_uploadoffset = 0;
}
// logerror("ARGH! %08x %08x\n",mem_mask,data);
fatalerror("upload mode 2 error, too big\n");
return;
}
else if (mem_mask==0x0000ffff)
{
if (m_decathlt_prot_uploadmode==1)
{
if (m_decathlt_prot_uploadoffset>=24)
{
// logerror("upload mode 1 error, too big\n");
return;
}
//logerror("uploading table 1 %04x %04x\n",m_decathlt_prot_uploadoffset, data&0xffff);
m_decathlt_prottable1[m_decathlt_prot_uploadoffset]=data&0xffff;
m_decathlt_prot_uploadoffset++;
printf("table 1 %04x\n", data & 0xffff);
{
/* 0x18 (24) values in this table, rom data is 0x1800000 long, maybe it has
something to do with that? or 24-address b its?
uploaded values appear to be 12-bit, some are repeated
*/
{
FILE* fp;
if (which==1) fp = fopen("table1x","wb");
else fp = fopen("table1","wb");
{
fwrite(&m_decathlt_prottable1,24,2,fp);
}
fclose(fp);
}
}
}
else if (m_decathlt_prot_uploadmode==2)
{
if (m_decathlt_prot_uploadoffset>=128)
{
//logerror("upload mode 2 error, too big\n");
return;
}
//logerror("uploading table 2 %04x %04x\n",m_decathlt_prot_uploadoffset, data&0xffff);
m_decathlt_prottable2[m_decathlt_prot_uploadoffset]=data&0xffff;
m_decathlt_prot_uploadoffset++;
printf("dictionary %04x\n", data & 0xffff);
{
/* the table uploaded here is a 256 byte table with 256 unique values, remaps something? */
{
FILE* fp;
if (which==1) fp = fopen("table2x","wb");
else fp = fopen("table2","wb");
{
fwrite(&m_decathlt_prottable2,128,2,fp);
}
fclose(fp);
}
}
}
else
{
// logerror("unknown upload mode!\n");
}
}
//logerror("uploading table 2 %04x %04x\n",m_channel[channel].m_decathlt_prot_uploadoffset, data&0xffff);
m_channel[channel].m_decathlt_dictionary[m_channel[channel].m_decathlt_prot_uploadoffset] = data & 0xffff;
m_channel[channel].m_decathlt_prot_uploadoffset++;
printf("dictionary %04x (channel %d)\n", data & 0xffff, channel);
}
if (offset>1)
{
// logerror("higher offset write\n");
}
}
WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot1_w )
void sega_315_5838_comp_device::write_prot_data(UINT32 data, UINT32 mem_mask, int channel, int rev_words)
{
write_prot_data(data,mem_mask, offset, 0);
if (mem_mask==0xffff0000)
{
if (rev_words==0) set_upload_mode(data >> 16, channel);
else upload_table_data(data >>16, channel);
}
else if (mem_mask == 0x0000ffff)
{
if (rev_words==0) upload_table_data(data & 0xffff, channel);
else set_upload_mode(data & 0xffff, channel);
}
else
{
fatalerror("write_prot_data invalid mem_mask\b");
}
}
WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot2_w )
{
write_prot_data(data,mem_mask, offset, 1);
}
WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot1_w_doa ) { write_prot_data(data, mem_mask, 0, 1); }
WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot1_w) { write_prot_data(data, mem_mask, 0, 0); }
WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot2_w) { write_prot_data(data, mem_mask, 1, 0); }
WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot1_srcaddr_w ) { set_prot_addr(data, mem_mask, 0); }
WRITE32_MEMBER( sega_315_5838_comp_device::decathlt_prot2_srcaddr_w) { set_prot_addr(data, mem_mask, 1); }
void sega_315_5838_comp_device::install_decathlt_protection()
{
/* It uploads 2 tables here, then performs what looks like a number of transfers, setting
a source address of some kind (scrambled?) and then making many reads from a single address */
//todo, install these in the driver, they differ between games
cpu_device* cpu = (cpu_device*)machine().device(":maincpu");
cpu->space(AS_PROGRAM).install_write_handler(0x37FFFF0, 0x37FFFF3, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_srcaddr_w), this)); // set compressed data source address
cpu->space(AS_PROGRAM).install_write_handler(0x37FFFF4, 0x37FFFF7, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_w), this)); // upload tables
cpu->space(AS_PROGRAM).install_read_handler(0x37FFFF8, 0x37FFFFb, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_r), this)); // read decompressed data
cpu->space(AS_PROGRAM).install_readwrite_handler(0x37FFFF0, 0x37FFFFF, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot_r), this), write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_w), this));
/* It accesses the device at this address too, with different tables, for the game textures, should it just act like a mirror, or a secondary device? */
cpu->space(AS_PROGRAM).install_readwrite_handler(0x27FFFF0, 0x27FFFFF, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot_ch2_r), this), write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot2_w), this));
// the device is addressed here too, uploading a different set of tables and accessing a different part of ROM
cpu->space(AS_PROGRAM).install_write_handler(0x27FFFF0, 0x27FFFF3, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot2_srcaddr_w), this)); // set compressed data source address
cpu->space(AS_PROGRAM).install_write_handler(0x27FFFF4, 0x27FFFF7, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot2_w), this)); // upload tables
cpu->space(AS_PROGRAM).install_read_handler(0x27FFFF8, 0x27FFFFb, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot2_r), this)); // read decompressed data
}
@ -387,15 +346,7 @@ WRITE32_MEMBER(sega_315_5838_comp_device::doa_prot_w)
{
printf("doa_prot_w %08x %08x %08x\n", offset*4, data, mem_mask);
if (offset == 0x7ff2 / 4)
{
if (data == 0)
{
m_protstate = 0;
strcpy((char *)m_protram, " TECMO LTD. DEAD OR ALIVE 1996.10.22 VER. 1.00"); // this is the single decompressed string DOA needs
}
}
else logerror("Unhandled Protection WRITE %x @ %x mask %x (PC=%x)\n", data, offset, mem_mask, space.device().safe_pc());
m_protstate = 0;
}
@ -403,5 +354,13 @@ void sega_315_5838_comp_device::install_doa_protection()
{
//todo, install these in the driver, they differ between games
cpu_device* cpu = (cpu_device*)machine().device(":maincpu");
m_protstate = 0;
strcpy((char *)m_protram, " TECMO LTD. DEAD OR ALIVE 1996.10.22 VER. 1.00"); // this is the single decompressed string DOA needs, note, 2 spaces at start, might indicate a dummy read like with 5881 on Model 2
cpu->space(AS_PROGRAM).install_readwrite_handler(0x01d80000, 0x01dfffff, read32_delegate(FUNC(sega_315_5838_comp_device::doa_prot_r), this), write32_delegate(FUNC(sega_315_5838_comp_device::doa_prot_w), this));
cpu->space(AS_PROGRAM).install_write_handler(0x01d87ff0, 0x01d87ff3, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_srcaddr_w), this)); // set compressed data source address (always set 0, data is in RAM)
cpu->space(AS_PROGRAM).install_write_handler(0x01d87ff4, 0x01d87ff7, write32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_w_doa), this)); // upload tab
// cpu->space(AS_PROGRAM).install_read_handler(0x01d87ff8, 0x01d87ffb, read32_delegate(FUNC(sega_315_5838_comp_device::decathlt_prot1_r), this)); // read decompressed data
}

View File

@ -4,6 +4,8 @@
#ifndef __SEGA315_5838_COMP__
#define __SEGA315_5838_COMP__
#define CHANNELS 2
typedef device_delegate<UINT16 (UINT32)> sega_dec_read_delegate;
extern const device_type SEGA315_5838_COMP;
@ -20,28 +22,36 @@ public:
// construction/destruction
sega_315_5838_comp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
sega_dec_read_delegate m_read_ch1;
sega_dec_read_delegate m_read_ch2;
static void set_read_cb_ch1(device_t &device,sega_dec_read_delegate readcb)
{
sega_315_5838_comp_device &dev = downcast<sega_315_5838_comp_device &>(device);
dev.m_read_ch1 = readcb;
dev.m_channel[0].m_read_ch = readcb;
}
static void set_read_cb_ch2(device_t &device,sega_dec_read_delegate readcb)
{
sega_315_5838_comp_device &dev = downcast<sega_315_5838_comp_device &>(device);
dev.m_read_ch2 = readcb;
dev.m_channel[1].m_read_ch = readcb;
}
DECLARE_READ32_MEMBER(decathlt_prot_r);
DECLARE_READ32_MEMBER(decathlt_prot_ch2_r);;
UINT32 genericdecathlt_prot_r(UINT32 offset, UINT32 mem_mask, int which);
DECLARE_READ32_MEMBER(decathlt_prot1_r);
DECLARE_READ32_MEMBER(decathlt_prot2_r);;
UINT32 genericdecathlt_prot_r(UINT32 mem_mask, int channel);
void write_prot_data(UINT32 data, UINT32 mem_mask, int offset, int which);
void write_prot_data(UINT32 data, UINT32 mem_mask, int channel, int rev_words);
void upload_table_data(UINT16 data, int channel);
void set_upload_mode(UINT16 data, int channel);
void set_prot_addr(UINT32 data, UINT32 mem_mask, int channel);
DECLARE_WRITE32_MEMBER(decathlt_prot1_w_doa);
DECLARE_WRITE32_MEMBER(decathlt_prot1_w);
DECLARE_WRITE32_MEMBER(decathlt_prot2_w);
DECLARE_WRITE32_MEMBER(decathlt_prot1_srcaddr_w);
DECLARE_WRITE32_MEMBER(decathlt_prot2_srcaddr_w);
void install_decathlt_protection();
void install_doa_protection();
@ -55,13 +65,22 @@ protected:
private:
// Decathlete specific variables and functions (see machine/decathlt.c)
UINT32 m_decathlt_protregs[4];
UINT32 m_decathlt_lastcount;
UINT32 m_decathlt_part;
UINT32 m_decathlt_prot_uploadmode;
UINT32 m_decathlt_prot_uploadoffset;
UINT16 m_decathlt_prottable1[24];
UINT16 m_decathlt_prottable2[128];
struct channel_type
{
UINT32 m_srcoffset;
UINT16 m_decathlt_prottable1[24];
UINT16 m_decathlt_dictionary[128];
UINT32 m_decathlt_lastcount;
UINT32 m_decathlt_prot_uploadmode;
UINT32 m_decathlt_prot_uploadoffset;
sega_dec_read_delegate m_read_ch;
};
channel_type m_channel[2];
// Doa
int m_protstate;