遊戲修改:天翔記

自從國中被我兩個同學推入「三國志」的無底洞以後,「三國志」和「信長之野望」就一直是我主要玩的遊戲。每一代都有其特色和精華,當然也有一些我討厭的機制。但大體來說我都會花時間下去玩(而且還會全破不少次),除了 ... 天翔記。

我本人是個種田派和公仔收集派的玩家,很遺憾的,「天翔記」是一款窮兵黷武的遊戲,這實在讓我很難上手。遊戲試圖要表現出「成長」的概念,這很好!但是到遊戲裏面就會發現 ... 一個剛出生的名將和久經戰陣的凡將是根本無法比擬的,這讓「天才」這兩個字在遊戲中基本上是不可能出現的。我認同能力不會一開始就全滿而要成長,但你差距也不可以高到這種程度吧!這樣完全不可能有名將的成長空間(因為在年輕時就被幹掉了)。此外,電腦的成長速度根本是用作弊來形容!我自己的經驗,當我用島津家努力違反自己的天性,一天到晚作戰時,卻發現「立花道雪」這老怪物的能力老早就上升的比我快30點,而且我主力培養的還只有義弘一個人而已 ... 經過一番奮戰,總算擊退道雪,天曉得下一季又再一次看到他,能力還比之前更高,我島津義弘都沒成長這麼快啊 ... 於是我關檔了。

可惜沒有威力加強版... 沒有就算了,那就自己來寫一個存檔修改器吧,當中也查了不少格式的資料,最後玩成品記載在下面:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

struct nb6man
{
    unsigned short dummy;
    unsigned char p_max;
    unsigned char b_max;
    unsigned char w_max;
    unsigned short p_real;
    unsigned short b_real;
    unsigned short w_real;
    unsigned char charming;
    unsigned char ambition;
    unsigned char loyalty;
    unsigned short merit;
    unsigned char blood;
    unsigned short attach;
    unsigned short gundan;
    unsigned short  castle;
    unsigned short rise;
    unsigned char soldier;
    unsigned char training;
    unsigned char solloyal;
    unsigned char form;
    unsigned char workyear;
    unsigned char spouse;
    unsigned char birth;//-1454
    unsigned short parents;
    unsigned short dummy2;
    unsigned char aptitude;  //00aa bbcc
    unsigned char tech;
    unsigned char job;
    unsigned short soldier_type;
    unsigned char option1; // aabbbccc, aa:independent, bbb:  ferocious, ccc: max_year
    unsigned char option2;
    unsigned char dummy4[4];
}__attribute__ ((__packed__));

struct nb6castle
{
    unsigned char dummy[17];
    unsigned short id;
    unsigned char hp;
    unsigned short farm;
    unsigned char bussiness;
    unsigned short  men;
    unsigned char loyalty;
    unsigned short soldier;
    unsigned char dummy2[5];
}__attribute__ ((__packed__));

struct nb6castlemax
{
    unsigned short dummy;
    unsigned char farm; // *10
    unsigned char bussiness;
    unsigned int dummy2;
}__attribute__ ((__packed__));

#define SABER 0x0007
#define RIDER 0x0038
#define GUNNER 0x01C0
#define NAVI 0x0E00
#define RATIO 0.7
               
static void util_forAllMen( char *, short, int );
static void util_cheat( struct nb6man *, int ); 
static long util_getFdSize( int );
static void util_printMan( struct nb6man * );
static void util_printMan2( struct nb6man * );

static void util_forAllCastle( char *, short, int );
static void util_cheat_castle( struct nb6castle *, struct nb6castlemax * );
static void util_printCastle( struct nb6castle *, struct nb6castlemax * );

static void util_find_man_cheat( char *, int, int, int );
static void util_find_man( char *, int, int, int );

int main( int argc, char *argv[] )
{
    int    i = 0;
    char   input[20];
    char   saveData[8];
    char   running = 1;
    int    a1 = 0, a2 = 0, a3 = 0;
    char   *delim = " ";
    
    int    fd = open( "SAVEDAT.NB6", O_RDWR );
    long    fileSize = 0;
    
    char   *_pFile = NULL;
    
    if( fd < 0 )
    {
        printf( "File open failed!\n" );
        return;
    }
    
    fileSize = util_getFdSize( fd );
    
    if( fileSize == -1 )
    {
        return;
    }
        
    _pFile = mmap( 0, fileSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
    _pFile += 11; // Offset
    
    printf( "Save Data:\n" );
    
    for( i = 0 ; i < 8; i++ )
    {
        printf( "%d: ", i );
        
        if( _pFile[i*2] == 1 )
        {
            printf( "%d.\n", 1454 + *( (short *)( _pFile + 4 + 16 + 158 * i ) ) );
            saveData[i] = 1;
        }
        else
        {
            printf( "None.\n" );
            saveData[i] = 0;
        }
    }
    
    _pFile += 4; // NULL
    _pFile += 16;  // Save Count
    _pFile += 158 * 8; // Save Meta Data
    
    printf( "Please Select the Save Data you want to cheat: " );
    memset( input, 0, 20 );
    fgets( input, 20, stdin );
    
    if( atoi( input ) > 7 || atoi( input ) < 0 )
    {
        printf( "Wrong range.\n" );
        running = 0;
    }
    else if( saveData[atoi( input )] == 1 )
    {
        _pFile += ( 93758 * atoi( input ) ) ;
        printf( "%d %d.\n", atoi( input ), 1454 + *( (short *)( _pFile ) ) );
    }
    else
    {
        printf( "No Save Data.\n" );
        running = 0;
    }
    
    while( running )
    {
        printf( "Options:\n" );
        printf( "    1: Men.\n" );
        printf( "    2: Men without soldiers\n" );
        printf( "    3: Print men.\n" );
        printf( "    4: Castle.\n" );
        printf( "    5: Castle Max.\n" );
        printf( "    6: Print castle.\n" );
        printf( "    7: Cheat on the target man.\n" ); 
        printf( "    8: Search.\n" );
        printf( "    9: Exit.\n" );
        printf( "Your choice: " );
        
        memset( input, 0, 20 );
        
        fgets( input, 20, stdin );
        
        if( atoi( input ) == 1 )
        {
            printf( "Please enter your country code: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            // printf( "country code: %d\n", atoi( input ) );
            util_forAllMen( _pFile, (short)atoi( input ), 1 );
        }
        else if( atoi( input ) == 2 )
        {
            printf( "Please enter your country code: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            // printf( "army group code: %d\n", atoi( input ) );
            util_forAllMen( _pFile, (short)atoi( input ), 0 );
        }
        else if( atoi( input ) == 3 )
        {
            printf( "Please enter your country code: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            // printf( "army group code: %d\n", atoi( input ) );
            util_forAllMen( _pFile, (short)atoi( input ), 2 );
        }
        else if( atoi( input ) == 4 )
        {
            printf( "Please enter your army group code: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            // printf( "army group code: %d\n", atoi( input ) );
            util_forAllCastle( _pFile, (short)atoi( input ), 0 );
        }
        else if( atoi( input ) == 5 )
        {
            printf( "Please enter your army group code: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            // printf( "army group code: %d\n", atoi( input ) );
            util_forAllCastle( _pFile, (short)atoi( input ), 1 );
        }
        else if( atoi( input ) == 6 )
        {
            printf( "Please enter your army group code: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            // printf( "army group code: %d\n", atoi( input ) );
            util_forAllCastle( _pFile, (short)atoi( input ), 2 );
        }
        else if( atoi( input ) == 7 )
        {
            printf( "Please enter the three abilities of the target man: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            a1 = atoi( strtok( input, delim ) );
            a2 = atoi( strtok( NULL, delim ) );
            a3 = atoi( strtok( NULL, delim ) );
            // printf( "%d %d %d\n", a1, a2, a3 );
            util_find_man_cheat( _pFile, a1/2, a2/2, a3/2 );
        }
        else if( atoi( input ) == 8 )
        {
            printf( "Please enter the three abilities of the target man: " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            a1 = atoi( strtok( input, delim ) );
            a2 = atoi( strtok( NULL, delim ) );
            a3 = atoi( strtok( NULL, delim ) );
            // printf( "%d %d %d\n", a1, a2, a3 );
            util_find_man( _pFile, a1/2, a2/2, a3/2 );
        }
        else if( atoi( input ) == 9 )
        {
            running = 0;
        }
        else
        {
            printf( "Wrong input!!\n" );
        }
    }
    
    /*
    if( argc == 2 )
    {
        util_forAllMen( "SAVEDAT.NB6", (short)atoi( argv[1] ) );
        return 0;
    }
    else if( argc == 3 )
    {
        util_forAllMen( "SAVEDAT.NB6", (short)atoi( argv[1] ) );
        util_forAllCastle( "SAVEDAT.NB6", (short)atoi( argv[2] ) );
        return 0;
    }
    
    printf( "Usage: cheat  \n" );
    */
    munmap( _pFile, fileSize );
    close( fd );
    
    return 0;
}

static void util_forAllMen( char *_pFile, short _id, int _soldier_flag )
{
    char *_pManStart = NULL;
    struct nb6man *_pMan = NULL;
    int  i = 0, counter = 1;
    
    //_pManStart = _pFile + 0x0FF5;
    _pManStart = _pFile + 0x0AE6;

    for( i = 0 ; i < 500 ; i++ )
    {
        _pMan = ( struct nb6man * )( _pManStart + ( i * 47 ) );
        
        //if( _pMan -> p_max + _pMan -> b_max + _pMan -> w_max >= 280 )
        if( _pMan -> attach == _id ) 
        {
            printf( "%d. \n", counter );
            util_printMan( _pMan );
            util_printMan2( _pMan );
            
            if( _soldier_flag != 2 )
            {
                util_cheat( _pMan, _soldier_flag );
                util_printMan( _pMan );
                util_printMan2( _pMan );
            }
            counter++;
        }
    }
    
    return;
}

static void util_cheat( struct nb6man *_pMan, int _soldier_flag )
{
    int saber = _pMan -> soldier_type & SABER;
    int rider = ( _pMan -> soldier_type & RIDER ) >> 3;
    int gunner = ( _pMan -> soldier_type & GUNNER ) >> 6;
    
    
    if( ( RATIO * 2000 ) > _pMan -> p_real )
    {
        _pMan -> p_real = 2000 * RATIO;
    }
    else if ( _pMan -> p_real > 1950 )
    {
        _pMan -> p_real = 2000;
    }
    
    if( ( 2000 * RATIO ) > _pMan -> b_real )
    {
        _pMan -> b_real = 2000 * RATIO;
    }
    else if( _pMan -> b_real > 1950 )
    {
        _pMan -> b_real = 2000;
    }
    
    if( ( 2000 * RATIO ) > _pMan -> w_real )
    {
        _pMan -> w_real = 2000 * RATIO;
    }
    else if( _pMan -> w_real > 1950 )
    {
        _pMan -> w_real = 2000;
    }
    
    if( _pMan -> merit >= 900 )
    {
        _pMan -> soldier = 100;
    }
    else if( _pMan -> merit >= 600 )
    {
        _pMan -> soldier = 75;
    }
    else if( _pMan -> merit >= 350 )
    {
        _pMan -> soldier = 55;
    }
    else if( _pMan -> merit >= 150 )
    {
        _pMan -> soldier = 40;
    }
    else
    {
        _pMan -> soldier = 30;
    }
    
    if( _soldier_flag == 1 )
    {
        _pMan -> loyalty = 100;
        _pMan -> training = 200;
        _pMan -> solloyal = 100;
    
        if( _pMan -> soldier_type & 0x2000 )
        {
            _pMan -> form = 3;
        }
        else if( gunner >= saber && gunner >= rider )
        {
            _pMan -> form = 2;
        }
        else if( rider >= saber )
        {
            _pMan -> form = 1;
        }
        else
        {
            if( saber <= 2 )
            {
                _pMan -> form = 2;
            }
            else
            {
                _pMan -> form = 0;
            }
        }
    
        _pMan -> soldier_type = _pMan -> soldier_type | 0x5000;
    }
    
    return;
}

static void util_printMan( struct nb6man *_pMan )
{
    if( _pMan -> dummy != 0xFFFF )
    {
        printf( "Error!!\n" );
        return;
    }
    
    printf( "%d/%d ", _pMan -> p_real * _pMan -> p_max / 1000, _pMan -> p_max * 2 );
    printf( "%d/%d ", _pMan -> b_real * _pMan -> b_max / 1000, _pMan -> b_max * 2 );
    printf( "%d/%d ", _pMan -> w_real * _pMan -> w_max / 1000, _pMan -> w_max * 2 );
    
    printf( "%d %d %d ", _pMan -> charming, _pMan -> ambition, _pMan -> loyalty );
    printf( "%d %d %d ", _pMan -> soldier, _pMan -> training, _pMan -> solloyal );
    
    if( _pMan -> aptitude % 4 == 0 )
    {
        printf( "C" );
    }
    else if ( _pMan -> aptitude % 4 == 1 )
    {
        printf( "B" );
    }
    else
    {
        printf( "A" );
    }
    
    if( ( _pMan -> aptitude / 4 ) % 4 == 0 )
    {
        printf( "C" );
    }
    else if ( ( _pMan -> aptitude / 4 ) % 4 == 1 )
    {
        printf( "B" );
    }
    else
    {
        printf( "A" );
    }
    
    if( ( _pMan -> aptitude / 16 ) % 4 == 0 )
    {
        printf( "C" );
    }
    else if ( ( _pMan -> aptitude / 16 ) % 4 == 1 )
    {
        printf( "B" );
    }
    else
    {
        printf( "A" );
    }
    
    printf( " " );
    
    printf( "%d%d%d%d ", _pMan -> soldier_type & SABER, 
                         ( _pMan -> soldier_type & RIDER ) >> 3,
                         ( _pMan -> soldier_type & GUNNER ) >> 6,
                         ( _pMan -> soldier_type & NAVI ) >> 9 );
    
    printf( "\n" );
    
    return;
}

static void util_printMan2( struct nb6man *_pMan )
{
    unsigned char *_pPtr = (char *)_pMan;
    int i = 0;
    
    for( i = 0 ; i < 47 ; i++ )
    {
        printf( "%02X ", _pPtr[i] );
    }
    
    printf( "\n" );
    
    return;
}

static long util_getFdSize( int fd )
{
    struct stat statbuf;
    
    if( fstat( fd, &statbuf ) < 0 )
    {
        close( fd );
        return -1;
    }
    
    return statbuf.st_size;
}

static void util_forAllCastle( char *_pFile, short _id, int _max )
{
    char *_pCastleStart = NULL;
    struct nb6castle *_pCastle = NULL;
    struct nb6castlemax *_pCastleMax = NULL;
    int  i = 0, counter = 1;
    
    //_pCastleStart = _pFile + 0x9705;
    _pCastleStart = _pFile + 0x91F6;

    for( i = 0 ; i < 214 ; i++ )
    {
        _pCastle = ( struct nb6castle * )( _pCastleStart + ( i * 33 ) );
        _pCastleMax = ( struct nb6castlemax * )( _pCastleStart + ( 33 * 214 ) + ( i * 8 ) );
        
        if( _pCastle -> id == _id ) 
        {
            printf( "%d.\n", counter );
            
            if( _max == 2 )
            {
                util_printCastle( _pCastle, _pCastleMax );
            }
            
            if( _max == 1 )
            {
                _pCastleMax -> farm = 0xFF;
                _pCastleMax -> bussiness = 0xFF;
            }
            
            util_cheat_castle( _pCastle, _pCastleMax );
            counter++;
        }
    }
    
    return;
}

static void util_cheat_castle( struct nb6castle *_pCastle, struct nb6castlemax *_pCastleMax )
{
    _pCastle -> farm = _pCastleMax -> farm * 10;
    _pCastle -> bussiness = _pCastleMax -> bussiness;
    _pCastle -> hp = 250;
    _pCastle -> loyalty = 100;
    
    if( _pCastle -> soldier < 500 )
    {
        _pCastle -> soldier = 500;
    }
    
    return;
}

static void util_printCastle( struct nb6castle *_pCastle, struct nb6castlemax *_pCastleMax )
{
    printf( "%d/%d ", _pCastle -> farm, ( _pCastleMax -> farm * 10 ) );
    printf( "%d/%d ", _pCastle -> bussiness, _pCastleMax -> bussiness ); 
    printf( "%d %d ", _pCastle -> hp, _pCastle -> loyalty );
    printf( "%d(%d)", _pCastle -> men + _pCastle -> soldier, _pCastle -> soldier ); 
        
    printf( "\n" );
    
    return;
}

static void util_find_man( char *_pFile, int _a1, int _a2, int _a3 )
{
    char *_pManStart = NULL;
    struct nb6man *_pMan = NULL;
    int  i = 0;
    
    _pManStart = _pFile + 0x0AE6;

    for( i = 0 ; i < 500 ; i++ )
    {
        _pMan = ( struct nb6man * )( _pManStart + ( i * 47 ) );
        
        if( _pMan -> p_max == _a1 && _pMan -> b_max == _a2 && _pMan -> w_max == _a3 ) 
        {
            printf( "country code: %d\n", _pMan -> attach );
            printf( "army group code: %d\n", _pMan -> gundan );
        }
    }
    
    return;
}

static void util_find_man_cheat( char *_pFile, int _a1, int _a2, int _a3 )
{
    char *_pManStart = NULL;
    struct nb6man *_pMan = NULL;
    int  i = 0;
    char input[20];
    unsigned char value1 = 0;
    unsigned short value2 = 0, tmp = 0;
    
    _pManStart = _pFile + 0x0AE6;

    for( i = 0 ; i < 500 ; i++ )
    {
        _pMan = ( struct nb6man * )( _pManStart + ( i * 47 ) );
        
        if( _pMan -> p_max == _a1 && _pMan -> b_max == _a2 && _pMan -> w_max == _a3 ) 
        {
            printf( "Please enter the politic ability (max:200): " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) < 201 )
            {
                value1 = atoi( input ) / 2;
                _pMan -> p_max = value1;
            }
            printf( "Please enter the politic exp (max:2000): " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) <= 2000 )
            {
                value2 = atoi( input );
                _pMan -> p_real = value2;
            }
            
            printf( "Please enter the battle ability (max:200): ");
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) < 201 )
            {
                value1 = atoi( input ) / 2;
                _pMan -> b_max = value1;
            }
            printf( "Please enter the battle exp (max:2000): " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) <= 2000 )
            {
                value2 = atoi( input );
                _pMan -> b_real = value2;
            }
            
            printf( "Please enter the wisdom ability (max:200): " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) < 201 )
            {
                value1 = atoi( input ) / 2;
                _pMan -> w_max = value1;
            }
            printf( "Please enter the wisdom exp (max:2000): " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) <= 2000 )
            {
                value2 = atoi( input );
                _pMan -> w_real = value2;
            }
            
            printf( "Please enter charming (%d, max:200): ", _pMan -> charming );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) <= 200 )
            {
                value1 = atoi( input );
                _pMan -> charming = value1;
            }
            
            printf( "Please enter ambition (%d, max:200): ", _pMan -> ambition );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) > 0 && atoi( input ) <= 200 )
            {
                value1 = atoi( input );
                _pMan -> ambition = value1;
            }
            
            printf( "Please enter tech (0x%02X, max:255): ", _pMan -> tech );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            if( atoi( input ) >= 0 && atoi( input ) <= 255 )
            {
                value1 = atoi( input );
                _pMan -> tech = value1;
            }
            
            printf( "Please enter soldier type ([1|0]xxxx): " );
            memset( input, 0, 20 );
            fgets( input, 20, stdin );
            _pMan -> soldier_type = 0;
            if( input[0] == '1' )
            {
                _pMan -> soldier_type += 8192;
            }
            _pMan -> soldier_type += ( input[1] - 48 ) * 1;
            _pMan -> soldier_type += ( input[2] - 48 ) * 8;
            _pMan -> soldier_type += ( input[3] - 48 ) * 64;
            _pMan -> soldier_type += ( input[4] - 48 ) * 512;
            
            _pMan -> option1 = _pMan -> option1 | 0x3F;
        }
    }
    
    return;
}

留言

這個網誌中的熱門文章

如何將Linux打造成OpenFlow Switch:Openvswitch

我弟家的新居感恩禮拜分享:善頌善禱

Linux Virtual Interface: TUN/TAP