#include "types.h" #include "maple_if.h" #include "maple_helper.h" #include "maple_devs.h" #include "maple_cfg.h" #include #if _ANDROID #include #include #else #define LOGW printf #define LOGI printf #endif #include "deps/zlib/zlib.h" const char* maple_sega_controller_name = "Dreamcast Controller"; const char* maple_sega_vmu_name = "Visual Memory"; const char* maple_sega_kbd_name = "Emulated Dreamcast Keyboard"; const char* maple_sega_mouse_name = "Emulated Dreamcast Mouse"; const char* maple_sega_dreameye_name_1 = "Dreamcast Camera Flash Devic"; const char* maple_sega_dreameye_name_2 = "Dreamcast Camera Flash LDevic"; const char* maple_sega_mic_name = "MicDevice for Dreameye"; const char* maple_sega_brand = "Produced By or Under License From SEGA ENTERPRISES,LTD."; #define HAS_VMU enum MapleFunctionID { MFID_0_Input = 0x01000000, //DC Controller, Lightgun buttons, arcade stick .. stuff like that MFID_1_Storage = 0x02000000, //VMU , VMS MFID_2_LCD = 0x04000000, //VMU MFID_3_Clock = 0x08000000, //VMU MFID_4_Mic = 0x10000000, //DC Mic (, dreameye too ?) MFID_5_ARGun = 0x20000000, //Artificial Retina gun ? seems like this one was never developed or smth -- I only remember of lightguns MFID_6_Keyboard = 0x40000000, //DC Keyboard MFID_7_LightGun = 0x80000000, //DC Lightgun MFID_8_Vibration = 0x00010000, //Puru Puru Puur~~~ MFID_9_Mouse = 0x00020000, //DC Mouse MFID_10_StorageExt = 0x00040000, //Storage ? probably never used MFID_11_Camera = 0x00080000, //DreamEye }; enum MapleDeviceCommand { MDC_DeviceRequest = 0x01, //7 words.Note : Initialises device MDC_AllStatusReq = 0x02, //7 words + device dependent ( seems to be 8 words) MDC_DeviceReset = 0x03, //0 words MDC_DeviceKill = 0x04, //0 words MDC_DeviceStatus = 0x05, //Same as MDC_DeviceRequest ? MDC_DeviceAllStatus = 0x06, //Same as MDC_AllStatusReq ? //Various Functions MDCF_GetCondition = 0x09, //FT MDCF_GetMediaInfo = 0x0A, //FT,PT,3 pad MDCF_BlockRead = 0x0B, //FT,PT,Phase,Block # MDCF_BlockWrite = 0x0C, //FT,PT,Phase,Block #,data ... MDCF_GetLastError = 0x0D, //FT,PT,Phase,Block # MDCF_SetCondition = 0x0E, //FT,data ... MDCF_MICControl = 0x0F, //FT,MIC data ... MDCF_ARGunControl = 0x10, //FT,AR-Gun data ... }; enum MapleDeviceRV { MDRS_DeviceStatus = 0x05, //28 words MDRS_DeviceStatusAll = 0x06, //28 words + device dependent data MDRS_DeviceReply = 0x07, //0 words MDRS_DataTransfer = 0x08, //FT,depends on the command MDRE_UnknownFunction = 0xFE, //0 words MDRE_UnknownCmd = 0xFD, //0 words MDRE_TransminAgain = 0xFC, //0 words MDRE_FileError = 0xFB, //1 word, bitfield MDRE_LCDError = 0xFA, //1 word, bitfield MDRE_ARGunError = 0xF9, //1 word, bitfield }; #define SWAP32(a) ((((a) & 0xff) << 24) | (((a) & 0xff00) << 8) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff)) //fill in the info void maple_device::Setup(u32 prt) { maple_port = prt; bus_port = maple_GetPort(prt); bus_id = maple_GetBusId(prt); logical_port[0] = 'A' + bus_id; logical_port[1] = bus_port == 5 ? 'x' : '1' + bus_port; logical_port[2] = 0; } maple_device::~maple_device() { if (config) delete config; } /* Base class with dma helpers and stuff */ struct maple_base: maple_device { u8* dma_buffer_out; u32* dma_count_out; u8* dma_buffer_in; u32 dma_count_in; void w8(u8 data) { *((u8*)dma_buffer_out)=data;dma_buffer_out+=1;dma_count_out[0]+=1; } void w16(u16 data) { *((u16*)dma_buffer_out)=data;dma_buffer_out+=2;dma_count_out[0]+=2; } void w32(u32 data) { *(u32*)dma_buffer_out=data;dma_buffer_out+=4;dma_count_out[0]+=4; } void wptr(const void* src,u32 len) { u8* src8=(u8*)src; while(len--) w8(*src8++); } void wstr(const char* str,u32 len) { size_t ln=strlen(str); verify(len>=ln); len-=ln; while(ln--) w8(*str++); while(len--) w8(0x20); } u8 r8() { u8 rv=*((u8*)dma_buffer_in);dma_buffer_in+=1;dma_count_in-=1; return rv; } u16 r16() { u16 rv=*((u16*)dma_buffer_in);dma_buffer_in+=2;dma_count_in-=2; return rv; } u32 r32() { u32 rv=*(u32*)dma_buffer_in;dma_buffer_in+=4;dma_count_in-=4; return rv; } void rptr(const void* dst,u32 len) { u8* dst8=(u8*)dst; while(len--) *dst8++=r8(); } u32 r_count() { return dma_count_in; } virtual u32 Dma(u32 Command,u32* buffer_in,u32 buffer_in_len,u32* buffer_out,u32& buffer_out_len) { dma_buffer_out=(u8*)buffer_out; dma_count_out=&buffer_out_len; dma_buffer_in=(u8*)buffer_in; dma_count_in=buffer_in_len; return dma(Command); } virtual u32 dma(u32 cmd)=0; }; /* Sega Dreamcast Controller No error checking of any kind, but works just fine */ struct maple_sega_controller: maple_base { virtual u32 dma(u32 cmd) { //printf("maple_sega_controller::dma Called 0x%X;Command %d\n",device_instance->port,Command); switch (cmd) { case MDC_DeviceRequest: //caps //4 w32(MFID_0_Input); //struct data //3*4 w32( 0xfe060f00); w32( 0); w32( 0); //1 area code w8(0xFF); //1 direction w8(0); //30 wstr(maple_sega_controller_name,30); //60 wstr(maple_sega_brand,60); //2 w16(0x01AE); //2 w16(0x01F4); return MDRS_DeviceStatus; //controller condition case MDCF_GetCondition: { PlainJoystickState pjs; config->GetInput(&pjs); //caps //4 w32(MFID_0_Input); //state data //2 key code w16(pjs.kcode); //triggers //1 R w8(pjs.trigger[PJTI_R]); //1 L w8(pjs.trigger[PJTI_L]); //joyx //1 w8(pjs.joy[PJAI_X1]); //joyy //1 w8(pjs.joy[PJAI_Y1]); //not used //1 w8(0x80); //1 w8(0x80); } return MDRS_DataTransfer; default: //printf("UNKOWN MAPLE COMMAND %d\n",cmd); return MDRE_UnknownFunction; } } }; /* Sega Dreamcast Visual Memory Unit This is pretty much done (?) */ #ifdef HAS_VMU u8 vmu_default[] = { 0x78,0x9c,0xed,0xd2,0x31,0x4e,0x02,0x61,0x10,0x06,0xd0,0x8f,0x04,0x28,0x4c,0x2c, 0x28,0x2d,0x0c,0xa5,0x57,0xe0,0x16,0x56,0x16,0x76,0x14,0x1e,0xc4,0x03,0x50,0x98, 0x50,0x40,0x69,0xc1,0x51,0x28,0xbc,0x8e,0x8a,0x0a,0xeb,0xc2,0xcf,0x66,0x13,0x1a, 0x13,0xa9,0x30,0x24,0xe6,0xbd,0xc9,0x57,0xcc,0x4c,0x33,0xc5,0x2c,0xb3,0x48,0x6e, 0x67,0x01,0x00,0x00,0x00,0x00,0x00,0x4e,0xaf,0xdb,0xe4,0x7a,0xd2,0xcf,0x53,0x16, 0x6d,0x46,0x99,0xb6,0xc9,0x78,0x9e,0x3c,0x5f,0x9c,0xfb,0x3c,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x80,0x5f,0xd5,0x45,0xfd,0xef,0xaa,0xca,0x6b,0xde,0xf2,0x9e,0x55, 0x3e,0xf2,0x99,0xaf,0xac,0xb3,0x49,0x95,0xef,0xd4,0xa9,0x9a,0xdd,0xdd,0x0f,0x9d, 0x52,0xca,0xc3,0x91,0x7f,0xb9,0x9a,0x0f,0x6e,0x92,0xfb,0xee,0xa1,0x2f,0x6d,0x76, 0xe9,0x64,0x9b,0xcb,0xf4,0xf2,0x92,0x61,0x33,0x79,0xfc,0xeb,0xb7,0xe5,0x44,0xf6, 0x77,0x19,0x06,0xef, }; struct maple_sega_vmu: maple_base { FILE* file; u8 flash_data[128*1024]; u8 lcd_data[192]; u8 lcd_data_decoded[48*32]; virtual void OnSetup() { memset(flash_data,0,sizeof(flash_data)); memset(lcd_data,0,sizeof(lcd_data)); wchar tempy[512]; sprintf(tempy,"/vmu_save_%s.bin",logical_port); string apath=GetPath(tempy); file=fopen(apath.c_str(),"rb+"); if (!file) { printf("Unable to open VMU save file \"%s\", creating new file\n",apath.c_str()); file=fopen(apath.c_str(),"wb"); if (file) { fwrite(flash_data, sizeof(flash_data), 1, file); fseek(file,0,SEEK_SET); } else { printf("Unable to create vmu\n"); } } if (!file) { printf("Failed to create VMU save file \"%s\"\n",apath.c_str()); } else { fread(flash_data,1,sizeof(flash_data),file); } u8 sum = 0; for (int i=0;iport,Command); switch (cmd) { case MDC_DeviceRequest: { //caps //4 w32(MFID_1_Storage | MFID_2_LCD | MFID_3_Clock); //struct data //3*4 w32( 0x403f7e7e); // for clock w32( 0x00100500); // for LCD w32( 0x00410f00); // for storage //1 area code w8(0xFF); //1 direction w8(0); //30 wstr(maple_sega_vmu_name,30); //60 wstr(maple_sega_brand,60); //2 w16(0x007c); //2 w16(0x0082); return MDRS_DeviceStatus; } //in[0] is function used //out[0] is function used case MDCF_GetMediaInfo: { u32 function=r32(); switch(function) { case MFID_1_Storage: { w32(MFID_1_Storage); //total_size; w16(0xff); //partition_number; w16(0); //system_area_block; w16(0xFF); //fat_area_block; w16(0xfe); //number_fat_areas_block; w16(1); //file_info_block; w16(0xfd); //number_info_blocks; w16(0xd); //volume_icon; w8(0); //reserved1; w8(0); //save_area_block; w16(0xc8); //number_of_save_blocks; w16(0x1f); //reserverd0 (something for execution files?) w32(0); return MDRS_DataTransfer;//data transfer } break; case MFID_2_LCD: { u32 pt=r32(); if (pt!=0) { printf("VMU: MDCF_GetMediaInfo -> bad input |%08X|, returning MDRE_UnknownCmd\n",pt); return MDRE_UnknownCmd; } else { w32(MFID_2_LCD); w8(47); //X dots -1 w8(31); //Y dots -1 w8(((1)<<4) | (0)); //1 Color, 0 contrast levels w8(0); //Padding return MDRS_DataTransfer; } } break; default: printf("VMU: MDCF_GetMediaInfo -> Bad function used |%08X|, returning -2\n",function); return MDRE_UnknownFunction;//bad function } } break; case MDCF_BlockRead: { u32 function=r32(); switch(function) { case MFID_1_Storage: { w32(MFID_1_Storage); u32 xo=r32(); u32 Block = (SWAP32(xo))&0xffff; w32(xo); if (Block>255) { printf("Block read : %d\n",Block); printf("BLOCK READ ERROR\n"); Block&=255; } wptr(flash_data+Block*512,512); return MDRS_DataTransfer;//data transfer } break; case MFID_2_LCD: { w32(MFID_2_LCD); w32(r32()); // mnn ? wptr(flash_data,192); return MDRS_DataTransfer;//data transfer } break; case MFID_3_Clock: { if (r32()!=0) { printf("VMU: Block read: MFID_3_Clock : invalid params \n"); return MDRE_TransminAgain; //invalid params } else { w32(MFID_3_Clock); time_t now; time(&now); tm* timenow=localtime(&now); u8* timebuf=dma_buffer_out; w8((timenow->tm_year+1900)%256); w8((timenow->tm_year+1900)/256); w8(timenow->tm_mon+1); w8(timenow->tm_mday); w8(timenow->tm_hour); w8(timenow->tm_min); w8(timenow->tm_sec); w8(0); printf("VMU: CLOCK Read-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d!\n",timebuf[0]+timebuf[1]*256,timebuf[2],timebuf[3],timebuf[4],timebuf[5],timebuf[6]); return MDRS_DataTransfer;//transfer reply ... } } break; default: printf("VMU: cmd MDCF_BlockRead -> Bad function |%08X| used, returning -2\n",function); return MDRE_UnknownFunction;//bad function } } break; case MDCF_BlockWrite: { switch(r32()) { case MFID_1_Storage: { u32 bph=r32(); u32 Block = (SWAP32(bph))&0xffff; u32 Phase = ((SWAP32(bph))>>16)&0xff; u32 write_adr=Block*512+Phase*(512/4); u32 write_len=r_count(); rptr(&flash_data[write_adr],write_len); if (file) { fseek(file,write_adr,SEEK_SET); fwrite(&flash_data[write_adr],1,write_len,file); fflush(file); } else { printf("Failed to save VMU %s data\n",logical_port); } return MDRS_DeviceReply;//just ko } break; case MFID_2_LCD: { u32 wat=r32(); rptr(lcd_data,192); u8 white=0xff,black=0x00; for(int y=0;y<32;++y) { u8* dst=lcd_data_decoded+y*48; u8* src=lcd_data+6*y+5; for(int x=0;x<6;++x) { u8 col=*src--; for(int l=0;l<8;l++) { *dst++=col&1?black:white; col>>=1; } } } config->SetImage(lcd_data_decoded); #if !defined(TARGET_PANDORA) && HOST_OS != OS_DARWIN push_vmu_screen(lcd_data_decoded); #endif #if 0 // Update LCD window if (!dev->lcd.visible) { dev->lcd.visible=true; ShowWindow(dev->lcd.handle,SHOW_OPENNOACTIVATE); } InvalidateRect(dev->lcd.handle,NULL,FALSE); } // Logitech G series stuff start #ifdef _HAS_LGLCD_ { lgLcdBitmap160x43x1 bmp; bmp.hdr.Format = LGLCD_BMP_FORMAT_160x43x1; const BYTE white=0x00; const BYTE black=0xFF; //make it all black... memset(bmp.pixels,black,sizeof(bmp.pixels)); //decode from the VMU for(int y=0;y<32;++y) { BYTE *dst=bmp.pixels+5816+((-y)*(48+112)); //ugly way to make things look right :p BYTE *src=dev->lcd.data+6*y+5; for(int x=0;x<48/8;++x) { BYTE val=*src; for(int m=0;m<8;++m) { if(val&(1<<(m))) *dst++=black; else *dst++=white; } --src; } } //Set the damned bits res = lgLcdUpdateBitmap(openContext.device, &bmp.hdr, LGLCD_ASYNC_UPDATE(LGLCD_PRIORITY_NORMAL)); } #endif //Logitech G series stuff end #endif return MDRS_DeviceReply;//just ko } break; case MFID_3_Clock: { if (r32()!=0 || r_count()!=8) return MDRE_TransminAgain; //invalid params ... else { u8 timebuf[8]; rptr(timebuf,8); printf("VMU: CLOCK Write-> datetime is %04d/%02d/%02d ~ %02d:%02d:%02d! Nothing set tho ...\n",timebuf[0]+timebuf[1]*256,timebuf[2],timebuf[3],timebuf[4],timebuf[5],timebuf[6]); return MDRS_DeviceReply;//ok ! } } break; default: { printf("VMU: command MDCF_BlockWrite -> Bad function used, returning MDRE_UnknownFunction\n"); return MDRE_UnknownFunction;//bad function } } } break; case MDCF_GetLastError: return MDRS_DeviceReply;//just ko case MDCF_SetCondition: { switch(r32()) { case MFID_3_Clock: { u32 bp=r32(); if (bp) { printf("BEEP : %08X\n",bp); } return MDRS_DeviceReply;//just ko } break; default: { printf("VMU: command MDCF_SetCondition -> Bad function used, returning MDRE_UnknownFunction\n"); return MDRE_UnknownFunction;//bad function } break; } } default: //printf("Unknown MAPLE COMMAND %d\n",cmd); return MDRE_UnknownCmd; } } }; #endif struct maple_microphone: maple_base { u8 micdata[SIZE_OF_MIC_DATA]; virtual void OnSetup() { memset(micdata,0,sizeof(micdata)); } virtual u32 dma(u32 cmd) { //printf("maple_microphone::dma Called 0x%X;Command %d\n",this->maple_port,cmd); //LOGD("maple_microphone::dma Called 0x%X;Command %d\n",this->maple_port,cmd); switch (cmd) { case MDC_DeviceRequest: LOGI("maple_microphone::dma MDC_DeviceRequest"); //this was copied from the controller case with just the id and name replaced! //caps //4 w32(MFID_4_Mic); //struct data //3*4 w32( 0xfe060f00); w32( 0); w32( 0); //1 area code w8(0xFF); //1 direction w8(0); //30 wstr(maple_sega_mic_name,30); //60 wstr(maple_sega_brand,60); //2 w16(0x01AE); //2 w16(0x01F4); return MDRS_DeviceStatus; case MDCF_GetCondition: { LOGI("maple_microphone::dma MDCF_GetCondition"); //this was copied from the controller case with just the id replaced! //PlainJoystickState pjs; //config->GetInput(&pjs); //caps //4 w32(MFID_4_Mic); //state data //2 key code //w16(pjs.kcode); //triggers //1 R //w8(pjs.trigger[PJTI_R]); //1 L //w8(pjs.trigger[PJTI_L]); //joyx //1 //w8(pjs.joy[PJAI_X1]); //joyy //1 //w8(pjs.joy[PJAI_Y1]); //not used //1 w8(0x80); //1 w8(0x80); } return MDRS_DataTransfer; case MDC_DeviceReset: //uhhh do nothing? LOGI("maple_microphone::dma MDC_DeviceReset"); return MDRS_DeviceReply; case MDCF_MICControl: { //LOGD("maple_microphone::dma handling MDCF_MICControl %d\n",cmd); //MONEY u32 function=r32(); //LOGD("maple_microphone::dma MDCF_MICControl function (1st word) %#010x\n", function); //LOGD("maple_microphone::dma MDCF_MICControl words: %d\n", dma_count_in); switch(function) { case MFID_4_Mic: { //MAGIC HERE //http://dcemulation.org/phpBB/viewtopic.php?f=34&t=69600 // <3 <3 BlueCrab <3 <3 /* 2nd word What it does: 0x0000??03 Sets the amplifier gain, ?? can be from 00 to 1F 0x0f = default 0x00008002 Enables recording 0x00000001 Returns sampled data while recording is enabled While not enabled, returns status of the mic. 0x00000002 Disables recording * */ u32 secondword=r32(); //LOGD("maple_microphone::dma MDCF_MICControl subcommand (2nd word) %#010x\n", subcommand); u32 subcommand = secondword & 0xFF; //just get last byte for now, deal with params later //LOGD("maple_microphone::dma MDCF_MICControl (3rd word) %#010x\n", r32()); //LOGD("maple_microphone::dma MDCF_MICControl (4th word) %#010x\n", r32()); switch(subcommand) { case 0x01: { //LOGI("maple_microphone::dma MDCF_MICControl someone wants some data! (2nd word) %#010x\n", secondword); w32(MFID_4_Mic); //from what i can tell this is up to spec but results in transmit again //w32(secondword); //32 bit header w8(0x04);//status (just the bit for recording) w8(0x0f);//gain (default) w8(0);//exp ? #ifndef TARGET_PANDORA if(get_mic_data(micdata)){ w8(240);//ct (240 samples) wptr(micdata, SIZE_OF_MIC_DATA); }else #endif { w8(0); } return MDRS_DataTransfer; } case 0x02: LOGI("maple_microphone::dma MDCF_MICControl toggle recording %#010x\n",secondword); return MDRS_DeviceReply; case 0x03: LOGI("maple_microphone::dma MDCF_MICControl set gain %#010x\n",secondword); return MDRS_DeviceReply; case MDRE_TransminAgain: LOGW("maple_microphone::dma MDCF_MICControl MDRE_TransminAgain"); //apparently this doesnt matter //wptr(micdata, SIZE_OF_MIC_DATA); return MDRS_DeviceReply;//MDRS_DataTransfer; default: LOGW("maple_microphone::dma UNHANDLED secondword %#010x\n",secondword); break; } } default: LOGW("maple_microphone::dma UNHANDLED function %#010x\n",function); break; } } default: LOGW("maple_microphone::dma UNHANDLED MAPLE COMMAND %d\n",cmd); return MDRE_UnknownFunction; } } }; maple_device* maple_Create(MapleDeviceType type) { maple_device* rv=0; switch(type) { case MDT_SegaController: rv=new maple_sega_controller(); break; case MDT_Microphone: rv=new maple_microphone(); break; #ifdef HAS_VMU case MDT_SegaVMU: rv = new maple_sega_vmu(); break; #endif default: return 0; } return rv; }