游戏开发论坛

 找回密码
 立即注册
搜索
查看: 6053|回复: 5

如何检测CPU型号?

[复制链接]

22

主题

40

帖子

44

积分

注册会员

Rank: 2

积分
44
发表于 2007-4-1 17:22:00 | 显示全部楼层 |阅读模式
我想用C写个程序检测一下CPU型号

比如 pentium D core2 AMD系列的CPU等

但我找不到相关资料

哪位知道的请告诉我

谢谢!!!

187

主题

6490

帖子

6491

积分

论坛元老

团长

Rank: 8Rank: 8

积分
6491
发表于 2007-4-1 18:46:00 | 显示全部楼层

Re:如何检测CPU型号?

模拟键盘鼠标消息打开系统属性对话框,截取CPU那一段然后关闭对话框。
(愚人节开个玩笑,高手继续)

86

主题

2251

帖子

2386

积分

金牌会员

Rank: 6Rank: 6

积分
2386
QQ
发表于 2007-4-2 08:53:00 | 显示全部楼层

Re:如何检测CPU型号?

MSDN查cpuid

1万

主题

1万

帖子

2万

积分

管理员

中级会员

Rank: 9Rank: 9Rank: 9

积分
20823
发表于 2007-4-2 09:34:00 | 显示全部楼层

Re:如何检测CPU型号?

// 获取当前机器CPU的处理器名称。
char* GetCPUName(void)
{
        long marker = 0;
        char str[256];
        memset(str, 0, 256);

        char* pStr = str;
        _asm
        {
                mov eax, 0
                cpuid
                mov marker, eax
                mov eax, pStr
                mov [eax], ebx
                add eax, 4
                mov [eax], edx
                add eax, 4
                mov [eax], ecx
                add eax, 4
        }

        return &str[0];
}

8

主题

390

帖子

390

积分

中级会员

Rank: 3Rank: 3

积分
390
发表于 2007-4-2 10:13:00 | 显示全部楼层

Re:如何检测CPU型号?

http://sol-biotech.com/code/SelfModifyingCPUID/SelfModifyingCPUID.c

/*********************************************
    SelfModifyingCPUID.c
    Written by Keith Oxenrider
    koxenrider[at]sol[dash]biotech[dot]com
    January 25, 2005 (my boy is 3 months old today!)

    This code is hereby placed in the public domain.
    No warrenty expressed or implied, use at your own risk!
*********************************************/

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "SelfModifyingCPUID.h"

typedef long (* FunctionLongLong)(long, long);
typedef void (* FunctionVoid)(void);

#define MAX_INSTRUCTION_BYTES 8192 //just to have a large enough buffer

void printBits(int bits2print, int numBits, FILE *fout){
    unsigned int test;
    int i;

    for (i=numBits-1; i>-1; i--){
        test = (unsigned int)pow(2, i);
        if (test & bits2print){
            fprintf(fout, "1");
        }else{
            fprintf(fout, "0");
        }
    }
}

const char *GetBrandID(unsigned char brandID){
    int i;
    for (i=0; i<BrandIDCnt; i++){
        if (BrandIDs == brandID)
            break;
    }
    if (i==BrandIDCnt)
        return BrandStr[BrandIDCnt];
    return BrandStr;
}

void ReportFeatureFlags(unsigned int EDX, unsigned int ECX){
    unsigned int test = 1;
    int i;
   
    printf("\n\nThis processor has the following EDX feature flags:\n");
    for (i=0; i<32; i++){
        if (test & EDX){
            printf("\t%s\n", EDXFeatureFlags);
        }else{
            if (strncmp(EDXFeatureFlags, "Reserved", 8))
                printf("NOT SUPPORTED: %s\n", EDXFeatureFlags);
        }

        if (i==19){
            if (test & EDX)
                boolHasCLFLUSHInstr = TRUE;
        }
        if (i==28){
            if (test & EDX)
                boolIsHyperThreaded = TRUE;
        }
        test <<= 1;
    }


    printf("\n\nThis processor has the following ECX feature flags:\n");
    test = 1;
    for (i=0; i<32; i++){
        if (test & ECX){
            printf("\t%s\n", ECXFeatureFlags);
        }else{
            if (strncmp(ECXFeatureFlags, "Reserved", 8))
                printf("NOT SUPPORTED: %s\n", ECXFeatureFlags);
        }
        test <<= 1;
    }
}


//right out of K&R 2nd ed, p49
unsigned int getbits(unsigned int x, int p, int n){
    return (x >> (p+1-n)) & ~(~0 << n);
}

void ReportProcessorSig(unsigned int EAX){
    unsigned int ExFam,ExMod,Type,Family,Model,Stepping, tmp=EAX;
    ExFam=ExMod=Type=Family=Model=Stepping=0;

    Stepping = getbits(EAX, 3, 4);
    Model = getbits(EAX, 7, 4);
    Family = getbits(EAX, 11, 4);
    Type = getbits(EAX, 13, 2);
    ExMod = getbits(EAX, 19, 4);
    ExFam = getbits(EAX, 27, 8);

    printf("\n\nProcessor Signature:");
    printf("(bin EAX: "); printBits(tmp, 32, stdout); printf(")");
    printf("(hex EAX: %08X)", tmp);
    printf("\n");
    printf("\tStepping: "); printBits(Stepping, 4, stdout); printf("\n");
    printf("\tModel: "); printBits(Model, 4, stdout); printf("\n");
    printf("\tFamily: "); printBits(Family, 4, stdout); printf("\n");
    printf("\tType: "); printBits(Type, 2, stdout); printf("\n");
    printf("\tExMod: "); printBits(ExMod, 4, stdout); printf("\n");
    printf("\tExFam: "); printBits(ExFam, 8, stdout); printf("\n");

}


void ReportCacheTLB(unsigned int EAX, unsigned int EBX,
                    unsigned int ECX, unsigned int EDX,
                    BOOL skipByte){
    int i,j,k;
    unsigned char *chrPtr;

    printf("\n\nCache and TLB information:\n");
    for (k=0; k<4; k++){
        if (k==0)
            chrPtr = (unsigned char *)&EAX;
        if (k==1)
            chrPtr = (unsigned char *)&EBX;
        if (k==2)
            chrPtr = (unsigned char *)&ECX;
        if (k==3)
            chrPtr = (unsigned char *)&EDX;
        for (i=0; i<4; i++){
            if (skipByte && i == 0 && k == 0) continue;//least significant byte is count first time around
            if (chrPtr == 0) continue;
            for (j=0; j<CacheTLBCnt; j++){
                if (CacheTLB[j].val == chrPtr) break;
            }
            if (j==CacheTLBCnt){
                printf("Unsupported value for 0x%02X\n", chrPtr);
            }else{
                printf("\t%s\n", CacheTLB[j].descr);
            }
        }
    }

}

/*******************************
*
*
*  writeCPUIDInstructions
*
*
*******************************/

unsigned int writeCPUIDInstructions(unsigned char *codePtr, unsigned char *typPtr,
                 int memptr, unsigned int *destArr){
    unsigned int i, j;
    unsigned char *destPtr = (unsigned char *) &destArr;
    unsigned char reg = 0xC3;

//fprintf(stderr, "Inside writeCPUIDInstructions\n");

    codePtr[memptr++] = 0x55; // push ebp (stack pointer)
    codePtr[memptr++] = 0xB8; // mov type to eax (which kind of CPUID instruction
    for (i=0; i<4; i++)   // load the four bytes of type
        codePtr[memptr++] = typPtr;
    codePtr[memptr++] = 0x0F; // execute the CPUID instruction
    codePtr[memptr++] = 0xA2;

    //now we want to copy the register values to a fixed memory location
    //so we can easily process them after the return
    //normally this data is left on the stack
    for (i=0; i<4; i++){
        codePtr[memptr++] = 0xA3; // mov contents of EAX to memory location
        for (j=0; j<4; j++)   // four bytes of memory location (this won't work on a 64 bit machine!)
            codePtr[memptr++] = destPtr[j];
        codePtr[memptr++] = 0x8B; //mov other register to EAX,
        codePtr[memptr++] = reg--;//order: ebx (C3H), edx (C2H), ecx (C1H)
        destArr++;//increment pointer to new memory address
    }
    codePtr[memptr++] = 0x5D; // pop ebp (restore stack pointer)
    codePtr[memptr++] = 0xC3; // ret eax (return value is ignored, data already stored)

    return memptr;
}

/*******************************
*
*
*  writeSUMInstructions
*
*
*******************************/

unsigned int writeSUMInstructions(unsigned char * chrArr, int arrPtr){

    chrArr[arrPtr++] = 0x55; // push ebp
    chrArr[arrPtr++] = 0x8B; // mov ebp, esp
    chrArr[arrPtr++] = 0xEC;
    chrArr[arrPtr++] = 0x8B; // mov eax,[bp+8]
    chrArr[arrPtr++] = 0x45;
    chrArr[arrPtr++] = 0x08;
    chrArr[arrPtr++] = 0x03; // add eax,[bp+12]
    chrArr[arrPtr++] = 0x45;
    chrArr[arrPtr++] = 0x0C;
    chrArr[arrPtr++] = 0x5D; // pop ebp
    chrArr[arrPtr++] = 0xC3; // ret eax

    return arrPtr;
}



/*******************************
*
*
*  main
*
*
*******************************/

int main(){
    unsigned char *chrArr;
    unsigned int i, j, k, cnt, arg;
    unsigned int maxCPUIDCycles=0;
    FunctionVoid ExeCPUID;
    BOOL doSums = FALSE;
    BOOL doBrandID = TRUE;
    BOOL doCPUID = TRUE;
    BOOL showRegisters = FALSE;
    unsigned int data[4*MAXCPUIDTESTS];//for storing eax, ebx, edx, ecx from CPUID
    char *ptrTmp;


    chrArr = (unsigned char*) malloc(MAX_INSTRUCTION_BYTES);
    if (!chrArr){
        fprintf(stderr, "Failed to allocate memory for instruction buffer!\n");
        exit(1);
    }

    if (doSums){//a couple of sums, just for the heck of it (and what the original code had)...
        FunctionLongLong ComputeSum1, ComputeSum2;
        unsigned int val1, val2;
        unsigned int retVal1, retVal2;

        ComputeSum1 = (FunctionLongLong) chrArr;
        cnt = 0;
        cnt = writeSUMInstructions(chrArr, cnt);

        ComputeSum2 = (FunctionLongLong) &chrArr[cnt];
        cnt = writeSUMInstructions(chrArr, cnt);

        val1 = 123456;
        val2 = 654321;
        retVal1 = ComputeSum1(val1, val2);
        printf("Using ComputSum1, result for adding %d and %d is %d\n", val1, val2, retVal1);

        val1 = 7890;
        val2 = 9876;
        retVal2 = ComputeSum2(val1, val2);
        printf("Using ComputSum2, result for adding %d and %d is %d\n", val1, val2, retVal2);

        val1 = 1999;
        val2 = 2003;
        retVal1 = ComputeSum1(val1, val2);
        printf("Reusing ComputSum1, result for adding %d and %d is %d\n", val1, val2, retVal1);

    }

    if (doBrandID){
        //test for &quotrocessor Brand String Feature"
        ExeCPUID = (FunctionVoid) chrArr;
        arg = 0x80000000;
        cnt=0;
        cnt = writeCPUIDInstructions(chrArr, (unsigned char *)&arg, cnt, data);
        ExeCPUID();
        if (data[0] >= 0x80000004){
            //feature is supported
            char buf[50];
            int bufptr=0;
            int dataBlockPtr=0;
            char *datptr = (char *) &data;
            BOOL IsIntialSpace = TRUE;

            j=0;
            for (i=2;i<5;i++){//current documentation only supports 2, 3, and 4
                ExeCPUID = (FunctionVoid) chrArr;
                arg = 0x80000000+i;
                cnt=0;
                cnt = writeCPUIDInstructions(chrArr, (unsigned char *)&arg, cnt, &data[dataBlockPtr*4]);
                ExeCPUID();

                //this block of nastiness is because it appears that Intel
                //doesn't want to make this easy (or maybe it was the way I copied
                //the registers)
                ptrTmp = (char *)&data[dataBlockPtr*4];
                for (j=0; j<4; j++){
                    if (IsIntialSpace && ptrTmp[(0*4)+j] == ' ') continue;
                    IsIntialSpace = FALSE;
                    buf[bufptr++] = ptrTmp[(0*4)+j];
                }
                for (j=0; j<4; j++){
                    if (IsIntialSpace && ptrTmp[(0*4)+j] == ' ') continue;
                    IsIntialSpace = FALSE;
                    buf[bufptr++] = ptrTmp[(1*4)+j];
                }
                for (j=0; j<4; j++){
                    if (IsIntialSpace && ptrTmp[(0*4)+j] == ' ') continue;
                    IsIntialSpace = FALSE;
                    buf[bufptr++] = ptrTmp[(3*4)+j];
                }
                for (j=0; j<4; j++){
                    if (IsIntialSpace && ptrTmp[(0*4)+j] == ' ') continue;
                    IsIntialSpace = FALSE;
                    buf[bufptr++] = ptrTmp[(2*4)+j];
                }
                dataBlockPtr++;
            }
            buf[bufptr++] = '\0';
            if (showRegisters){
                for (i=0; i<3; i++){
                    printf("\tEAX: %08X\n", data[(i*4)+0]);
                    printf("\tEBX: %08X\n", data[(i*4)+1]);
                    printf("\tEDX: %08X\n", data[(i*4)+2]);
                    printf("\tECX: %08X\n", data[(i*4)+3]);
                }
                ptrTmp = (char *)&data;
                printf("[");
                for (i=0; i<48; i++){
                    printf("%c", ptrTmp);
                    if ((i+1)%4 == 0 && i < 47)
                        printf("]\n[");
                }
                printf("]\n");
            }
            printf("Processor Brand String Feature = '%s'\n", buf);
        }else{
            printf("'Processor Brand String Feature' is not supported\n");
        }
    }



    if (doCPUID){
        int dataBlockPtr=0;
        printf("\nNow we look at the output from the CPUID instruction...\n\n");
        i=0;
        ExeCPUID = (FunctionVoid) chrArr;
        do{
            cnt=0;
            cnt = writeCPUIDInstructions(chrArr, (unsigned char *)&i, cnt, data);
//            fprintf(stderr, "Wrote %d bytes for CPUID type %d\n", cnt, i);

            ExeCPUID();
            if (showRegisters){
                printf("CPUID output when EAX = %08X\n", i);
                printf("\tEAX: %08X\n", data[0]);
                printf("\tEBX: %08X\n", data[1]);
                printf("\tEDX: %08X\n", data[2]);
                printf("\tECX: %08X\n", data[3]);
            }

            if (i==0){
                maxCPUIDCycles = data[0];
                printf("CPU Vendor ID: '");
                for (j=1; j<4; j++){
                    ptrTmp = (char *)&data[j];
                    for (k=0; k<4; k++)
                        printf("%c", ptrTmp[k]);
                }
                printf("'\n\tNumber of CPUID EAX tests: %d\n", maxCPUIDCycles+1);
            }
            if (i==1){
                unsigned char APIC_ID, COUNT, CHUNKS;

                ptrTmp = (char *)&data[1];
                APIC_ID = ptrTmp[3];
                COUNT = ptrTmp[2];
                CHUNKS = ptrTmp[1];

                printf("The Brand ID (0x%02X) is %s\n", ptrTmp[0], GetBrandID(ptrTmp[0]));
                ReportFeatureFlags(data[2], data[3]);
                ReportProcessorSig(data[0]);

                if (boolIsHyperThreaded){
                    printf("The number of logical processors (Hyperthreading): %d\n", COUNT);
                }
                if (boolHasCLFLUSHInstr){
                    printf("The CLFLUSH size: %d\n", CHUNKS);
                    printf("\tThe cache line size: %d\n", CHUNKS*8);
                }
            }
            if (i==2){
                ReportCacheTLB(data[0], data[1], data[2], data[3], TRUE);
                //this hasn't been tested 'cuz my machine doesn't support it
                ptrTmp = (char *)&data[0];
                for (k=1; k<(*ptrTmp); k++){
                    cnt=0;
                    cnt = writeCPUIDInstructions(chrArr, (unsigned char *)&i, cnt, data);
                    ExeCPUID();
                    if (showRegisters){
                        printf("CPUID output when EAX = %08X\n", i);
                        printf("\tEAX: %08X\n", data[0]);
                        printf("\tEBX: %08X\n", data[1]);
                        printf("\tEDX: %08X\n", data[2]);
                        printf("\tECX: %08X\n", data[3]);
                    }
                    ReportCacheTLB(data[0], data[1], data[2], data[3], FALSE);
                }
            }
            i++;
            for (j=0; j<4; j++) data[j] = 0;
        }while (i<(maxCPUIDCycles+1) && i<4);//documentation only supports up to 4
    }
    free(chrArr);
    return 0;
}

8

主题

390

帖子

390

积分

中级会员

Rank: 3Rank: 3

积分
390
发表于 2007-4-2 10:14:00 | 显示全部楼层

Re:如何检测CPU型号?

http://www.gamedev.net/community/forums/topic.asp?topic_id=438752

#ifndef PROCESSOR_INCLUDED
#define PROCESSOR_INCLUDED

#include <string>

namespace hw
{

    class Processor
    {
    public:
        Processor(void);

        bool HasSSE(void) const
        {
            return has_sse;
        }

        bool HasSSE2(void) const
        {
            return has_sse2;
        }

        bool HasSSE3(void) const
        {
            return has_sse3;
        }

        bool HasMMX(void) const
        {
            return has_mmx;
        }

        bool HasMMXExt(void) const
        {
            return has_mmx_ext;
        }

        bool Has3DNow(void) const
        {
            return has_3dnow;
        }

        bool Has3DNowExt(void) const
        {
            return has_3dnow_ext;
        }

        bool IsHTT(void) const
        {
            return is_htt;
        }

        std::string GetName(void) const
        {
            return cpu_name;
        }

        std::string GetVendorID(void) const
        {
            return cpu_vendor;
        }

        std::string GetVendorName(void) const
        {
            if (cpu_vendor == "AuthenticAMD")
            {
                return std::string("AMD");
            }
            else if (cpu_vendor == "GenuineIntel")
            {
                return std::string("Intel");
            }
            else if (cpu_vendor == "CyrixInstead")
            {
                return std::string("Cyrix");
            }
            else if (cpu_vendor == "CentaurHauls")
            {
                return std::string("Centaur");
            }
            else if (cpu_vendor == "RiseRiseRise")
            {
                return std::string("Rise");
            }
            else if (cpu_vendor == "GenuineTMx86")
            {
                return std::string("Transmeta");
            }
            else if (cpu_vendor == "UMC UMC UMC ")
            {
                return std::string("UMC");
            }
            else
            {
                return cpu_vendor;
            }
        }

        unsigned long GetSpeed(void) const
        {
            return cpu_speed;
        }

        int GetStepping(void) const
        {
            return stepping;
        }

        int GetModel(void) const
        {
            return model;
        }

        int GetFamily(void) const
        {
            return family;
        }

        int GetType(void) const
        {
            return type;
        }

        int GetExtModel(void) const
        {
            return ext_model;
        }

        int GetExtFamily(void) const
        {
            return ext_family;
        }

    private:

        bool has_mmx;
        bool has_mmx_ext;
        bool has_3dnow;
        bool has_3dnow_ext;
        bool has_sse;
        bool has_sse2;
        bool has_sse3;
        bool is_htt;

        int stepping;
        int model;
        int family;
        int type;
        int ext_model;
        int ext_family;

        std::string cpu_name;
        std::string cpu_vendor;

        unsigned long cpu_speed;

        unsigned __int64 GetTSC()
        {
            __asm
            {
                    rdtsc
            }
        }

        void MeasureSpeed(void);

        void DoCPUID(void);
    };
}

#endif


#include <string>
#include <windows.h>
#include &quotrocessor.h"

using namespace std;

namespace hw
{

    Processor:rocessor(void) :
        has_mmx(false),
        has_mmx_ext(false),
        has_3dnow(false),
        has_3dnow_ext(false),
        has_sse(false),
        has_sse2(false),
        has_sse3(false),
        is_htt(false),
        stepping(0),
        model(0),
        family(0),
        type(0),
        ext_model(0),
        ext_family(0),
        cpu_name("(unknown)"),
        cpu_vendor("(unknown)"),
        cpu_speed(0)
    {
        MeasureSpeed();
        DoCPUID();
    }

    void Processor::MeasureSpeed(void)
    {
        LARGE_INTEGER    s, e, freq;
        unsigned __int64 cs, ce;

        // Determine timer frequency.
        QueryPerformanceFrequency(&freq);

        QueryPerformanceCounter(&s);
        cs = GetTSC();

        // Wait for a while...
        for (volatile long i = 0; i < 1000000; ++i) ;

        ce = GetTSC();
        QueryPerformanceCounter(&e);

        // Calculate frequency.
        cpu_speed = (unsigned long) ((ce - cs) * freq.QuadPart / (e.QuadPart - s.QuadPart));
    }

    // this is the "core" method - it will cal CPUID instruction and parse output
    void Processor:oCPUID(void)
    {
        char cpu_name_string[49] = {0};  // max 48 chars + terminating 0
        char cpu_vendor_id_string[13] = {0}; // max 12 chars + terminating 0
        unsigned int cpu_feat_eax = 0;
        unsigned int cpu_feat_edx = 0;
        unsigned int cpu_feat_ecx = 0;
        unsigned int cpu_feat_ext_edx = 0;

        __asm
        {
            mov     eax, 0x00000000              // first CPUID function, always supported (on reasonable cpu)
            cpuid                                // get info
            mov     DWORD PTR [cpu_vendor_id_string + 0], ebx  // copy vendor id string
            mov     DWORD PTR [cpu_vendor_id_string + 4], edx
            mov     DWORD PTR [cpu_vendor_id_string + 8], ecx
            test    eax, eax                     
            jz      no_features                  // if eax is 0, no info will be available

            mov     eax, 0x00000001              // get extended info about cpu
            cpuid
            mov     [cpu_feat_eax], eax          // store data for later processing
            mov     [cpu_feat_edx], edx
            mov     [cpu_feat_ecx], ecx

            mov     eax, 0x80000000              // first extended function
            cpuid

            // now test which extended functions are supported
            cmp     eax, 0x80000001              // is eax < 0x80000001
            jb      no_features                  // yes -> jump to no_features label
            cmp     eax, 0x80000004              // is eax < 0x80000004
            jb      ext_feats_only               // yes -> jump to ext_feats_only label

            // now get name of the cpu
            mov     eax, 0x80000002
            cpuid
            mov     DWORD PTR [cpu_name_string + 0], eax
            mov     DWORD PTR [cpu_name_string + 4], ebx
            mov     DWORD PTR [cpu_name_string + 8], ecx
            mov     DWORD PTR [cpu_name_string + 12], edx

            mov     eax, 0x80000003
            cpuid
            mov     DWORD PTR [cpu_name_string + 16], eax
            mov     DWORD PTR [cpu_name_string + 20], ebx
            mov     DWORD PTR [cpu_name_string + 24], ecx
            mov     DWORD PTR [cpu_name_string + 28], edx

            mov     eax, 0x80000004
            cpuid
            mov     DWORD PTR [cpu_name_string + 32], eax
            mov     DWORD PTR [cpu_name_string + 36], ebx
            mov     DWORD PTR [cpu_name_string + 40], ecx
            mov     DWORD PTR [cpu_name_string + 44], edx

        ext_feats_only:
            // get extended features
            mov     eax, 0x80000001
            cpuid
            mov     [cpu_feat_ext_edx], edx

        no_features:
            // done
        } // __asm

        // now process data we got from cpu
        cpu_name = string(cpu_name_string);
        cpu_vendor = string(cpu_vendor_id_string);

        stepping = cpu_feat_eax & 0xF;
        model = (cpu_feat_eax >> 4) & 0xF;
        family = (cpu_feat_eax >> 8) & 0xF;
        type = (cpu_feat_eax >> 12) & 0x3;
        ext_model = (cpu_feat_eax >> 16) & 0xF;
        ext_family = (cpu_feat_eax >> 20) & 0xFF;

        has_mmx = (cpu_feat_edx >> 23) & 0x1;
        has_sse = (cpu_feat_edx >> 25) & 0x1;
        has_sse2 = (cpu_feat_edx >> 26) & 0x1;
        is_htt = (cpu_feat_edx >> 28) & 0x1;

        has_sse3 = cpu_feat_ecx & 0x1;

        has_mmx_ext = (cpu_feat_ext_edx >> 22) & 0x1;
        has_3dnow = (cpu_feat_ext_edx >> 31) & 0x1;
        has_3dnow_ext = (cpu_feat_ext_edx >> 30) & 0x1;
    }
}


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2026-4-13 00:41

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表