投票
标题:DOS的GRA文件怎么编辑?
Sonny
万物创造者
Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9


论坛之王【顶级】  论坛之星【特级】  论坛之花【高级】  灌水之王  神奇法师  
UID 18
精华 17
积分 9503
帖子 2234
狼毛 4 根
阅读权限 110
注册 2004-5-12
来自 Shanghai
状态 离线
发表于 2008-9-11 22:20 资料 短消息  加为好友 
DOS的GRA文件怎么编辑?

迷走都市的英文版是无码的,我想把GRA图片移植到PC98下去使用,但是出错了。有什么办法可以编辑GRA图片文件?我想让PC98版也变成无码的。





弃我去者昨日之日不可留,乱我心者今日之日多烦忧……
 顶部
wphoto2003 (马力)
圣洁的灵魂
Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7
往事随风 开始新的生活


论坛之王【顶级】  热情火山  神奇法师  百目惠识  热情号角  
UID 60
精华 10
积分 45874
帖子 1379
狼毛 9826 根
阅读权限 90
注册 2004-5-13
来自 天津
状态 离线
发表于 2008-9-11 22:41 资料 短消息  加为好友 QQ
GRA文件格式分析

笔者按:某日某人 疯狂的迷上了PC98模拟器上一个叫mari的游戏.这个游戏难得可以用BT来形容了.但是丝毫没有动摇此人通关此GAME的决心."通关了就好了..通关了就一定有回想模式的..."他对自己说.可惜,回想模式只是一些无聊的黑白图片,完全没有此人想象中的H图片秀。。。。此人发现,此GAME文件夹下有很多.GRA的文件。。。和之前玩的《迷走X市》以及《天使之X后》一样...

本文介绍的GRA图片格式仅限于16色。

首先是可忽略的项目,直到遇到0x1a。0x1a即Ctrl^Z。因此这些项目可以是一段文本。

然后是一个字节的0。

然后是flag字节。此字节最高位为1表示文件内无内置调色板,为0表示有调色板。如果为0,则在图像数据之前会有48个字节的调色板数据,按RGB顺序。本文没有处理无调色板的情况。

然后是一个word,此word跟rom打交道,忽略之。注意:GRA文件中出现的所有word都是big-endian的,即高字节在前低字节在后。

然后是一个注释块。此块以一个大小字节开始,后跟注释块内容。大小字节必须是4。

然后又是一个注释块,此块以一个word指出大小,后跟内容。

然后是宽度1个word,高度1个word。

接着是调色板数据。紧接着是图片数据。

图片数据是高度压缩的,最终可达到约3:1的无损压缩比。它的机理为:游程码+固定码表huffman编码+字典表。

字典表是一个256字节的表,它开始被这样初始化:

0, f0, e0, ... 10,

10, 00, f0, .. 20,

...

f0, e0, d0, ... 00

huffman+字典表以2个点为单位,并且后面的点将成为前面的点的字典偏移选择,例如:解开一个f0,则后面一个点查表时从f0开始,以f0, e0, d0, ...00这一段为表。同时,每次从字典查到一个码后,都要将它提到本段最前。字典以16个字节为单位。huffman编码给出的是查表的位数n,之后为n位的表偏移,例如位数为1,之后的1bit为1,则表示取该段的第1个字节f0,并把第1个字节提到最前,把第0字节推后。

huffman表:

code  rewind count
11 (91c7) 1(swap top 2 bytes)
10   0
00   2+nextbit
010  4+nextbit*2+nextbit
011  8+nextbit*4+nextbit*2+nextbit

数据的组织基本安排如下:

首先用1个哈夫曼的数据填充一行,然后游程码给出要拷贝的偏移(负数,可以是4,width/2,width/2-1,...),或者不拷贝,直接取后面n个编码。具体见本文代码。

以下是从gra转化为bmp的源码,运行方式是在命令行下直接打:gra2bmp gra文件名或通配符(缺省为*.gra)。代码在vs2003中编译通过。

// gra2bmp.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "windows.h"
#include "io.h"

struct gra_data {
unsigned char *buf;
unsigned char *org_buf;
int    count;
unsigned char byte_left;
unsigned char left_bit_count;
};


int load_gra_data(char* filename, struct gra_data* pdata)
{
FILE *fp = fopen(filename, "rb");
if ( !fp ) return 0;
fseek(fp, 0, SEEK_END);
int len = (int)ftell(fp);
fseek(fp, 0, SEEK_SET);
pdata->buf = (unsigned char*)malloc(len);
pdata->count = len;
pdata->left_bit_count = 0;
pdata->byte_left = 0;
pdata->org_buf = pdata->buf;
fread(pdata->buf, len, 1, fp);
fclose(fp);
return 0;
}

void free_gra_data(struct gra_data* pdata)
{
if ( !pdata->org_buf ) return;
free(pdata->org_buf);
pdata->org_buf = NULL;
}

#define GRA_EOF  -1

int gra_next_bit(struct gra_data *pdata)
{
if ( pdata->left_bit_count == 0 ) {
  if ( pdata->count <= 0 ) return GRA_EOF;
  pdata->left_bit_count = 8;
  pdata->byte_left = *pdata->buf++;
  pdata->count--;
}
int ret;
if (pdata->byte_left & 0x80) ret = 1; else ret = 0;
pdata->left_bit_count--;
pdata->byte_left <<= 1;
return ret;
}

unsigned char gra_next_byte(struct gra_data *pdata)
{
unsigned char ch = *pdata->buf++;
pdata->count--;
return ch;
}

void gra_ignore_bytes(struct gra_data *pdata, int n)
{
pdata->buf += n;
pdata->count -= n;
}

BOOL gra_eof(struct gra_data *pdata)
{
return pdata->count <= 0;
}

void init_dict(unsigned char *dict)
{
unsigned char ch;
int j;
for ( j = 0, ch = 0; j < 16; j++, ch += 16 ) {
  for ( int i = 0; i < 16; ++i, ch -= 16 ) {
   *dict++ = ch;
  }
}
}

unsigned char swap_dict(unsigned char *dict, int lastindex)
{
if ( lastindex == 0 ) return dict[0];
if ( lastindex == 1 ) {
  unsigned char ch = dict[1];
  dict[1] = dict[0];
  dict[0] = ch;
  return ch;
}
unsigned char ch = dict[lastindex];
for ( int i = lastindex; i > 0; --i ) {
  dict = dict[i - 1];
}
dict[0] = ch;
return ch;
}

int decode_code(struct gra_data* pdata, unsigned char *dict, unsigned char dict_offset)
{
char buf[40];
sprintf( buf, "%02x\n", dict_offset );
//OutputDebugString( buf );
int bit = gra_next_bit(pdata);
bit = (bit << 1) | gra_next_bit(pdata);
int ch1, ch2, cnt;
switch(bit)
{
case 3: // 11
  ch1 = swap_dict(dict + dict_offset, 1);
  break;
case 2: // 10
  ch1 = dict[dict_offset];
  break;
case 0: // 00
  cnt = 2 + gra_next_bit(pdata);
  ch1 = swap_dict(dict + dict_offset, cnt);
  break;
default: // 01
  if ( gra_next_bit(pdata) == 0 ) {
   // 010
   cnt = 2 + gra_next_bit(pdata);
   cnt = (cnt << 1) + gra_next_bit(pdata);
  } else {
   // 011
   cnt = 2 + gra_next_bit(pdata);
   cnt = (cnt << 1) + gra_next_bit(pdata);
   cnt = (cnt << 1) + gra_next_bit(pdata);
  }
  ch1 = swap_dict(dict + dict_offset, cnt);
  break;
}
dict_offset = ch1;
bit = gra_next_bit(pdata);
bit = (bit << 1) | gra_next_bit(pdata);
switch(bit)
{
case 3: // 11
  ch2 = swap_dict(dict + dict_offset, 1);
  break;
case 2: // 10
  ch2 = dict[dict_offset];
  break;
case 0: // 00
  cnt = 2 + gra_next_bit(pdata);
  ch2 = swap_dict(dict + dict_offset, cnt);
  break;
default: // 01
  if ( gra_next_bit(pdata) == 0 ) {
   // 010
   cnt = 2 + gra_next_bit(pdata);
   cnt = (cnt << 1) + gra_next_bit(pdata);
  } else {
   // 011
   cnt = 2 + gra_next_bit(pdata);
   cnt = (cnt << 1) + gra_next_bit(pdata);
   cnt = (cnt << 1) + gra_next_bit(pdata);
  }
  ch2 = swap_dict(dict + dict_offset, cnt);
  break;
}
return (ch2 << 8) + ch1;
}

unsigned char *decode_until_bit0(struct gra_data* pdata,
         unsigned char *dest,
         unsigned char *dict,
         int* old_offset)
{
int code;
int dict_offset = *(dest - 1);
for ( ;; ) {
  code = decode_code(pdata, dict, dict_offset);
  *(unsigned short *)dest = (short)code;
  dict_offset = code >> 8;
  dest += 2;
  if ( gra_next_bit(pdata) != 1 ) break;
}
*old_offset = 0;
return dest;
}

unsigned char *get_n_bytes_and_copy(struct gra_data* pdata,
         unsigned char *dest,
         unsigned char *dict,
         int src_offset,
         int *old_offset)
{
if ( *old_offset == src_offset ) {
  return decode_until_bit0(pdata, dest, dict, old_offset);
}
*old_offset = src_offset;
int bit_cnt = 0, mov_cnt = 0;
if ( gra_next_bit(pdata) == 0 ) bit_cnt = 0;
else {
  do {
   ++bit_cnt;
  } while ( gra_next_bit(pdata) == 1 );
}
if ( bit_cnt == 0 ) mov_cnt = 1;
else {
  mov_cnt = 1;
  for ( int i = 0; i < bit_cnt; ++i ) mov_cnt = (mov_cnt << 1) | gra_next_bit(pdata);
}
for ( int i = 0; i < mov_cnt; ++i ) {
  // mov_sw
  *(unsigned short *)dest = *(unsigned short *)(dest + src_offset);
  dest += 2;
}
return dest;
}


void gra2bmp(struct gra_data* pdata, char* target_file)
{
unsigned char ch;
do {
  ch = gra_next_byte(pdata);
  if ( gra_eof(pdata) ) return ;
} while ( ch != 0x1A );
gra_next_byte(pdata); // load byte 0
ch = gra_next_byte(pdata); // load byte ignore
unsigned char flags = ch;
gra_next_byte(pdata); // load word(hi)
gra_next_byte(pdata); // load word(lo)
// the word above is related with ROM, just ignore
ch = gra_next_byte(pdata); // 4
if ( ch != 4 ) return ;
gra_ignore_bytes(pdata, 4);
int size = gra_next_byte(pdata);
size = (size << 8) | gra_next_byte(pdata);
gra_ignore_bytes(pdata, size);
int width_bytes;
size = gra_next_byte(pdata);
size = (size << 8) | gra_next_byte(pdata);
width_bytes = size;

size = gra_next_byte(pdata);
size = (size << 8) | gra_next_byte(pdata);
int height = size;
unsigned char palette[48];
if ( flags & 0x80 ) {
  // don't know how to fill the palette  
} else {
  // has palette
  for ( int i = 0; i < 48; ++i ) {
   palette = gra_next_byte(pdata);
  }
}
unsigned char dict[256];
init_dict(dict);
unsigned char *buf = NULL;
buf = (unsigned char*)malloc(width_bytes * height * 2 /* test */);
unsigned char *p = buf;
int code = decode_code(pdata, dict, 0);
// fill background
for ( int i = 0; i < width_bytes; ++i ) {
  *(unsigned short *)p = (short)code;
  p += 2;
}
int src_offset = 0;
int old_offset = 0;
for ( ;; ) {
  if ( gra_eof(pdata) ) break;
  if ( gra_next_bit(pdata) == 1 ) {
   // 9030
   if ( gra_next_bit(pdata) == 0 ) {
    src_offset = -width_bytes * 2;
    p = get_n_bytes_and_copy(pdata, p, dict, src_offset, &old_offset);
   } else {
    src_offset = -width_bytes + 1;
    if ( gra_next_bit(pdata) == 1 ) src_offset -= 2;
    p = get_n_bytes_and_copy(pdata, p, dict, src_offset, &old_offset);
   }
  } else {
   // 90a0
   if ( gra_next_bit(pdata) == 1 ) {
    src_offset = -width_bytes;
    p = get_n_bytes_and_copy(pdata, p, dict, src_offset, &old_offset);
   } else {
    // 90a7
    src_offset = -4;
    if ( *(p - 1) == *(p - 2) ) {
     // 90fc
     if ( src_offset == old_offset ) {
      p = decode_until_bit0(pdata, p, dict, &old_offset);
     } else {
      old_offset = src_offset;

      int bit_cnt = 0, mov_cnt = 0;
      src_offset = -2;

      if ( gra_next_bit(pdata) == 0 ) bit_cnt = 0;
      else {
       do {
        ++bit_cnt;
       } while ( gra_next_bit(pdata) == 1 );
      }
      if ( bit_cnt == 0 ) mov_cnt = 1;
      else {
       mov_cnt = 1;
       for ( int i = 0; i < bit_cnt; ++i ) mov_cnt = (mov_cnt << 1) | gra_next_bit(pdata);
      }
      for ( int i = 0; i < mov_cnt; ++i ) {
       // mov_sw
       *(unsigned short *)p = *(unsigned short *)(p + src_offset);
       p += 2;
      }
     }
    } else {
     p = get_n_bytes_and_copy(pdata, p, dict, src_offset, &old_offset);
    }
   }
  }
}
unsigned char *bmp_buffer = NULL;
// 1 byte for 1 pixel, convert to compressed form
int bpl = width_bytes;
// make dword aligned
bpl = width_bytes * 4 + 31;
bpl /= 32; bpl *= 4;

bmp_buffer = (unsigned char *)malloc(width_bytes * height);
for ( int j = 0; j < height; ++j ) {
  for ( int i = 0; i < width_bytes / 2; ++i ) {
   bmp_buffer[(height - j - 1) * bpl + i] = (buf[j * width_bytes + i * 2]) | (buf[j * width_bytes + i * 2 + 1] >> 4);
  }
}
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bmi;
memset(&bfh, 0, sizeof(BITMAPFILEHEADER));
bfh.bfType = 'MB';
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + 64;
memset(&bmi, 0, sizeof(BITMAPINFOHEADER));
bmi.biSize = sizeof(BITMAPINFOHEADER);
bmi.biSizeImage = width_bytes * height;
bmi.biWidth = width_bytes;
bmi.biHeight = height;
bmi.biPlanes = 1;
bmi.biBitCount = 4;
bmi.biClrUsed = 16;
bmi.biClrImportant = 16;
FILE *fp = fopen(target_file, "wb");
fwrite(&bfh, 1, sizeof bfh, fp);
fwrite(&bmi, 1, sizeof bmi, fp);
RGBQUAD bmp_pal[16];
for ( int i = 0; i < 16; ++i ) {
  bmp_pal.rgbRed = palette[i * 3];
  bmp_pal.rgbGreen = palette[i * 3 + 1];
  bmp_pal.rgbBlue = palette[i * 3 + 2];
  bmp_pal.rgbReserved = 0;
}
fwrite(bmp_pal, 4, 16, fp);
fwrite(bmp_buffer, 1, bpl * height, fp);
fclose(fp);
free(bmp_buffer);
free(buf);
}

int _tmain(int argc, _TCHAR* argv[])
{
char *input_files = "*.gra";
if ( argc == 2 ) {
  input_files = argv[1];
}
if ( argc > 2 ) {
  printf("usage: gra2bmp [gra files]\n");
  return -1;
}
struct _finddata_t t;
long handle;
if ((handle = (long)_findfirst(input_files, &t)) != -1)
{
  char target_file[260];
  do
  {
   strcpy(target_file, t.name);
   char *dot_pos = strrchr(target_file, '.');
   if ( dot_pos ) *dot_pos = '\0';
   strcat(target_file, ".BMP");
   struct gra_data data;
   printf("Processing %s ...", t.name);
   load_gra_data( t.name, &data );
   gra2bmp( &data, target_file );
   free_gra_data( &data );  
   printf("Done.\n");
  }while(_findnext(handle, &t) != -1);
}

return 0;
}

 顶部
wphoto2003 (马力)
圣洁的灵魂
Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7Rank: 7
往事随风 开始新的生活


论坛之王【顶级】  热情火山  神奇法师  百目惠识  热情号角  
UID 60
精华 10
积分 45874
帖子 1379
狼毛 9826 根
阅读权限 90
注册 2004-5-13
来自 天津
状态 离线
发表于 2008-9-11 22:43 资料 短消息  加为好友 QQ
另外一篇:

GPC文件格式分析

http://blog.csdn.net/superarhow/archive/2007/04/23/1575219.aspx

推荐sonny去看看 这人是个程序员 对pc98的文件也有研究的说

 顶部
Sonny
万物创造者
Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9


论坛之王【顶级】  论坛之星【特级】  论坛之花【高级】  灌水之王  神奇法师  
UID 18
精华 17
积分 9503
帖子 2234
狼毛 4 根
阅读权限 110
注册 2004-5-12
来自 Shanghai
状态 离线
发表于 2008-9-11 22:47 资料 短消息  加为好友 
这个方法对我来说有点太复杂了……
最好有个工具可以用啊……





弃我去者昨日之日不可留,乱我心者今日之日多烦忧……
 顶部
Sonny
万物创造者
Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9


论坛之王【顶级】  论坛之星【特级】  论坛之花【高级】  灌水之王  神奇法师  
UID 18
精华 17
积分 9503
帖子 2234
狼毛 4 根
阅读权限 110
注册 2004-5-12
来自 Shanghai
状态 离线
发表于 2008-9-15 20:54 资料 短消息  加为好友 
手工改了一下文件,失败了。看来得找其他方法。





弃我去者昨日之日不可留,乱我心者今日之日多烦忧……
 顶部
ckid
无名的旅人
Rank: 1



UID 120300
精华 0
积分 32
帖子 22
狼毛 0 根
阅读权限 10
注册 2008-7-19
状态 离线
发表于 2008-9-16 12:25 资料 短消息  加为好友 
看不出你还挺追求完美的...跟我一样

这2篇文章其实就是那个什么看图器的原理 加个批处理





抵制Thunder、拒绝QQ、无视所有杀毒软件
 顶部
Sonny
万物创造者
Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9


论坛之王【顶级】  论坛之星【特级】  论坛之花【高级】  灌水之王  神奇法师  
UID 18
精华 17
积分 9503
帖子 2234
狼毛 4 根
阅读权限 110
注册 2004-5-12
来自 Shanghai
状态 离线
发表于 2008-9-16 15:37 资料 短消息  加为好友 
希望能有一个GRA编辑软件,现在那个GRA2BMP可以将GRA转更BMP,反过来是否可以呢?
我看了一下PC98版和DOS版的GRA文件,除了文件头不一样,其他好像都差不多,手工修改不知道能否成功。





弃我去者昨日之日不可留,乱我心者今日之日多烦忧……
 顶部
hongqizhen
欢喜的慈雨
Rank: 5Rank: 5Rank: 5Rank: 5Rank: 5
闲杂人等


UID 6900
精华 0
积分 1590
帖子 753
狼毛 10 根
阅读权限 40
注册 2005-8-31
状态 离线
发表于 2008-9-16 16:30 资料 短消息  加为好友 
用UltraEdit-32同时打开两个文件,对比一下,如果确定只有文件头不同,那么就单击右键,将剪贴版选择为用户剪贴版1,在16进制代码模式,复制DOS版 GRA文件的文件头,粘贴到PC98版的 GRA文件,覆盖掉原有的文件头,不过有一点一定要注意,就是长度一定要一致,长度如果不一致运行一定出错。

 顶部
Sonny
万物创造者
Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9Rank: 9


论坛之王【顶级】  论坛之星【特级】  论坛之花【高级】  灌水之王  神奇法师  
UID 18
精华 17
积分 9503
帖子 2234
狼毛 4 根
阅读权限 110
注册 2004-5-12
来自 Shanghai
状态 离线
发表于 2008-9-16 16:44 资料 短消息  加为好友 
图片大小是一样的,但是长度是否一致倒不清楚。需要再确认一下。





弃我去者昨日之日不可留,乱我心者今日之日多烦忧……
 顶部
 



当前时区 GMT+8, 现在时间是 2024-11-23 09:36
苏ICP备2024131517号

Powered by Discuz! 5.5.0 © 2001-2007
Processed in 0.017657 second(s), 8 queries, Gzip enabled

清除 Cookies - 联系我们 - 狼窝 - Archiver - WAP