上位机MFC组合逻辑仿真器源代码下载
转出结果如下: 源代码下载地址: 上位机VC MFC程序开发精典实例大全源码与视频讲解配套下载408例 经历1年的编程与录制点击进入查看 如果您认可,可联系功能定制! 如果您着急,充值会员可直接联系发您资料!
工程的介绍可以参考下面。 程序由几个大模块组成:元件数据结构模块, 电路图编辑模块,元件库模块,计算结果(仿真)模块。 l 元件数据结构模块 统一的元件数据结构可以提高程序运行速度,方便程序编制。不足是由于元件的不同存储空间上可能会有少许浪费。 所有的元件种类共有8种,如图 定义数据结构如下: typedefstruct tagMyNode { Mytypetype; //元件类型 MySubtype Subtype; //元件子类型 tagMyNode*input1; //输入端1 tagMyNode* input2; //输入端1 tagMyNode* output1; //输出端1 UINTinput1value; //输入端input1的值 UINTinput2value; //输入端input2的值 UINT output1value; //输出端output1的值 intinputs; //当前已经有几个输入端有值 int number; //对于输入结点的序号 CPointOrgpoint; //记录元件左上角位置 int width; //记录元件宽度 int height; //记录元件高度 }MyNode; u 元件类型:元件类型Mytype type中Mytype是一个枚举类型定义如下: enum Mytype { Node, //结点 Gate, //门 }; 分为两种类型:Node结点和Gate门。 u 元件子类型:元件子类型MySubtype Subtype中MySubtype也是一个枚举类 型,定义如下: enum MySubtype { Input, //输入端 Output, //输出端 ANDGate, //与门 ORGate, //或门 NOTGate, //非门 NORGate, //或非门 NANDGate, //与非门 XORGate, //异或门 }; u 指针连接: tagMyNode* input1; tagMyNode*input2; tagMyNode*output1 是指向此结点的指针。由于元件之间是要相互连接的,于是设置这几个指针用于元件之间的连接。其中特殊情况有: 非门:由于非门只有一个输入端,所以非门不用tagMyNode* input2; 输入结点:输入结点只有一个链接端(这里称之为触点),采用tagMyNode* output1 输出结点:同输入结点,只有一个触点,采用tagMyNode* input1; u 保存触点值:由于要进行仿真计算,所以还需保存各个触点的值: UINT input1value; UINTinput2value; UINT output1value; 同指针连接,有3种特殊情况: 非门:不用UINT input2value; 输入结点:采用UINT output1value; 输出结点:采用UINT input1value; u 进位标志:int inputs; 在进行仿真计算时,要用进位标志辅助计算。如与门只有在两个输入端都有值时,即inputs==2时,才能进位。 u 输入结点序号:int number; 每个输入结点都有不同的序号,从1开始递增。 u 元件位置和大小: CPoint Orgpoint; int width; int height; Orgpoint用于记录元件在视图中左上角的坐标 width用于记录元件宽度 height用于记录元件高度 l 电路图编辑模块 电路图编辑模块又分为两个子模块:鼠标放置元件模块,鼠标连接元件模块 首先在工具栏中可以选择这两种状态,如图 在按钮上单击可以切换状态。 定义一个枚举类型MyStatus来记录当前状态: enum MyStatus { NONE, //鼠标连接元件状态 ANDGATE, ORGATE, NOTGATE, NORGATE, NANDGATE, XORGATE, NODEINPUT, NODEOUTPUT }; MyStatus Status; 其中:NONE为鼠标连接状态,其他为鼠标放置状态。 u 鼠标放置元件模块 其算法如图 n DrawObject函数: 首先根据Status的状态,即六个门,两个端结点。共8种来调用DrawObject函数 2 引入准备好的八张位图(六个门,两个端) CBitmap MyBitMap; MyBitMap.LoadBitmap (nID); 2 将引入的位图拷贝入窗体窗户区 BITMAP bmpInfo; MyBitMap.GetBitmap (&bmpInfo); pOldBitmap=dc.SelectObject (&MyBitMap); ClientDC.BitBlt(point.x ,point.y,bmpInfo.bmWidth ,bmpInfo.bmHeight,&dc,0,0,SRCAND); dc.SelectObject (pOldBitmap); 2 用全局变量bmWidth和bmHeight来保存元件的宽度和高度 bmWidth=bmpInfo.bmWidth ; bmHeight=bmpInfo.bmHeight ; n CreateMyObject函数 函数声明为:CreateMyObject(Mytype type, MySubtype Subtype, CPoint point) 2 初始化元件 MyNode* pNode=new MyNode; pNode->type =type; pNode->Subtype=Subtype; pNode->input1 =0; pNode->input2 =0; pNode->output1 =0; pNode->output2 =0; pNode->Orgpoint =point; pNode->width=bmWidth; pNode->height=bmHeight; pNode->input1value =0; pNode->input2value=0; pNode->output1value=0; pNode->inputs =0; 2 如果创建的元件为输入结点,则要创建并画输入结点前的序号,这里 采用一个全局数组CArray<CPoint,CPoint> numpoint来记录结点前序号。 if(Subtype==Input) { //当创建Input时加入点到numpoint数组中 numpoint.Add(CPoint(point.x-15,point.y)); pNode->number =numpoint.GetSize(); //创建时重绘序号 redrawnum(); } 而redrawnum()函数就是将所有输入结点前的序号重绘。 2 最后将元件加入到全局链表CList<MyNode*,MyNode*> MyList中。 MyList.AddTail (pNode); u 鼠标连接元件模块 鼠标连接元件模块分为三个过程模块:鼠标移动模块,鼠标按下模块,鼠标抬起模块。 n 鼠标移动模块 其算法如图 代码如下: void CMyView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your messagehandler code here and/or call default //此时必然是非画图状态,所以status==NONE; if(Status==NONE) { //当前点在某个物件上吗? 并且 //当前点在该物件触点上吗? if(IsPointInObject(point)&& IsPointInPut(point)) { //全局变量pNodeNow是在IsPointInObject()这个函数里面记录的 //circlepoint和put是在IsInInput1() IsInInput2() IsInOutput1() //这三个函数中记录的 //判断此时触点时否己连接非常重要 if(IsPutLinked()) { //如果此时触点己连接,则退出 return; } //此时鼠标移进触点 //当前是连接态吗? if(IsLink) { //连接态画图 LinkStatusDraw(point); } //开启画圆圈态 IsDrawCircle=TRUE; //画圆圈 DrawMyCircle(); } else//此时鼠标移出触点 { //如果此时已画圆圈,则要擦除圆圈 if(IsDrawCircle==TRUE) { EraserMyCircle(); //关闭画圆圈状态 IsDrawCircle=FALSE; //重绘连接线 moveoutredrawline(); //重绘圆圈所在的那个物件,因为擦除圆圈的时候可能擦除了部分物件 //------------------- redrawMyObject(pNodeNow); //如果此时是连接状态,连接态画图 } if(IsLink) { //连接态画图 LinkStatusDraw(point); } } } CView::OnMouseMove(nFlags,point); } 2 两个关键状态:可连接态IsDrawCircle和正在连接态IsLink Ø 可连接态IsDrawCircle 当且仅当鼠标移动到某个元件上的某个尚未连接的触点上,才开启可连接态IsDrawCircle。之所以取名IsDrawCircle是因为此时会在鼠标停留的尚未连接的触点上画一个黑色小圆圈。 当鼠标移动离开触点,可连接态IsDrawCircle关闭。 Ø 正在连接态IsLink 当鼠标按下(见图5)并且此时可连接态IsDrawCircle开启(为TRUE)时正在连接态IsLink开启。 2 判断当前点是否在某个元件函数:IsPointInObject() 其算法如图 2 判断当前点是否在该元件触点上函数:IsPointInPut() 其算法如图 与门与其它5个门有所不同,与门只有一个输入端,所以要分开来判断 对于输入结点,则判断当前点是否在第一个输出端触点。 对于输出结点,则判断当前点是否在第一个输入端触点。 输入结点和输出结点的这样判断,一眼看上去似乎反了,但实际上有利于整个程序的编写。可以简单地这样分类:总共只有两种端,一种输入,一种输出。 这样,我们就可以将判断触点分为三个函数: IsInInput1() IsInInput2() IsInOutput1() 拿IsInInput1()来分析: centerpoint=GetCirclePoint(Input_1); if(IsInArea(point)) { //说明此时就在触点Input_1,用全局变量put记录下来 put=Input_1; //如果当前点在,则要保存触点中心点 circlepoint=centerpoint; returnTRUE; } else { //如果移出触点,肯定不要再保存中心点 returnFALSE; } 首先,调用函数GetCirclePoint()来取得当前触点的中心点。然后调用IsInArea(point)函数来判断当前点point是否在以当前触点中心点为中心的矩形区域内。如果是,则用一个全局枚举变量put来记录来前触点是两个输入端和一个输出端中哪一个。 我们看这个枚举类型: enumMyput { Input_1, Input_2, Output_1 }; 接下来用一个全局变量circlepoint来记录当前触点中心点。再返回真。 如果当前点不在以当前触点中心点为中心的矩形区域内,则返回假。这时千万不能记录当前触点中心点。这点不注意会出大错。 2 判断当前触点是否已连接函数:IsPutLinked() BOOLCMyView::IsPutLinked() { switch(put) { case Input_1: if(pNodeNow->input1 !=0) return TRUE; break; case Input_2: if(pNodeNow->input2 !=0) return TRUE; break; case Output_1: if(pNodeNow->output1 !=0) return TRUE; } return FALSE; } 这里根据全局变量put的类型和全局变量pNodeNow所指向的元件, 就可以判断当前元件的当前触点是否已连接。如果连接相应指针不为0。返回真,否则返回假。 2 连接态画图函数:LinkStatusDraw() voidCMyView: inkStatusDraw(CPoint point) { CClientDC clientDC(this); CPenwhitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=clientDC.SelectObject(&whitepen); clientDC.MoveTo (startpoint); clientDC.LineTo (lastpoint); clientDC.SelectObject (pOldPen); CPen redpen(PS_DOT,1,RGB(255,0,0)); pOldPen=clientDC.SelectObject(&redpen); clientDC.MoveTo (startpoint); clientDC.LineTo (point); clientDC.SelectObject (pOldPen); lastpoint=point; //重绘所有输入结点前的序号 redrawnum(); //重绘连接线 LinkLineRedraw(startpoint,point); //重绘物件 lineRedraw(startpoint,point); } 这里,startpoint是鼠标按下开始连接时起始元件触点中心点坐 标,lastpoint是上一次鼠标移动所停留的点。为了实在连接时鼠标移动 的动画效果,我们要先擦除上一次移动画的线(用白笔),然后再从startpoint到当前点point画线。移动时由于不信的擦除重画,可能将先前已画的元件,输入结点前的序号,和已经连接好的线擦除。于是我们需要重绘。 重绘所有输入结点前的序号 redrawnum(); voidCMyView::redrawnum() { CClientDC dc(this); char buffer[20]; CPoint point; //重绘所有Input前的序号 for(int i=0;i<numpoint.GetSize ();i++) { //将数字转换成字符,存于buffer中 _itoa(i+1,buffer,10); point=numpoint.GetAt (i); dc.TextOut (point.x ,point.y,buffer); } } 由于每创建一个输入结点,就要相应地记录一个序号。这个序号的位置点记录在numpoint这个数组中。数组声明如下: CArray<CPoint,CPoint> numpoint; 而数组的下标加1就为序号。 所以每次重绘为了方便,将所有序号都重绘。 重绘连接线 voidCMyView: inkLineRedraw(CPoint startpoint, CPoint point) { //将起点startpoint到终点point扩充成一个矩形drawrect CRect drawrect(startpoint,point); //rect用于产生连接线最大矩形 CRect rect; //rectInter用于计算两个矩形的相交区域 CRect rectInter; //point1和point2用于产生连接线最大矩形 CPoint point1; CPoint point2; drawrect.NormalizeRect (); drawrect.InflateRect (1,1); //遍历MyPointList链表 POSITION pos=MyPointList.GetHeadPosition(); while(pos!=0) { //pPointArray用于指向点数组对象首址 CArray<CPoint,CPoint>*pPointArray=MyPointList.GetNext (pos); point1=pPointArray->GetAt (0); switch(pPointArray->GetSize ()) { //分两种情况 :2个点和4,5个点的情况 case 2: //2个点时 point2=pPointArray->GetAt(1); break; default: //4,5个点时 point2=pPointArray->GetAt(3); } //用point1和point2设置矩形rect rect.left =point1.x ; rect.top =point1.y; rect.right =point2.x; rect.bottom =point2.y; rect.NormalizeRect (); rect.InflateRect (1,1); //如果两个矩形相交,则要重绘 if(rectInter.IntersectRect(&drawrect,&rect)) { DrawLinkLine(pPointArray); } } } 主要的算法思想是:将起点startpoint到当前点point扩充成一个矩形drawrect,然后遍历连接线链表,将每根连接线扩充成一个矩形rect,再判断这两个矩形是否相交,若相交,则需要重绘这根连接线。 连接线链表声明如下: CList<CArray<CPoint,CPoint>*,CArray<CPoint,CPoint>*> MyPointList; 链表中每个结点是一个数组对象的地址,而这个数组中每个元素是一个点。这样一个数组就表示了一根连接线,而一个链表可以遍历所以连接线。 2 画提示连接的小圆圈函数:DrawMyCircle() voidCMyView: rawMyCircle() { //此时全局变量circlepoint记录了要画圆圈的 //而pNodeNow指向了当前的物件 //将物件坐标中的circlepoint转换成VIEW中的坐标 int x,y; x=pNodeNow->Orgpoint .x+circlepoint.x; y=pNodeNow->Orgpoint .y+circlepoint.y; CClientDC dc(this); //创建一个黑色的画刷 CBrush brush(RGB(0,0,0)); //创建指针pOldBrush用于保存原来的画刷 CBrush* pOldBrush; //将黑色的画刷选进设备装置DC,并用pOldBrush保存原来的画刷 pOldBrush=dc.SelectObject (&brush); //画一个圆圈,圆心是(x,y) //半径是4 dc.Ellipse (x-4,y-4,x+4,y+4); //将原来的画刷选回 dc.SelectObject (pOldBrush); } 由于全局变量circlepoint保存的是元件内部的相对坐标,需要将它 转换成视图中的坐标 x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y+circlepoint.y; 以上两句完成坐标的转换。 然后以(x,y)为圆心,4为半径,画一个黑色小圆圈 dc.Ellipse (x-4,y-4,x+4,y+4) 2 擦除小圆圈函数:EraserMyCircle() voidCMyView::EraserMyCircle() { int x,y; x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y+circlepoint.y; CClientDC dc(this); CPenwhitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=dc.SelectObject (&whitepen); dc.Ellipse (x-4,y-4,x+4,y+4); dc.SelectObject (pOldPen); } 与画小圆圈不同的是,擦除时要选择白色的笔和白色的画刷(默认) CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=dc.SelectObject (&whitepen); 以上3句选择白色的笔。 2 鼠标移开触点重绘连接线函数:moveoutredrawline() 为什么需要这个函数,原因是在鼠标称出触点后,此时要擦除刚才画 的小圆圈,而如果此时已经生成了连接线,则会擦除掉连接线的一小部分。于是需要这个函数。 voidCMyView::moveoutredrawline() { int x,y; x=pNodeNow->Orgpoint .x+circlepoint.x; y=pNodeNow->Orgpoint .y+circlepoint.y; CPoint point1; CPoint point2; point1.x=x-4; point1.y=y-4; point2.x=x+4; point2.y=y+4; LinkLineRedraw(point1,point2); } 此时pNodeNow指向刚擦除小圆圈的元件,而circlepoint则记录着 触点中心。于是只要将以ciclepoint为中心的半径为4的矩形的左上角点和右下角点为参数调用LinkLineRedraw即可。 2 重绘元件函数redrawMyObject() voidCMyView::redrawMyObject(MyNode* pNode) { switch(pNode->Subtype ) { case ANDGate: DrawObject(pNode->Orgpoint,IDB_ANDGATE); break; case ORGate: DrawObject(pNode->Orgpoint,IDB_ORGATE); break; case NOTGate: DrawObject(pNode->Orgpoint,IDB_NOTGATE); break; case NORGate: DrawObject(pNode->Orgpoint,IDB_NORGATE); break; case NANDGate: DrawObject(pNode->Orgpoint,IDB_NANDGATE); break; case XORGate: DrawObject(pNode->Orgpoint,IDB_XORGATE); break; case Input: DrawObject(pNode->Orgpoint,IDB_NODEINPUT); break; case Output: DrawObject(pNode->Orgpoint,IDB_NODEOUTPUT); break; } } 该函数参数为指向元件的指针,用于重绘所指向的元件。 n 鼠标按下模块 如图 前面已经分析了放置元件状态,现在看连接元件状态中的判断: “当前点是否在某个元件未连接的触点上”其实就是判断“可连接态”IsDrawCircle是否为真。代码如下: if(IsDrawCircle)//当前点在某个元件未连接的触点上 { //全局变量IsLink表示开始连接状态 IsLink=TRUE; //全局变量pNodeStart记录当前物件 pNodeStart=pNodeNow; //全局变量startpoint记录当前触点中心坐标(注,此时要进行坐标转换 startpoint.x=pNodeNow->Orgpoint.x +circlepoint.x; startpoint.y=pNodeNow->Orgpoint.y +circlepoint.y; //全局变量startput记录当前触点类别:Input_1,Input_2,Output_1; startput=put; //lastpoint用于鼠标移动时擦除线效果 lastpoint=startpoint; } 进行连接初始化:首先开启开始连接状态 IsLink=TRUE; 然后用全局变量pNodeStart指向当前元件 pNodeStart=pNodeNow 全局变量startpoint记录当前触点中心坐标(这时要进行坐标的转换) startpoint.x=pNodeNow->Orgpoint.x +circlepoint.x; startpoint.y=pNodeNow->Orgpoint.y +circlepoint.y; 全局变量startput记录当前触点类别 startput=put; 最后lastpoint用于鼠标移动时擦除线效果 lastpoint=startpoint; n 鼠标抬起模块 其算法如图 代码如下: voidCMyView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler codehere and/or call default if(IsLink) { //首先擦除从startpoint到point CClientDC clientDC(this); CClientDC*pDC=&clientDC; CPenwhitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=clientDC.SelectObject(&whitepen); clientDC.MoveTo(startpoint); clientDC.LineTo (point); clientDC.SelectObject(pOldPen); //重绘所有输入结点前的序号 redrawnum(); //重绘连接线 LinkLineRedraw(startpoint,point); //重绘物件 lineRedraw(startpoint,point); if(IsDrawCircle) { //用全局变量pNodeCurrent记录终点连接的物体 pNodeCurrent=pNodeNow; //用全局变量currentput记录终点连接的触点 currentput=put; //用全局变量currentpoint记录终点触点的中心坐标 currentpoint.x=pNodeNow->Orgpoint.x +circlepoint.x; currentpoint.y=pNodeNow->Orgpoint.y +circlepoint.y; //IsTwoObjectsCanLink()函数判断两个物件是否能连接 if(IsTwoObjectsCanLink()) { //先擦除圆圈 //EraserMyCircle();没有必要,只要鼠标移开时重绘连接线就可 //开始两个物件的画图连接 LineLink(); //开始真正连接:指针连接 RealLink(); } } //关闭连接状态: IsLink=FALSE; } CView::OnLButtonUp(nFlags, point); } n 判断两个元件是否可以连接 BOOL CMyView::IsTwoObjectsCanLink() { //判断两个物件是否能连接 //这两个物件分别由pNodeStart和pNodeCurrent指向 //两个触点分别由startput和currentput标识 //若所指同一物件 if(pNodeStart==pNodeCurrent) { MessageBox("连接错误!自身物件不能相互连接"); return FALSE; } //输出直接结输出 if(startput==Output_1 &¤tput==Output_1) { MessageBox("连接错误!输出端不能相互连接"); return FALSE; } //输入直接连接输入 if( (startput==Input_1 ||startput==Input_2) &&(currentput==Input_1||currentput==Input_2) ) { MessageBox("连接错误!输入端不能相互连接"); return FALSE; } //循环连接 if( (startput==Output_1) &&(currentput==Input_1||currentput==Input_2)) { if(pNodeCurrent->output1==pNodeStart) { MessageBox("连接错误!不能循环连接"); return FALSE; } } if((startput==Input_1||startput==Input_2) &&(currentput==Output_1) ) { if(pNodeStart->output1==pNodeCurrent) { MessageBox("连接错误!不能循环连接"); return FALSE; } } //如果以上情况都不发生,表示可以连接 return TRUE; } 用图来表示上述几种错误:
同一元件不能连接 输出端不能连接输出端 输入端不能连接输入端 两个元件不能循环连接
上位机MFC组合逻辑仿真器源代码下载
n 两个元件的画图连接:LineLink() 该函数调用了recordLine() 代码如下: void CMyView::recordLine() { //记录两个物件之间的连接线经过的关键点 //先动态生成一个数组CArray<CPoint,CPoint>之对象 //记录下连接线的关键点,然后将这个数组对象之地址加入到 //CList<CArray<CPoint,CPoint>*,CArray<CPoint,CPoint>*>MyPointList中 int x0,y0,x1,y1,delta_x,delta_y; //(x0,y0)用于记录输出端起始点坐标 //(x1,y1)用于记录输入端终点坐标 //delta_x,delta_y用于记录x和y的偏移量 //一定是从输出端向输入端画线 if(startput==Output_1) { x0=startpoint.x; y0=startpoint.y; x1=currentpoint.x; y1=currentpoint.y; } else { x1=startpoint.x; y1=startpoint.y; x0=currentpoint.x; y0=currentpoint.y; } delta_x=5; //动态生成数组对象 CArray<CPoint,CPoint>*pPointArray=new CArray<CPoint,CPoint>; //根据点的位置分为三种情况:2个点,4个点,5个点 if(x0<x1) { if(y0==y1) { //两个点情况 pPointArray->Add(CPoint(x0,y0)); pPointArray->Add(CPoint(x1,y1)); } else { //4个点情况 pPointArray->Add(CPoint(x0,y0)); pPointArray->Add(CPoint(x0+delta_x,y0)); pPointArray->Add(CPoint(x0+delta_x,y1)); pPointArray->Add(CPoint(x1,y1)); } } else if(x0==x1) { //两个点情况 pPointArray->Add(CPoint(x0,y0)); pPointArray->Add (CPoint(x1,y1)); } else //x0>x1 { //5个点情况 if(y0<y1) { delta_y=20; } else { delta_y=-20; } pPointArray->Add(CPoint(x0,y0)); pPointArray->Add(CPoint(x0,y0+delta_y)); pPointArray->Add(CPoint(x1-delta_x,y0+delta_y)); pPointArray->Add(CPoint(x1-delta_x,y1)); pPointArray->Add(CPoint(x1,y1)); } //加入当前数组对象地址到MyPointList MyPointList.AddTail (pPointArray); //用数组中的点画线 DrawLinkLine(pPointArray); } 首先保证从输出端向输入端画线,这样可以统一画线操作。 然后动态生成数组: CArray<CPoint,CPoint>*pPointArray=new CArray<CPoint,CPoint>; 用指针pPointArray指向该数组,用于存储连接线的关键点。 连接线根据位置总共有三种线型,如下图所示:
(1)两个关键点的连接线: (2)4个关键点的连接线 (3)5个关键点的连接线 n 两个元件的指针连接:RealLink(); 其代码如下: voidCMyView::RealLink() { //一定是输入连接输出或 输出连接输入 if(startput==Input_1||startput==Input_2) { //输入连接输出 if(startput==Input_1) { pNodeStart->input1=pNodeCurrent; } else { pNodeStart->input2=pNodeCurrent; } pNodeCurrent->output1=pNodeStart; } else//startput==Output_1 { //输出连接输入 pNodeStart->output1=pNodeCurrent; if(currentput==Input_1) { pNodeCurrent->input1=pNodeStart; } else { pNodeCurrent->input2=pNodeStart; } } } 指针连接只有两种情况:输入连接输出和输出连接输入。可以用下图来表示
输入端连接输出端 输出端连接输入端
上位机MFC组合逻辑仿真器源代码下载
|