文件和压缩例程



以下的例程提供了一个快速缓冲的文件 I/O 系统, 它支持基于 Haruhiko Okumura 写的 LZSS 压缩器使用一个环状缓冲互除法 来支持读写压缩文件. 这没有实现象 zip 和 lha 程序那样好的压缩率, 但解压缩非常的快而且不需要很多内存. 压缩文件总是由一个 32 位的值 F_PACK_MAGIC 开始, 自动用值 F_NOPACK_MAGIC 检测文件.

char *fix_filename_case(char *path);
转换文件名到基本大小写设置. DOS 下, 将是全部大写. 返回一个路径参数的拷贝.

char *fix_filename_slashes(char *path);
转换所有的目录分割符. 在 DOS 下, 转换成反斜杠. 返回路径参数的拷贝.


char *fix_filename_path(char *dest, char *path, int size);
转换部分文件名到完全路径名, 长度限制在指定的字符个数. 返回 dest 参数的拷贝.


char *replace_filename(char *dest, char *path, char *filename, int size);
替换指定的路径+文件名为新的文件名, 长度限制在指定的最大字符个数.返回 dest 参数的拷贝.


char *replace_extension(char *dest, char *filename, char *ext, int size);
替换指定文件名+后缀为新的后缀, 长度限制在指定的最大字符个数. 返回 dest 参数的拷贝.

char *append_filename(char *dest, char *path, char *filename, int size);
连接指定的文件名到指定路径的末尾, 长度限制在指定的最大字符个数. 返回 dest 参数的拷贝.

char *get_filename(char *path);
当传入一个完整的指定文件路径, 这个返回指向文件名的指针. '\' 和 '/' 都将被看作是目录分隔符.


char *get_extension(char *filename);
当传入一个完整的文件名 (有或没有路径信息) 这返回指向文件扩展名的指针.

void put_backslash(char *filename);
如果文件名的最后字符不是 '\', '/', 或 '#', 这个例程将接上一个 '\' 在上面.

int file_exists(char *filename, int attrib, int *aret);
检查匹配给出的名字和属性的文件是否存在, 如果是返回非零. 文件属性可能包含有 dir.h 里定义的 FA_* 中任何一个. 如果 aret 不是 NULL, 它将被设置成匹配文件的属性. 如果错误发生, 系统错误代码将被储存在 errno 中.


int exists(char *filename);
file_exists() 的快捷方式版本, 它检查正常的文件, 就是文档型或者设置了只读属性位的, 但是不检查隐含文件, 目录, 系统文件, 等等.

long file_size(char *filename);
以字节为单位返回文件长度. 如果文件不存在或错误发生了, 它将返回零并将系统出错码放在 errno 里.


long file_time(char *filename);
返回文件的更改时间.

int delete_file(char *filename);
从磁盘中删除一个文件.

int for_each_file(char *name, int attrib, void (*callback)(), int param);
在磁盘上寻找匹配给定的统配符和文件属性的文件, 没找到一个就执行一次 callback(). callback() 将被传入三个参数, 第一个字符串包含了完整的文件名, 第二个为文件的属性, 第三个是一个整数它就是 param 简单的复制 (你可以按你自己的任何想法来使用它). 如果发生了错误, 错误码将储存在 errno 里, callback() 可以由自己设置 errno 来导致 for_each_file() 退出. 返回成功调用 callback() 的次数. 文件属性可以包含 dir.h 里的 FA_* 标志中的任何一个.


void packfile_password(char *password);
设置密码口令用来使用压缩文件的所有读写操作. 文件经过设置障碍口令写入后如果不设置同样的口令将不能读, 因此小心点: 如果你忘掉了密码, 我不能恢复你的数据! 传入 NULL 或一个空串将返回到正常, 无障碍模式. 如果你使用这个函数来阻止人们得到你的数据文件的控制权, 注意不要在你的可执行文件中储存明显的口令: 如果有任何字符串象 "I'm the password for the datafile", 那么得到你的数据的控制权就相当的简单 :-)

PACKFILE *pack_fopen(char *filename, char *mode);
以某个模式打开一个文件, 模式可以包含以下标志:

取代这些标志的是, 任何常数 F_READ, F_WRITE, F_READ_PACKED, F_WRITE_PACKED 或 F_WRITE_NOPACK 之一可以用作模式参数. 成功了, pack_fopen() 返回文件结构指针, 出错了, 它返回 NULL 并将错误代码储存在 errno 里. 一个用压缩模式读取正常文件的企图将导致 errno 被设置为 EDOM.


压缩文件函数也能理解一些 "magic" 文件名用作特殊的效果. 就是:

由于这些特殊的文件名, 就是指数据文件中的部分或 加在文件后面的数据可以以和正常文件完全相同的方式读取, 因此 Allegro 里的文件控制函数 ( 比如. load_pax() 和 set_config_file()) 可以用这些方式读. 注意你不能写向这些特殊文件: 假文件是只读的. 而且,如果你想将对象分开读入的话, 你必须将数据文件以非压缩方式储存或以单个对象方式压缩 (否则在读取的时候将花去大量的时间定位). 最后, 要明白指定的 Allegro 类型同型的你输入的文件的格式不同. 当你输入输入位图或采样声音数据到 grabber 的时候, 它们已经被转换成 Allegro 特殊的格式, 但是 '#' 型的文件中读出的文件是 2 进制级别的. 就是说, 比如, 你想使用 load_pcx 来从数据文件中读入一个图片, 你应当输入一个 2 进制块而不是 BITMAP 对象.


int pack_fclose(PACKFILE *f);
int pack_fseek(PACKFILE *f, int offset);
int pack_feof(PACKFILE *f);
int pack_ferror(PACKFILE *f);
int pack_getc(PACKFILE *f);
int pack_putc(int c, PACKFILE *f);
int pack_igetw(PACKFILE *f);
long pack_igetl(PACKFILE *f);
int pack_iputw(int w, PACKFILE *f);
int pack_igetw(PACKFILE *f);
long pack_igetl(PACKFILE *f);
int pack_iputw(int w, PACKFILE *f);
long pack_iputl(long l, PACKFILE *f);
int pack_mgetw(PACKFILE *f);
long pack_mgetl(PACKFILE *f);
int pack_mputw(int w, PACKFILE *f);
long pack_mputl(long l, PACKFILE *f);
long pack_fread(void *p, long n, PACKFILE *f);
long pack_fwrite(void *p, long n, PACKFILE *f);
char *pack_fgets(char *p, int max, PACKFILE *f);
int pack_fputs(char *p, PACKFILE *f);

这所有的都和 stdio 函数工作相同, 除了 pack_fread() 和 pack_fwrite() 只需要单个的 size 参数以取代 愚蠢的 size 和 num_elements 方式, 而且定位仅支持相对当前位置的前移运动. pack_fgets() 函数没有在返回字符串里包含结果. pack_i* 和 pack_m* 例程分别使用 Intel 和 Motorola 字节次序系统 (endianness) 读写 16 和 32 位值. 注意当读取压缩文件时定位操作非常的慢, 因此当你不能确认文件是没有压缩的时应尽量避免这个操作.


PACKFILE *pack_fopen_chunk(PACKFILE *f, int pack);
打开文件的子块, 块主要是为数据文件使用而设计, 但是它们也可以用在你自己的文件例程中. 一个块提供的文件的一部分的逻辑形式, 它可以作为一个独立实体压缩并将自动插入,检查长度记数可以阻止读到块外去. 使用下面的代码写块到文件 f:


      /* 假定 f 是一个 PACKFILE * 它以写模式打开 */
      f = pack_fopen_chunk(f, pack);
      write some data to f
      f = pack_fclose_chunk(f);

写入块的数据将由两个长度记数预处理 (32 位, big-endian). 对不压缩的块这两个将被设置为块中数据的大小. 对压缩的块 (设置了 pack 标志而创建的), 第一个长度将设为实际的块长度, 第二个将设为压缩前的数据长度的负值.

读取快, 使用代码:

      /* 假定 f 是一个 PACKFILE * 它被以读模式打开 */
      f = pack_fopen_chunk(f, FALSE);
      read data from f
      f = pack_fclose_chunk(f);

顺序是先读块建立时的长度记数, 如果它被压缩了就自动解压缩. 长度也是用来阻止读到块以外 (如果你试图干这个, Allegro 将返回 EOF), 当你调用 pack_fclose_chunk() 时自动的忽略掉任何没有读的块数据.


块可以被重复调用 pack_fopen_chunk() 来递归处理. 当写向一个文件, 压缩状态将从父文件继承, 因此你仅仅只需在父文件没压缩但你想压缩块数据时才需要设置 pack 标志. 如果父文件已经由压缩模式打开了, 设置 pack 标志将使数据压缩两次: 一次是写向块, 又一次是在块传到父文件后.

PACKFILE *pack_fclose_chunk(PACKFILE *f);
关闭前面由调用 pack_fopen_chunk() 打开的文件中的子块.





返回