月光宝盒游戏机大家都了解,但是里面开发流程指导的人比较少,去年本人有幸接到过这样一个项目,研发月光宝盒游戏机。 在过去几年这边积累了非常多的模拟器开发资源。下面是淘宝上一款月光宝盒机器,实际上目前有几个大厂在做,淘宝上爆款一看就了解,我的客户机器比这个还要大一些,需要投币。
可以在网盘下载,app体验:https://pan.baidu.com/s/1c2BdnPY
软件分为2个部分:输入装置+游戏机APP+输出装置。这样就组成一个游戏机系统。
输入装置: 这里主要是按键摇杆, 客户的按键是通过串口实现的,需要我自己去读串口发到我的APP中。
而且客户为了怕设备被破解,串口通信还加了密,真是坑爹啊。
游戏机APP:这里使用的是我 这边街机pro专业版游戏机模拟器,支持拳皇97 98等8000多个游戏。 通过修改,去掉游戏机里面触摸按键,介入上面说的游戏按键。
输出装置:输出装置就是显示器和声音。这些都是走的安卓系统通用api。不需要特殊处理。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "SerialPort.h"
#include "miyao.h"
#include "android/log.h"
static const char *TAG="serial_port";
#define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args)
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args)
#define IsLock 1
//下函数由叶先生实现
/*需要在JNI中实现如下函数功能函数
函数功能:将OStar_msg结构加密后转化成字节。
函数目的:(用于java层调用后获得返回字节结果,java层将直接写入到串口。)
char * MsgToByte(OStar_msg msg)
函数功能:将字节添加到缓冲区。 若成功返回1,若缓冲区满返回0
函数目的:(用于java层从串口获得字节后调用它将字节保存到缓冲区)
int MsgCacheOffer(char * byte)
函数功能:将从缓冲区取出24个有效数据返回,若无返回NULL。(函数需要校验 直到成功取出正确数据)
函数目的:(用于java层调用后,直接从串口取出正确数据)
OStar_msg MsgPoll()
*/
//下函数由softboy实现
/*
需要在JNI中实现 Java 层接口函数:
函数功能:将java层函数数据转化成 OStar_msg
函数目的:调用MsgToByte转成成字节返回给java。用于发送。调用 MsgToByte();
JNIEXPORT jbyteArray JNICALL Java_org_winplus_serial_utils_SerialPort_msg2byte
(JNIEnv *env, jobject thiz,jint msg1,jint msg2,jint msg3)
函数功能:将java层从串口读出的字节调用MsgCacheOffer 写入到缓冲区
函数目的:java层串口读取字节 ,调用 MsgCacheOffer();
JNIEXPORT jbyteArray JNICALL Java_org_winplus_serial_utils_SerialPort_msg2cache
(JNIEnv *env, jobject thiz,jbyteArray bytes)
函数功能:将java层从串口读出的字节调用MsgCacheOffer 写入到缓冲区
函数目的:java串口读取有效数据 ,调用 MsgPoll()
JNIEXPORT jobject JNICALL Java_org_winplus_serial_utils_SerialPort_msg2java
(JNIEnv *env, jobject thiz)
*/
static speed_t getBaudrate(jint baudrate)
{
switch (baudrate)
{
case 0: return B0;
case 50: return B50;
case 75: return B75;
case 110: return B110;
case 134: return B134;
case 150: return B150;
case 200: return B200;
case 300: return B300;
case 600: return B600;
case 1200: return B1200;
case 1800: return B1800;
case 2400: return B2400;
case 4800: return B4800;
case 9600: return B9600;
case 19200: return B19200;
case 38400: return B38400;
case 57600: return B57600;
case 115200: return B115200;
case 230400: return B230400;
case 460800: return B460800;
case 500000: return B500000;
case 576000: return B576000;
case 921600: return B921600;
case 1000000: return B1000000;
case 1152000: return B1152000;
case 1500000: return B1500000;
case 2000000: return B2000000;
case 2500000: return B2500000;
case 3000000: return B3000000;
case 3500000: return B3500000;
case 4000000: return B4000000;
default: return B9600;
}
}
typedef unsigned long u32;
typedef unsigned short u16;
typedef unsigned char u8;
#define JIAOME_LEN 18 //加密长度
typedef struct CMD_group1
{
u8 CMD;
u8 rand1;
}CMD_group;
typedef struct OStar_msg1
{
union
{
u8 tbyte[16];
u16 tword[8];
u32 tdword[4];
}data;
CMD_group CMD_g;
struct
{
u8 xuliehao1;
u8 xuliehao2;
u8 verify;
u8 Align[3];
}verf;
}OStar_msg;
typedef union
{
long d32;
char d8[4];
}U32data;
long Big_Lit_chang_32(long dat)
{
U32data a,b;
a.d32=dat;
b.d8[0]=a.d8[3];
b.d8[1]=a.d8[2];
b.d8[2]=a.d8[1];
b.d8[3]=a.d8[0];
return b.d32;
}
typedef union
{
unsigned short d16;
unsigned char d8[2];
}U16data;
unsigned short Big_Lit_chang_16(unsigned short dat)
{
U16data a,b;
a.d16=dat;
b.d8[0]=a.d8[1];
b.d8[1]=a.d8[0];
return b.d16;
}
#define GetItemSum( XX ) (sizeof(XX)/sizeof(XX[0]))
#define R_PUSH_BUFF_INT(_NAME, XXX) do{\
_NAME ## _flag ++; _NAME ## _buff[_NAME ## _in] = XXX; \
if(_NAME ## _in >= GetItemSum(_NAME ## _buff)-1) _NAME ## _in = 0; \
else _NAME ## _in++;}while(0)
#define R_POP_BUFF_INT(_NAME, XXX) do{ \
XXX = _NAME ## _buff[_NAME ## _out]; \
_NAME ## _flag --; \
if(_NAME ## _out >= GetItemSum(_NAME ## _buff) -1) _NAME ## _out =0; \
else _NAME ## _out++;}while(0)
#define MAX_BUFF 512
u32 USART_rece_in=0;
u32 USART_rece_out=0;
u32 USART_rece_flag=0;
u8 USART_rece_buff[MAX_BUFF];
u32 usart_err=0;
int MsgCacheOffer(char *byte,int len)
{
int i = 0;
for (i=0;i=MAX_BUFF)
{
usart_err++;
LOGD("jni MsgCacheOffer called errors:%d",usart_err);
return 0;
}
R_PUSH_BUFF_INT(USART_rece,(byte[i]&0xff));
}
return len;
}
void OutdataToBuff(u8 *dat,u32 len)
{
u8 i;
for (i=0;iGetStringUTFChars(env, path, &iscopy);
LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
fd = open(path_utf, O_RDWR | flags);
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf);
if (fd == -1)
{
/* Throw an exception */
LOGE("Cannot open port");
/* TODO: throw an exception */
return NULL;
}
}
/* Configure device */
{
struct termios cfg;
LOGD("Configuring serial port");
if (tcgetattr(fd, &cfg))
{
LOGE("tcgetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
cfmakeraw(&cfg);
cfsetispeed(&cfg, speed);
cfsetospeed(&cfg, speed);
if (tcsetattr(fd, TCSANOW, &cfg))
{
LOGE("tcsetattr() failed");
close(fd);
/* TODO: throw an exception */
return NULL;
}
}
static_fd = fd;
/* Create a corresponding file descriptor */
{
jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor");
jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "", "()V");
jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I");
mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor);
(*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd);
}
dpclazz_key= (*env)->FindClass(env,"org/winplus/serial/utils/SerialPort");
if (dpclazz_key==0)
{
LOGD("find class error");
return;
}
method_key = (*env)->GetStaticMethodID(env,dpclazz_key,"onKey","(III)V");
if (method_key==0)
{
LOGD("find onKeyValue error");
return;
}
return mFileDescriptor;
}
/*
* Class: cedric_serial_SerialPort
* Method: close
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_org_winplus_serial_utils_SerialPort_close
(JNIEnv *env, jobject thiz)
{
jclass SerialPortClass = (*env)->GetObjectClass(env, thiz);
jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor");
jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;");
jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I");
jobject mFd = (*env)->GetObjectField(env, thiz, mFdID);
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
static_fd = -1;
inited = -1;
LOGD("close(fd = %d)", descriptor);
close(descriptor);
}
JNIEXPORT void JNICALL Java_org_winplus_serial_utils_SerialPort_msg2cache
(JNIEnv *env, jobject thiz,jbyteArray pcmData,jint len){
jbyte *pcm;
jboolean isCopy = 0;
pcm = (*env)->GetByteArrayElements(env,pcmData, 0);
int ret = MsgCacheOffer(pcm,len);
//LOGD("jni msg2cache len:%d",ret);
(*env)->ReleaseByteArrayElements(env,pcmData, pcm, JNI_ABORT);
}
int PortSend(int fdcom, char *data, int datalen)
{
return write(fdcom, data, datalen);
}
enum CMD_2android //stm发给android
{
CMD_OStar_Key = 0xA1,
CMD_OStar_TuiB = 0x5f,
CMD_OStar_GetInfo = 0x12,
CMD_OStar_ErrorInfo = 0x11,
CMD_OStar_ShakeHand = 0x23,
CMD_OStar_BackFlsh,
CMD_OStar_BackOK,
};
enum CMD_2STM32 //android发给stm
{
CMD2STM32_tuiBi,
CMD2STM32_tuiCP,
CMD2STM32_GetMacInfo,
CMD2STM32_ResetSTM,
CMD2STM32_ShakeHand,
CMD2STM32_Ask_ShakeHand,
CMD2STM32_AskFlsh,
CMD2STM32_SaveOneFlsh,
CMD2STM32_TuiBPinit,
CMD2STM32_end = 0xff
};
const u32 MaskBits[32]
={
0x1,0x2,0x4,0x8,0x10,0x20,0x40,0x80,
(0x1UL<<8), (0x1UL<<9), (0x1UL<<10), (0x1UL<<11),
(0x1UL<<12), (0x1UL<<13), (0x1UL<<14), (0x1UL<<15),
(0x1UL<<16), (0x1UL<<17), (0x1UL<<18), (0x1UL<<19),
(0x1UL<<20), (0x1UL<<21), (0x1UL<<22), (0x1UL<<23),
(0x1UL<<24), (0x1UL<<25), (0x1UL<<26), (0x1UL<<27),
(0x1UL<<28), (0x1UL<<29), (0x1UL<<30), (0x1UL<<31),
};
#define KeyDowndelay 1
#define KeyUpdelay 1
#define MAX_SLAVE 2
int OTHER_KEY_START = 15;
int FUNC_KEY_START = 20;
enum
{
MenuKey_None,
MenuKey_Down,
MenuKey_OK,
MenuKey_SGame,
MenuKey_Right,
MenuKey_Left,
MenuKey_No,
MenuKey_Up,
MenuKey_Count,
};
u8 meun_key_flag=0Xff;
u8 meun_key=0Xff;
#define MenuKeyDownDelay 1
#define MenuKeyUpDelay 3
u8 meunkeydelay[8];
void DealMeunKey(JNIEnv *env, jobject thiz)
{
u8 i,newflag,oldflag;
for (i=0;iMenuKeyDownDelay)
{
meunkeydelay[i]=0;
meun_key_flag&=~MaskBits[i];
//有键按下
LOGD("jni DealMeunKey 1 CMD_OStar_Key down:%d ",i);
(*env)->CallStaticVoidMethod(env,dpclazz_key,method_key,i+FUNC_KEY_START,1,0);
}
} else
{
if (meunkeydelay[i]>MenuKeyUpDelay)
{
meunkeydelay[i]=0;
meun_key_flag|=MaskBits[i];
//按键弹起
LOGD("jni DealMeunKey 1 CMD_OStar_Key down:%d ",i);
(*env)->CallStaticVoidMethod(env,dpclazz_key,method_key,i+FUNC_KEY_START,0,0);
}
}
} else
{
meunkeydelay[i]=0;
}
}
}
enum
{
play_start,
play_button3,
play_button1,
play_button2,
play_right,
play_left,
play_down,
play_up,
play_button4,
play_button5,
play_button6,
play_cnt,
};
u16 orgKeyflag[MAX_SLAVE] ={ 0xffff, 0xffff};
u16 doKeyflag[MAX_SLAVE] = { 0xffff, 0xffff};
u16 Key_delay[MAX_SLAVE][play_cnt] = { 0, 0};
void Do_Key(JNIEnv *env, jobject thiz)
{
int i, id, orgflag, doflag;
for (id = 0; id KeyDowndelay)
{
Key_delay[id][i] = 0;
doKeyflag[id] &= ~MaskBits[i];
//按键按下
LOGD("jni Do_Key 1 CMD_OStar_Key down:%d ,player is:%d",i,id);
(*env)->CallStaticVoidMethod(env,dpclazz_key,method_key,i,1,id);
}
} else
{
if (Key_delay[id][i]>KeyUpdelay)
{
Key_delay[id][i] = 0;
doKeyflag[id] |= MaskBits[i];
//按键弹起
LOGD("jni Do_Key 1 CMD_OStar_Key up:%d ,player is:%d",i);
(*env)->CallStaticVoidMethod(env,dpclazz_key,method_key,i,0,id);
}
}
} else
{
Key_delay[id][i] = 0;
}
}
}
}
//投币数
int ToubiCnt[MAX_SLAVE]={0,0};
void dealMsg(OStar_msg msg,JNIEnv *env, jobject thiz){
unsigned int i=0;
jclass dpclazz;jmethodID method;
LOGD("jni dealMsg called get msg,cmd:%d,errors:%d",msg.CMD_g.CMD,usart_err);
OStar_msg data;
int id=0;
switch (msg.CMD_g.CMD)
{
case CMD_OStar_ShakeHand:
inited = 0;
memset((unsigned char* )&data,0,sizeof(data));
data.data.tdword[0]=msg.data.tdword[0];
data.CMD_g.CMD=CMD2STM32_ShakeHand;
MsgToByteAndWrite(data,static_fd);
LOGD("jni dealMsg CMD_OStar_ShakeHand static_fd %d",static_fd);
break;
case CMD_OStar_Key:
orgKeyflag[0]=msg.data.tbyte[0];
orgKeyflag[1]=msg.data.tbyte[1];
if (msg.data.tbyte[6]&MaskBits[0])orgKeyflag[0]|=MaskBits[8];
else orgKeyflag[0]&=~MaskBits[8];
if (msg.data.tbyte[6]&MaskBits[1])orgKeyflag[1]|=MaskBits[8];
else orgKeyflag[1]&=~MaskBits[8];
if (msg.data.tbyte[6]&MaskBits[2])orgKeyflag[0]|=MaskBits[9];
else orgKeyflag[0]&=~MaskBits[9];
if (msg.data.tbyte[6]&MaskBits[3])orgKeyflag[0]|=MaskBits[10];
else orgKeyflag[0]&=~MaskBits[10];
if (msg.data.tbyte[6]&MaskBits[4])orgKeyflag[1]|=MaskBits[9];
else orgKeyflag[1]&=~MaskBits[9];
if (msg.data.tbyte[6]&MaskBits[5])orgKeyflag[1]|=MaskBits[10];
else orgKeyflag[1]&=~MaskBits[10];
Do_Key(env,thiz);
for (id=0;id0)
{
//正在投币中
ToubiCnt[id]+=msg.data.tbyte[7+id];
LOGD("jni dealMsg coin player: %d , total:%d ,add:%d",id,ToubiCnt[id],msg.data.tbyte[7+id]);
(*env)->CallStaticVoidMethod(env,dpclazz_key,method_key,OTHER_KEY_START,msg.data.tbyte[7+id],id);
}
}
meun_key = msg.data.tbyte[13];
DealMeunKey(env,thiz);
break;
case CMD_OStar_GetInfo:
LOGD("jni dealMsg CMD_OStar_GetInfo 0 %d",msg.data.tdword[0]);
LOGD("jni dealMsg CMD_OStar_GetInfo 1 %d",msg.data.tdword[1]);
LOGD("jni dealMsg CMD_OStar_GetInfo 2 %d",msg.data.tdword[2]);
LOGD("jni dealMsg CMD_OStar_GetInfo 3 %d",msg.data.tdword[3]);
break;
case CMD_OStar_ErrorInfo:
for (id=0;idCallStaticVoidMethod(env,dpclazz_key,method_key,OTHER_KEY_START+1,0,0);
} else
{
//清除投币固障
(*env)->CallStaticVoidMethod(env,dpclazz_key,method_key,OTHER_KEY_START+2,0,0);
}
}
break;
}
}
JNIEXPORT jobject JNICALL Java_org_winplus_serial_utils_SerialPort_msg2java
(JNIEnv *env, jobject thiz){
MsgPoll(env,thiz);
}
JNIEXPORT jint JNICALL Java_org_winplus_serial_utils_SerialPort_msginit
(JNIEnv *env, jobject thiz){
OStar_msg data;
memset((unsigned char* )&data,0,sizeof(data));
if (inited == 0)
{
LOGD("jni msginit called CMD2STM32_GetMacInfo");
data.CMD_g.CMD=CMD2STM32_GetMacInfo;
MsgToByteAndWrite(data,static_fd);
inited = 1;
} else if (inited == -1)
{
LOGD("jni msginit called CMD2STM32_Ask_ShakeHand");
data.CMD_g.CMD = CMD2STM32_Ask_ShakeHand;
MsgToByteAndWrite(data,static_fd);
} else if (inited == 1)
{
}
//LOGD("jni msginit called %d:",inited);
return inited;
}