#include "types.h" #include "maple_if.h" #include "maple_helper.h" #include "maple_devs.h" #include "maple_cfg.h" #include 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."; 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 ? propably never used MFID_11_Camera = 0x00080000, //DreamEye }; enum MapleDeviceCommand { MDC_DeviceRequest =0x01, //7 words.Note : Initialises device MDC_AllStatusReq =0x02, //7 words + device depedant ( 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 ? //Varius 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 depedant 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 anykind, 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); //trigers //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 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) { printf("Failed to create vmu save file \"%s\"\n",apath.c_str()); } else { fread(flash_data,1,sizeof(flash_data),file); } } virtual ~maple_sega_vmu() { if (file) fclose(file); } virtual u32 dma(u32 cmd) { //printf("maple_sega_vmu::dma Called for port 0x%X, Command %d\n",device_instance->port,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 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: cmd 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: cmd 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 maple_device* maple_Create(MapleDeviceType type) { maple_device* rv=0; switch(type) { case MDT_SegaController: rv=new maple_sega_controller(); break; #ifdef HAS_VMU case MDT_SegaVMU: rv = new maple_sega_vmu(); break; #endif default: return 0; } return rv; }