/* -------------------------------------------------------------------------------------------
- 版权声明:
- 本代码为开源代码,作者拥有代码版权,但你可以在任何非商业用途程序中引用,但请标注出处,你
- 也可以对代码进行更改。作者对代码中所包括的错误以及所造成的一切后果将不负任何责任。如果你发
- 现代码中有任何问题或错误,请与我联系。
- 联系方法:QQ 945318116,Email:945318116@qq.com
- ——作者:徐李 2010年12月28日
- ------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------
- 程序名称:写软盘映像工具
- 程序功能:
- 将引导扇区数据写入到软盘映像文件
- 格式化软盘映像文件为FAT12格式
- 将文件按Fat12格式写入到软盘映像文件
* - 作者:徐李
- 时间:2011年1月4日
* - 概述:这是一个命令行工具软件,对Fat12格式的软盘映像文件进行写入操作,可以写入引导扇区,对映像
- 文件进行格式化,写入一个文件。
* - 算法描述:无
* - 运行流程:
- 1、判断输入参数是否合法有效;
- 1)参数是否有重复;
- 2)参数是否有冲突;
- 3)参数是否有遗漏;
- 2、判断输入与输出文件是否正确;
- 1)如果是引导扇区数据,长度必须是512个字节,引导标志是否存在;
- 2)软盘映像文件长度是否是 5121880*2=1440K
- 3)取文件的长度,读取映像文件的FAT表,判断是否有足够的空间。一个簇号为512,FAT表的空
- 闲簇保留5个簇。
- 3、根据输入参数进行操作;
- 1)格式化:将FAT表1、FAT表2、根目录进行初使化,FAT表头为0xF0、0xFF、0xFF,根目录第一个
- 项目为卷标,卷标为“XL-OS”,但引导数据不改变。
- FAT表中每12个字节位对应一个簇号,0-0xff0 ;0xff1-0xff6为保留,0xff7表示为坏簇,0xff8-0xfff为结束标志
- 2)写引导扇区数据:将引导数据写到映像文件中的第一个扇区;
- 3)写文件数据:取写入文件名,取路径的第一个根路径;
- 生成路径(路径) 返回一个簇号。这个路径是一个11个字符的字符串。如果这个路径存
- 在则返回一个簇号,如果不存在则生成一个子目录的目录项数据并返回该簇号。
- 写入文件(文件名,簇号),文件名为一个11个字符的字符串,簇号为一个目录项簇号,
- 先生成一个文件目录项,如果这个文件存在则用这个文件的簇号为开始簇号。
* - 修订历史:
- 2011年1月24日 修正: 一个致命错误, 写文件数据时, 取空闲簇后要将该簇标记已用.
- 2011年1月20日 修正: 在子目录中查找文件.
- 2011年1月19日 修正:在重写文件时,重写文件长度。
- 2011年1月17日 修正了一个小BUG, 在新写入文件长度小于已存在文件时, 簇结束标志未正确终止, 并进行了优化;
- 2011年1月5日 对代码进行重构,更加方便阅读。
- ------------------------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------------------------
- 程序说明:
- 这是将一个引导扇区数据文件写入到一个软盘映像文件中的工具。
- 即将一个512字节的文件写到一个1.44M的IMG文件的开始处。
- ------------------------------------------------------------------------------------------- */
包含 "习语言系统.接口"
【 ============== 全局常量定义 ================ 】
定义 输入引导类型 真
定义 输入文件类型 假
定义 格式化标记 0x01
定义 帮助标记 0x02
定义 类型标记 0x04
定义 输入文件名标记 0x08
定义 输出文件名标记 0x10
定义 输出路径标记 0x20
【 ================ 结构定义 ================== 】
结构类型 Fat12头结构类型{
字节类型 A[0x3B];
// 字符类型 名字[8] ; // BS_OEMName[8] DB 'XL- OS ' OEM String, 必须 8 个字节
// 短整数类型 扇区字节数 ; // BPB_BytsPerSec DW 512 每扇区字节数
// 字节类型 簇扇区数 ; // BPB_SecPerClus DB 1 每簇多少扇区
// 短整数类型 引导扇区数0 ; // BPB_RsvdSecCnt DW 1 Boot 记录占用多少扇区
// 字节类型 FAT表数 ; // BPB_NumFATs DB 2 共有多少 FAT 表
// 短整数类型 根目录文件数; // BPB_RootEntCnt DW 224 根目录文件数最大值
// 短整数类型 扇区总数 ; // BPB_TotSec16 DW 2880 逻辑扇区总数
// 字节类型 描述符 ; // BPB_Media DB 0xF0 媒体描述符
// 短整数类型 FAT扇区数0 ; // BPB_FATSz16 DW 9 每FAT扇区数
// 短整数类型 磁道扇区数 ; // BPB_SecPerTrk DW 18 每磁道扇区数
// 短整数类型 磁头数 ; // BPB_NumHeads DW 2 磁头数(面数)
// 整数类型 隐藏扇区数 ; // BPB_HiddSec DD 0 隐藏扇区数
// 整数类型 首扇区数 ; // BPB_TotSec32 DD 0 如果 wTotalSectorCount 是 0 由这个值记录扇区数
// 字符类型 驱动号 ; // BS_DrvNum DB 0 中断 13 的驱动器号,即启动驱动器号
// 字符类型 未使用 ; // BS_Reserved1 DB 0 未使用
// 字符类型 扩展引导标记; // BS_BootSig DB 0x29 扩展引导标记 (29h)
// 整数类型 卷序列号 ; // BS_VolID DD 0 卷序列号
// 字符类型 卷标[11] ; // BS_VolLab[11] DB 'XL - OS ' 卷标, 必须 11 个字节
// 字符类型 系统类型[8] ; // BS_FileSysType[8] DB 'FAT12 ' 文件系统类型, 必须 8个字节
}Fat12头={
'X','L','-','O','S', 0x20,0x20,0x20,0,2,1,1,0,
2,0xE0,0,0x40,0x0B,0xF0,9,0, 0x12,0,2,0,0,0,0,0,
0,0,0,0,0,0,0x29,0xBC, 0xB3,0x54,0x9C,'X','L',0x20,'-',0x20,
'O','S',0x20,0x20, 0x20,0x20,'F','A','T','1','2',0x20,0x20,0x20
};
// 字节类型 文件名[8] ; // ; .文件名 times 8 db 0
// 字节类型 扩展名[3] ; // ; .扩展名 times 3 db 0
// 字节类型 属性 ; // ; .属性 db 0 位0:只读
// // 位1:隐藏
// // 位2:系统
// // 位3:卷标 只在根目录中出现,并且对应开始簇号为0
// // 位4:目录
// // 位5:文档 当文件被写入时置此位
// // 位6-7:保留 0
// 字节类型 保留 ; // ; .保留 db 0
// 字节类型 时间戳 ; // ; .时间戳 db 0
// 短整数类型 创建时间 ; // ; .创建时间 dw 0 位 0- 4:秒
// // 位 5-10:分
// // 位11-15:时
// 短整数类型 创建日期 ; // ; .创建日期 dw 0 位 0- 4:日
// // 位 5- 8:月
// // 位 9-15:年
// 短整数类型 访问日期 ; // ; .访问日期 dw 0
// 短整数类型 簇高位 ; // ; .开始簇号高位 dw 0
// 短整数类型 最后写入时间 ; // ; .最后一次写入时间 dw 0
// 短整数类型 最后写入日期 ; // ; .最后一次写入日期 dw 0
// 短整数类型 簇低位 ; // ; .开始簇号低位 dw 0
// 整数类型 长度 ; // ; .文件长度 dd 0
字节类型 FAT目录[32]={
【00】 'X','L','-','O','S',0x20,0x20,0x20, // 文件名
【08】 0x20,0x20,0x20, // 扩展名
【11】 0x08, // 属性
【12】 0, // 保留
【13】 0, // 时间戳
【14】 0,0, // 创建时间
【16】 0,0, // 创建日期
【18】 0,0, // 访问日期
【20】 0,0, // 簇高位
【22】 0,0, // 写入时间
【24】 0,0, // 写入日期
【26】 0,0, // 簇低位
【28】 0,0,0,0 // 长度
};
【 ============== 全局变量定义 ================ 】
字符类型 数据缓冲[512];
布尔类型 格式化标志=假;
字符类型* 输入文件名=空指针;
字符类型* 输出文件名=空指针;
字符类型* 输出路径=空指针;
布尔类型 输入类型标志=输入文件类型;
整数类型 计数器=0;
整数类型 错误代码;
文件类型* fp输入文件;
文件类型* fp输出文件;
整数类型 参数标记位=0; /* 字节位表示哪些参数已设定,当参数重复设定时显示错误信息!
位0:0x01 格式化;
位1:0x02 帮助;
位2:0x04 类型;
位3:0x08 输入文件名;
位4:0x10 输出文件名;
位5:0x20 输出路径; */
字符类型* 输出信息[]={
【00】"写软盘映像工具 V1.0 版,版权所有:徐李 2011年1月2日\n"
"参数格式:\n"
"写软盘映像工具 [-类型 <引导/文件>] -输出 <软盘映像文件名> -输入 <待写入文件名> "
" [-路径 <目标文件完整路径>] [-格式化]\n"
"参数说明:\n"
" -类型 <引导、文件> 待写入的文件类型,如果是引导类型,则写入到第一扇区,如"
" 果是文件类型,则按软盘的FAT12格式,将输入文件写到指"
" 定的路径中去,默认为引导文件。\n"
" -输出 <软盘映像文件名> 这是写出目标的软盘IMG映像文件名,长度必须是1.44M,目"
" 前仅支持1.44M 3.5#软盘映像。\n"
" -输入 <文件名> 如果是写入引导类型,则该文件长度必须是512个字节,并"
" 且最后两个字节是0x55 0xAA。\n"
" -路径 <目标文件完整路径> 如果是写入文件类型,则必须要指定文件在软盘映像中的 "
" 绝对路径(以'\'开头),默认按原文件名写入到根目录中。"
" 路径中的子目录不存在时,会自动创建。\n"
" -格式化 如有该选项,则在写入前先对软盘映像文件进行格式化,格"
" 式化为FAT12类型,再进行写入操作,只对写引导扇区有效。"
" -帮助 显示详细信息。\n",
【01】"\n输入文件名或路径长度错误!",
【02】"\n参数 -帮助 重复!",
【03】"\n参数 -格式化 重复!",
【04】"\n参数 -类型 重复!",
【05】"\n未指定参数 -类型 <引导、文件> 具体类型!",
【06】"\n输入类型错误,只能为 引导 或 文件 !",
【07】"\n参数 -输入 重复!",
【08】"\n参数 -输入 未指定 输入文件名!",
【09】"\n参数 -输出 重复!",
【10】"\n参数 -输出 未指定 输出文件名!",
【11】"\n参数 -路径 重复!",
【12】"\n参数 -路径 未指定 路径名!",
【13】"\n输入未知参数错误!",
【14】"\n打开输出文件错误!",
【15】"\n软盘映像文件长度不等1.44M!请检查!\n",
【16】"\n打开输入文件错误!",
【17】"\n引导扇区文件长度不等于512Byte,请检查!",
【18】"\n引导扇区文件引导标志错误,请检查!",
【19】"\n读取引导扇区文件数据错误!",
【20】"\n输入文件长度过大!",
【21】"\n缺少输入参数!",
【22】"\n缺少输出参数!",
【23】"\n格式化软盘映像错误_清FAT表!",
【24】"\n格式化软盘映像错误_写卷标",
【25】"\n格式化软盘映像错误_重置Fat头",
【26】"\n格式化软盘映像错误_FAT表0、1簇",
【27】"\n写引导扇区数据错误!",
【28】"\n软盘映像没有空间!",
【29】"\n已达根目录项最大数!",
【30】"\n簇号错误!",
【31】"\n警告: 写入文件不可以使用格式化参数,本次格式化将会被忽略!\n",
【32】"\n警告: 写入引导不可以使用路径参数,本次路径参数将会被忽略!\n"
};
字符类型 当前目录标志串[11]=". ";
字符类型 上级目录标志串[11]=".. ";
【 =============== 子函数定义 ================= 】
布尔类型 比较字符串(字符类型 字符串1,字符类型 字符串2);
整数类型 检查参数(整数类型 参数个数,字符类型 *参数表[]);
整数类型 检查文件();
整数类型 软盘映像格式化(); // 返回假为失败,返回真为成功。
整数类型 写软盘映像文件();
整数类型 写软盘映像引导();
整数类型 取下一簇号(整数类型 当前簇号);
整数类型 写入簇号(整数类型 簇号,整数类型 值);
整数类型 簇号转为扇区号(整数类型 簇号);
整数类型 扇区号转为簇号(整数类型 扇区号);
整数类型 整理文件名(字符类型 源文件名,字符类型 目标);
布尔类型 比较文件名(字符类型 源,字符类型 目标);
无类型 文件名复制(字符类型 源,字符类型 目标);
整数类型 查找根目录项_目录(字符类型* 名称);
整数类型 查找根目录项_文件(字符类型* 名称);
整数类型 取空闲簇();
整数类型 新建子目录(字符类型* 名称, 整数类型 位置);
整数类型 查找子目录项_目录(字符类型* 名称, 整数类型 簇号);
整数类型 查找子目录项_文件(字符类型* 名称, 整数类型 簇号);
整数类型 取一段路径(字符类型 路径,字符类型 输出);
整数类型 写文件到簇(整数类型 簇号);
【 ================= 主程序 =================== 】
整数类型 主函数( 整数类型 参数个数, 字符类型 *主参数表[])
{
整数类型 返回值=0;
如果(检查参数(参数个数,主参数表)){ 输出字符串(输出信息[错误代码]); 返回 -1; }
如果(参数标记位&2) 输出字符串(输出信息[0]);
// 在检查参数的时候同时也取得输入、输出文件名、路径。
fp输入文件=文件打开(输入文件名,"读+数"); // '数'如果不加的话会出现读取错误,不知道为什么只能读取66个字节。
fp输出文件=文件打开(输出文件名,"读+数");
如果(检查文件()) 输出字符串(输出信息[错误代码]);
否则
{
如果(参数标记位&1)
{
返回值=软盘映像格式化();
如果(返回值) 输出字符串(输出信息[错误代码]);
}
如果(!返回值)
{
如果(输入类型标志==输入引导类型) 返回值=写软盘映像引导();
否则 返回值=写软盘映像文件();
如果(返回值) 输出字符串(输出信息[错误代码]);
}
}
如果(fp输入文件!=空指针) 文件关闭(fp输入文件);
如果(fp输出文件!=空指针) 文件关闭(fp输出文件);
如果(返回值==-1) 返回 0;
返回 返回值;
}
【 =============== 子函数实现 ================= 】
布尔类型 比较字符串(字符类型 字符串1,字符类型 字符串2)
{
当(*字符串1 == *字符串2 并且 *字符串1 != 0 并且 *字符串2 != 0)重复
{
字符串1++;
字符串2++;
}
如果(*字符串1==0 并且 *字符串2==0)
返回 真;
返回 假;
}
布尔类型 比较文件名(字符类型 源,字符类型 目标)
{
整数类型 i;
循环(i=0;i<11;i++){
如果(转半角大写(*源)!=转半角大写(*目标)) 返回 假;
源++;
目标++;
}
返回 真;
}
无类型 文件名复制(字符类型 源,字符类型 目标)
{
整数类型 i;
循环(i=0;i<11;i++)
{
*目标=转半角大写(*源);
目标++;
源++;
}
返回;
}
整数类型 检查参数(整数类型 参数个数,字符类型 *参数表[]) // 错误返回-1,错误类型保存于'错误代码'中
{
如果(参数个数<=1){错误代码=0; 返回 -1;} // 没有参数,则显示全部参数说明,即帮助。
// 先进行参数检查处理,并提取参数中的数据。
计数器=1; // 从参数表第二个项开始,参数表第一个项是程序名。
当(计数器 < 参数个数)重复
{
如果(比较字符串(参数表[计数器],"-帮助"))
{
如果(参数标记位&帮助标记) {错误代码=2;返回 -1;}
参数标记位=参数标记位 | 帮助标记;
}
否则 如果(比较字符串(参数表[计数器],"-格式化"))
{
如果(参数标记位&格式化标记) {错误代码=3;返回 -1;}
参数标记位=参数标记位 | 格式化标记;
}
否则 如果(比较字符串(参数表[计数器],"-类型"))
{
如果(参数标记位&类型标记) {错误代码=4;返回 -1;}
否则
{
参数标记位=参数标记位 | 类型标记;
计数器++;
如果(计数器>=参数个数) {错误代码=5;返回 -1;}
如果(比较字符串(参数表[计数器],"引导"))
输入类型标志=输入引导类型;
否则 如果(比较字符串(参数表[计数器],"文件"))
输入类型标志=输入文件类型;
否则 {错误代码=6;返回 -1;}
}
}
否则 如果(比较字符串(参数表[计数器],"-输入"))
{
如果(参数标记位&输入文件名标记) {错误代码=7; 返回 -1;}
否则
{
参数标记位=参数标记位 | 输入文件名标记;
计数器++;
如果(计数器>=参数个数) {错误代码=8;返回 -1;}
输入文件名=参数表[计数器];
}
}
否则 如果(比较字符串(参数表[计数器], "-输出"))
{
如果(参数标记位&输出文件名标记) {错误代码=9;返回 -1;}
否则
{
参数标记位=参数标记位 | 输出文件名标记;
计数器++;
如果(计数器>=参数个数) {错误代码=10;返回 -1;}
输出文件名=参数表[计数器];
}
}
否则 如果(比较字符串(参数表[计数器],"-路径"))
{
如果(参数标记位&输出路径标记) {错误代码=11;返回 -1;}
否则
{
参数标记位=参数标记位 | 输出路径标记;
计数器++;
如果(计数器>=参数个数) {错误代码=12;返回 -1;}
输出路径=参数表[计数器];
}
}
否则 {错误代码=13;返回 -1;}
计数器++;
}
如果(参数标记位==帮助标记) {错误代码=0;返回 -1;}
如果(!(参数标记位&输入文件名标记)) {错误代码=21;返回 -1;}
如果(!(参数标记位&输出文件名标记)) {错误代码=22;返回 -1;}
如果(输入类型标志==输入文件类型 并且 (参数标记位&格式化标记)) 输出字符串(输出信息[31]);
如果(输入类型标志==输入引导类型 并且 (参数标记位&输出路径标记)) 输出字符串(输出信息[32]);
返回 0;
}
整数类型 检查文件() // 正常返回 0 ,错误返回 -1, 错误号保存于 '错误代码' 中;
{
整数类型 输入文件长度=0;
整数类型 剩余空间=0;
如果(fp输出文件==空指针) {错误代码=14;返回 -1;}
文件定位(fp输出文件,0,定位_文件尾);
如果(文件当前位置(fp输出文件)!=(512*18*80*2)) {错误代码=15;返回 -1;}
如果(fp输入文件==空指针) {错误代码=16;返回 -1;}
文件定位(fp输入文件,0,定位_文件尾);
如果(输入类型标志==输入引导类型)
{
如果(文件当前位置(fp输入文件)!=512) {错误代码=17;返回 -1;}
文件复位(fp输入文件);
文件定位(fp输入文件,0,定位_文件头);
如果(文件读数据(fp输入文件,数据缓冲,1,512)!=512) {错误代码=19;返回 -1;}
如果((字节类型)数据缓冲[510]!=0x55 或 (字节类型)数据缓冲[511]!=0xAA) {错误代码=18;返回 -1;}
}
否则
{
如果(文件当前位置(fp输入文件)>1024*1024) {错误代码=20;返回 -1;}
}
返回 0;
}
/*
==== 关于软盘映像Fat12格式化相关信息 ====
1.44M软盘映像长度=扇区长度*柱面数*磁头数*扇区数=512*80*2*18=1474560
引导扇区在0扇区;
Fat表1位于扇区 1- 9;
Fat表2位于扇区10-18;
根目录位于扇区19-32,需要进行计算,一般情况下1.44M软盘:根目录占扇区数=头信息中根目录最大数*32/512=224*32/512=14
数据区33-2879扇区;
簇号从2开始,0号、1号保留不用,始终为0xFF0 、0xFFF
每个目录项占32个字节,根目录的第一个目录项为软盘的卷标。
目录项的第一个字节属性: 0xE5 此目录为空
0x00 此目录为空(不再检查后面的项目)
0x05 实际应为 0xE5
根目录中没有 '.' 与 '..' 目录项。
*/
整数类型 软盘映像格式化() // 正常返回 0, 出错返回 -1, 错误号保存于 '错误代码' 中.
{
// 格式化只将FAT表进行初使化,并将根目录清空,写一个软盘的卷标信息。
// 而引导扇区数据则由客户指定文件数据写入。
循环(计数器=0;计数器<512;计数器++)
数据缓冲[计数器]=0;
数据缓冲[510]=0x55;
数据缓冲[511]=0xAA;
文件定位(fp输出文件,0,定位_文件头);
如果(文件写数据(fp输出文件,数据缓冲,1,512)!=512) {错误代码=23;返回 -1;}
数据缓冲[510]=0;
数据缓冲[511]=0;
// 软盘映像全部清零
文件定位(fp输出文件,512,定位_文件头);
循环(计数器=1;计数器<2880;计数器++) // 2880个扇区
如果(文件写数据(fp输出文件,数据缓冲,1,512)!=512) {错误代码=23;返回 -1;}
// 写FAT1与FAT2的前两个默认簇数据
数据缓冲[0]=0xF0;
数据缓冲[1]=0xFF;
数据缓冲[2]=0xFF;
文件定位(fp输出文件,512,定位_文件头); // FAT1
如果(文件写数据(fp输出文件,数据缓冲,1,3)!=3) {错误代码=26;返回 -1;}
文件定位(fp输出文件,512*10,定位_文件头); // FAT2
文件写数据(fp输出文件,数据缓冲,1,3);
// 写根目录卷标信息
文件定位(fp输出文件,512*19,定位_文件头); // FAT1
如果(文件写数据(fp输出文件,(字节类型 *)&FAT目录,1,类型长度(FAT目录))!=类型长度(FAT目录)) {错误代码=24;返回 -1;}
// 对引导扇区初使化,从第三个字节写Fat12头数据
文件定位(fp输出文件,3,定位_文件头);
如果(文件写数据(fp输出文件,(字节类型 *)&Fat12头,1,类型长度(Fat12头))!=类型长度(Fat12头)) {错误代码=25;返回 -1;}
返回 0;
}
/*
关于软盘文件FAT12文件系统
1、在根目录中查找文件目录项,找到对应簇号,直到根目录项第一个字符为0,每个簇(扇区)共有512/32=16个项目,当前簇没有则查找下一个簇。
2、找到这个目录后,在目录项所指的簇号簇中查找下一目录或文件。
3、如果没有该目录项则增加该目录项,及以后的目录项、文件项。
4、写入到文件的数据。
*/
整数类型 写软盘映像引导() // 正常返回 0; 错误返回 -1, 错误号保存于 '错误代码' 中.
{
文件定位(fp输入文件,0,定位_文件头);
文件定位(fp输出文件,0,定位_文件头);
如果(文件读数据(fp输入文件,数据缓冲,1,512)!=512) {错误代码=19;返回 -1;}
如果(文件写数据(fp输出文件,数据缓冲,1,512)!=512) {错误代码=27;返回 -1;}
返回 0;
}
/*
先取路径,注意:路径中含文件名,要判断是否已到文件名,
在根目录下查找该路径或文件名,如果有则返回簇号,如果没有则创建一个目录项。
如果文件存在则将履盖,将文件的簇清零,再重新写入数据。
所需要的子函数:
从当前簇号取下一簇号,
将簇号转为扇区号,
取路径目录(路径,开始位置)返回结束位置,结束位置指向0,即将'\'变为0,如果到结尾则为文件名,如果文件名为空则出错。
写目录项(扇区号,项目号,目录结构)
写文件到簇(开始簇号)
*/
整数类型 写软盘映像文件() // 正常返回 0,错误返回 -1, 错误号保存于 '错误代码' 中.
{
字符类型* lp路径;
字符类型* lp输入文件名;
字符类型* lp定位;
字符类型 路径目录[11];
整数类型 i,j,k,文件簇号,位置;
文件簇号=19; // 文件或目录从根目录开始查找。
lp路径=输出路径;
lp输入文件名=输入文件名;
lp定位=输入文件名;
// 进行映像文件定位,查找映像文件中待写入的位置,这段代码主要是确定文件在目录项目中的位置。
当(真)重复
{
i=取一段路径(lp路径,路径目录);
如果(i==-2) 跳出; // 无文件名,使用默认的文件名。
如果(i==0) {错误代码=1;返回 -1;} // 输入文件名或路径长度错误
如果(i==-1) // 找到文件名。
{
lp输入文件名=lp路径;
跳出;
}
lp路径=lp路径+i;
// 查找目录名,如没有则创建目录。
如果(文件簇号==19) // 是否是根目录的开始扇区
{
// 根目录
文件簇号=查找根目录项_目录(路径目录);
如果(文件簇号==-1) 返回 -1;
}
否则
{
// 在子目录中查找,找到保存目标扇区,没找到新建一个目录或文件。
文件簇号=(查找子目录项_目录(路径目录, 文件簇号));
如果(文件簇号==-1) 返回 -1;
}
}
如果(整理文件名(lp输入文件名,路径目录)) 返回 -1;
如果(文件簇号==19) i=查找根目录项_文件(路径目录);
否则 i=查找子目录项_文件(路径目录, 文件簇号);
如果(i==-1) 返回 -1;
返回 写文件到簇(i);
}
整数类型 取下一簇号(整数类型 当前簇号) // 成功返回对应簇号的FAT项值,如果参数错误则返回 -1;
{
整数类型 下一簇号=0;
整数类型 奇偶标志=0; // 0为偶数,1为奇数
如果(当前簇号<2 或 当前簇号>2840) 返回 -1;
// 把当前簇号换算成对应的序号
奇偶标志=当前簇号%2;
// 格式输出("奇偶标志:0x%六\n",奇偶标志);
下一簇号=512+当前簇号*3/2; // 得到对应字节号
// 定位到FAT表头
文件定位(fp输出文件,下一簇号,定位_文件头);
如果(奇偶标志) 下一簇号=(文件读字符(fp输出文件)>>4)+(((整数类型)文件读字符(fp输出文件))<<4); // 是奇数
否则 下一簇号=文件读字符(fp输出文件)+((((整数类型)文件读字符(fp输出文件)&0x0f))<<8); // 是偶数
返回 下一簇号;
}
整数类型 写入簇号(整数类型 簇号,整数类型 值) // 成功返回 0,错误返回 -1;
{
整数类型 序号;
整数类型 高字节,低字节;
整数类型 奇偶标志=0; // 0为偶数,1为奇数
如果(簇号<2 或 簇号>2840){错误代码=30;返回 -1;}
奇偶标志=簇号%2;
序号=((簇号*3)>>1)+512;
文件定位(fp输出文件,序号,定位_文件头);
低字节=文件读字符(fp输出文件);
高字节=文件读字符(fp输出文件);
如果 (奇偶标志) // 奇数
{
低字节=((低字节&0x0F)|(值&0x0F)<<4);
高字节=((值>>4)&0xFF);
}
否则 // 偶数
{
低字节=(值&0xFF);
高字节=((高字节&0xF0)|((值>>8)&0x0F));
}
文件定位(fp输出文件,序号,定位_文件头);
文件写字符(fp输出文件,低字节);
文件写字符(fp输出文件,高字节);
文件定位(fp输出文件,序号+512*9,定位_文件头);
文件写字符(fp输出文件,低字节);
文件写字符(fp输出文件,高字节);
返回 0;
}
整数类型 簇号转为扇区号(整数类型 簇号)
{
如果(簇号<2 或 簇号>2846) 返回 -1;
返回 簇号-2+33 ;
}
整数类型 扇区号转为簇号(整数类型 扇区号)
{
如果(扇区号>2879 或 扇区号<33) 返回 -1;
返回 扇区号-33+2;
}
整数类型 取空闲簇() // 取一个空闲的簇,如果没有空闲的簇则返回0
{
整数类型 i,j;
循环(i=2;i<2840;i++)
如果(取下一簇号(i)==0)
{
文件定位(fp输出文件, 簇号转为扇区号(i)*512, 定位_文件头);
循环(j=0;j<512;j++)
文件写字符(fp输出文件, 0);
返回 i;
}
错误代码=28;
返回 0;
}
整数类型 整理文件名(字符类型 源文件名,字符类型 目标)
// 将源文件名整理为 8+3 格式,并且将小写转为大写并且不足补空格。
// 正常返回 0,错误返回-1,错误号保存于 '错误代码' 中。
{
整数类型 i;
字符类型* 源;
源=源文件名;
// 检查输入文件名是否符合要求,文件名 不能大于8个字符,扩展名 不能大小3个字符。
当(*源文件名!=0)重复 // 取路径中的文件全名,遇到 '\'路径分隔符就将输入文件名指针指到下一个字符上。
{
如果(*源文件名==0x5C) 源=源文件名+1;// '\'==0x5C
源文件名++;
}
// 将文件名调整为目录项格式的文件名,即全部为大写的 8+3 格式。
循环(i=0;i<8;i++) // 先取文件名,再取扩展名
{
如果(*源==0 或 *源=='.') *目标=0x20;
否则
{
*目标 = 转半角大写(*源);
源++;
}
目标++;
}
如果(*源=='.') 源++;
循环(i=0;i<3;i++) // 取文件扩展名
{
如果(*源==0) *目标=0x20;
否则
{
*目标 = 转半角大写(*源);
源++;
}
目标++;
}
如果(*源!=0) {错误代码=1;返回 -1;} // 文件名长度大于11个字符,返回错误。
返回 0;
}
整数类型 取一段路径(字符类型 路径,字符类型 输出)
// 返回一个取出路径后面的开始位置,如果到结尾则返回-1,如果路径为空或目录名长度超过8个字符则返回0;
// 目录名中的'.'解释与文件名相同。
// 取出路径字符串第一个目录名,也可能是文件名。返回的是-1则是文件名,返回的是正整数则是目录名,如果'\'后结尾则返回-2,说明无文件名。
{
整数类型 i=0;
字符类型* 返回值;
返回值=路径;
如果(路径==空指针) 返回 -2;
// 如果路径的字符串第一个字符不是 '\' 或是0则报错。
如果(*路径!=0x5C 或 *路径==0) 返回 i;
路径++;
// 如果第二个字符是'\'则报错。
如果(*路径==0x5C) 返回 i;
如果(*路径==0) 返回 -2;// 无文件名。
循环(i=0;i<11;i++)
{
如果(*路径==0x5C 或 *路径==0) *输出=0x20;
否则
{
如果(*路径=='.')
{
如果(i>=8) 返回 0;
*输出=0x20;
}
否则 *输出=转半角大写(*路径);
路径++;
}
输出++;
}
如果(*路径==0x5C) 返回 (路径-返回值);
返回 -1;
}
整数类型 查找根目录项_目录(字符类型* 名称)
// 在根目录中查找目录名称,找到返回目录对应的簇号,否则新建一个子目录,返回新建的子目录所在簇号;
// 错误返回 -1, 错误号保存于 '错误代码' 中.
{
字符类型 目录名称[11];
字节类型 目录数据[32];
整数类型 i,j,k,簇号;
如果(整理文件名(名称,目录名称))返回 -1;
循环(j=0;j<224;j++) // 最大224个根目录项
{
文件定位(fp输出文件,512*19+j*32,定位_文件头); // 按根目录序号定位到根目录项数据上。
文件读数据(fp输出文件,目录数据,1,32);// 取一个根目录项数据
如果( 目录数据[0] == 0 ) // 到根目录的结束,而不是结尾,新增目录项。
{
i=文件当前位置(fp输出文件);
i=i-32;
// 重置目录结构数据。
循环(k=0;k<32;k++) 目录数据[k]=0;
文件名复制(目录名称,目录数据);
目录数据[11]=0x10; // 属性:目录
k=取空闲簇();
如果(!k) 返回 -1; // 没有空闲的簇
目录数据[26]=k&0xFF; // 写簇号
目录数据[27]=(k>>8)&0xFF;
文件定位(fp输出文件,i,定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
簇号=(目录数据[26]+(目录数据[27]<<8)); // 保存这个目录所在的簇号
// 写子目录默认的 '.' 与 '..' 项
循环(k=0;k<32;k++) 目录数据[k]=0;
文件名复制(当前目录标志串, 目录数据);
目录数据[11]=0x10; // 属性
目录数据[26]=簇号&0xff; // 簇号
目录数据[27]=(簇号>>8)&0xff;
文件定位(fp输出文件,簇号转为扇区号(簇号)*512,定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
文件名复制(上级目录标志串, 目录数据);
目录数据[26]=0; // 簇号
目录数据[27]=0;
文件写数据(fp输出文件,目录数据,1,32);
写入簇号(簇号,0xFFF);
返回 簇号;
}
如果(比较文件名(目录数据,目录名称) 并且 ((目录数据[11]&0x38)==0x10)) // 如果找到直接返回簇号
返回 (目录数据[26]+(目录数据[27]<<8));
}
错误代码=29;
返回 -1; // 已达到根目录项的最大数。
}
整数类型 查找根目录项_文件(字符类型* 名称)
// 在根目录中查找文件名,找到后返回该文件数据所在簇号,否则新建一个文件目录项,返回取出的空白簇号,
// 错误返回 -1, 错误号保存于 '错误代码' 中.
{
字符类型 文件名称[11];
字节类型 目录数据[32];
整数类型 i,j,k,簇号;
如果(整理文件名(名称,文件名称))返回 -1;
循环(j=0;j<224;j++)
{
文件定位(fp输出文件,512*19+j*32,定位_文件头); // 按根目录序号定位到根目录项数据上。
文件读数据(fp输出文件,目录数据,1,32); // 取一个根目录项数据
i=文件当前位置(fp输出文件);
i=i-32;
如果(目录数据[0]==0) // 到根目录结束,不是结尾
{
// 重置FAT目录结构数据
循环(k=0;k<32;k++) 目录数据[k]=0;
文件名复制(文件名称,目录数据);
目录数据[11]=0x20; // 属性:文件
// 取一个空白簇
k=取空闲簇();
如果(!k) 返回 -1; // 没有空闲簇号
目录数据[26]=k&0xFF;
目录数据[27]=(k>>8)&0xFF;
文件定位(fp输入文件, 0, 定位_文件尾);
j=文件当前位置(fp输入文件);
目录数据[28]=j&0xFF;
目录数据[29]=(j>>8)&0xFF;
目录数据[30]=(j>>16)&0xFF;
目录数据[31]=(j>>24)&0xFF;
文件定位(fp输出文件,i,定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
簇号= (目录数据[26]+(目录数据[27]<<8));
写入簇号(簇号,0xFFF);
返回 簇号;
}
如果(比较文件名(目录数据,文件名称) 并且 ((目录数据[11]&0x18)==0x00))
{
// 重写文件长度
文件定位(fp输入文件, 0, 定位_文件尾);
j=文件当前位置(fp输入文件);
目录数据[28]=j&0xFF;
目录数据[29]=(j>>8)&0xFF;
目录数据[30]=(j>>16)&0xFF;
目录数据[31]=(j>>24)&0xFF;
文件定位(fp输出文件,i,定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
返回 (目录数据[26]+(目录数据[27]<<8));
}
}
错误代码=29;
返回 -1;
}
整数类型 查找子目录项_目录(字符类型* 名称, 整数类型 簇号)
// 在子目录中查找目录名称,找到返回目录对应的簇号,否则新建一个子目录,返回新建的子目录所在簇号;
// 错误返回 -1, 错误号保存于 '错误代码' 中.
{
字符类型 目录名称[11];
字节类型 目录数据[32];
整数类型 i,j,x,扇区号;
i=簇号;
扇区号=簇号转为扇区号(簇号);
如果(整理文件名(名称,目录名称))返回 -1;
重复
{
簇号=i;
文件定位(fp输出文件, 扇区号*512, 定位_文件头);
循环(j=0;j<16;j++)
{
文件读数据(fp输出文件, 目录数据, 1, 32);
如果(目录数据[0]==0) // 新建目录项
{
x=文件当前位置(fp输出文件);
返回 新建子目录(目录名称, x-32);
}
如果(比较文件名(目录数据,目录名称) 并且 ((目录数据[11]&0x38)==0x10))
返回 ((目录数据[26]+(目录数据[27]<<8)));
}
i=取下一簇号(簇号);
扇区号=簇号转为扇区号(i);
}直到(i>0xFF7);
// 取一个新簇,并新建目录项
i=取空闲簇();
如果(!i) 返回 -1; // 没有空闲的簇
写入簇号(簇号,i);
写入簇号(i,0xFFF);
返回 新建子目录(目录名称, 簇号转为扇区号(i)*512);
}
整数类型 新建子目录(字符类型* 名称, 整数类型 位置)
// 在指定位置建一个子目录项,并且在新建子目录簇中初使'.' 与'..' 项。正常返回新目录所在簇号,出错返回-1
{
字符类型 目录名称[11];
字节类型 目录数据[32];
整数类型 簇号;
循环(簇号=0;簇号<32;簇号++)目录数据[簇号]=0; // 数组清零
如果(整理文件名(名称,目录名称))返回 -1;
文件名复制(目录名称, 目录数据);
目录数据[11]=0x10; // 属性: 目录
簇号=取空闲簇();
如果(!簇号) 返回 -1; // 没有空闲的簇
写入簇号(簇号, 0xFFF);
如果(簇号==0){错误代码=28; 返回 -1;} // 没有空闲的簇
目录数据[26]=簇号&0xFF; // 写簇号
目录数据[27]=(簇号>>8)&0xFF;
文件定位(fp输出文件, 位置, 定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
// 生成子目录的'.' '..'
文件名复制(当前目录标志串, 目录数据);
目录数据[11]=0x10; // 属性
目录数据[26]=簇号&0xff; // 簇号
目录数据[27]=(簇号>>8)&0xff;
文件定位(fp输出文件,簇号转为扇区号(簇号)*512,定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
文件名复制(上级目录标志串, 目录数据);
目录数据[26]=0; // 簇号
目录数据[27]=0;
文件写数据(fp输出文件,目录数据,1,32);
返回 簇号;
}
整数类型 查找子目录项_文件(字符类型* 名称, 整数类型 簇号)
// 在子目录扇区中查找文件名, 找到后将该文件的开始簇号返回, 否则新建一个文件的目录项,并将取一个空簇用于保存文件数据,返回这个空簇号.
// 错误返回 -1, 错误号保存于 '错误代码' 中.
{
字符类型 文件名[11];
字节类型 目录数据[32];
整数类型 i,j,k,前簇号;
如果(整理文件名(名称,文件名))返回 -1;
重复{
前簇号=簇号;
文件定位(fp输出文件, 簇号转为扇区号(簇号)*512, 定位_文件头);
j=文件当前位置(fp输出文件);
循环(i=0;i<16;i++)
{
文件定位(fp输出文件,j,定位_文件头);
文件读数据(fp输出文件, 目录数据, 1, 32);
j=文件当前位置(fp输出文件);
如果(目录数据[0]==0)
{
// 新建文件项
// 重置FAT目录结构数据
循环(k=0;k<32;k++) 目录数据[k]=0;
文件名复制(文件名,目录数据);
目录数据[11]=0x20; // 属性:文件
// 取一个空白簇
k=取空闲簇();
如果(!k) 返回 -1; // 没有空闲簇号
目录数据[26]=k&0xFF;
目录数据[27]=(k>>8)&0xFF;
文件定位(fp输入文件, 0, 定位_文件尾);
i=文件当前位置(fp输入文件);
目录数据[28]=i&0xFF;
目录数据[29]=(i>>8)&0xFF;
目录数据[30]=(i>>16)&0xFF;
目录数据[31]=(i>>24)&0xFF;
文件定位(fp输出文件,(j-32),定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
写入簇号(k,0xFFF);
返回 k;
}
如果(比较文件名(目录数据,文件名) 并且 ((目录数据[11]&0x18)==0x00))
{
// 重写文件长度
文件定位(fp输入文件, 0, 定位_文件尾);
i=文件当前位置(fp输入文件);
目录数据[28]=i&0xFF;
目录数据[29]=(i>>8)&0xFF;
目录数据[30]=(i>>16)&0xFF;
目录数据[31]=(i>>24)&0xFF;
文件定位(fp输出文件,(j-32),定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
返回 (目录数据[26]+(目录数据[27]<<8));
}
}
簇号=取下一簇号(前簇号);
如果(簇号==-1 或 簇号==0)返回 -1; // 取簇号错误。
}直到(簇号>0xFF7);
i=取空闲簇();
如果(!i) 返回 -1; // 没有空闲簇号
写入簇号(前簇号,i);
写入簇号(i,0xFFF);
// 重置FAT目录结构数据
循环(k=0;k<32;k++) 目录数据[k]=0;
文件名复制(文件名,目录数据);
目录数据[11]=0x20; // 属性:文件
// 取一个空白簇, 这个簇用于保存文件
k=取空闲簇();
如果(!k) 返回 -1; // 没有空闲簇号
目录数据[26]=k&0xFF;
目录数据[27]=(k>>8)&0xFF;
文件定位(fp输出文件,簇号转为扇区号(i)*512,定位_文件头);
文件写数据(fp输出文件,目录数据,1,32);
返回 k;
}
整数类型 写文件到簇(整数类型 簇号) // 将输入文件写到指定簇开始的映像文件中.
// 正常返回 0, 错误返回 -1, 错误号保存于 '错误代码' 中.
{
整数类型 文件长度,读取长度,i;
文件定位(fp输入文件, 0, 定位_文件尾);
文件长度=文件当前位置(fp输入文件);
文件定位(fp输入文件, 0, 定位_文件头);
i=簇号;
当(文件长度)重复{
如果(文件长度<512) 读取长度=文件长度;
否则 读取长度=512;
簇号=i;
文件读数据(fp输入文件, 数据缓冲, 1, 读取长度);
循环(i=读取长度;i<512;i++) 数据缓冲[i]=0; // 把不足512个字节长度的数据后面清零;
文件定位(fp输出文件, 簇号转为扇区号(簇号)*512, 定位_文件头);
文件写数据(fp输出文件, 数据缓冲, 1, 512);
文件长度=文件长度-读取长度;
i=取下一簇号(簇号);
如果((i<2 或 i>2840) 并且 文件长度) // 生成新簇
{
i=取空闲簇();
如果(!i)返回 -1;
写入簇号(簇号,i);
写入簇号(i, 0xFFF); // 如果不写的话, 下次取空闲簇还会再取出该簇, 陷入死循环!
}
}
// 如果原文件长于新文件, 则将原文件的FAT表剩余项清零.
写入簇号(簇号,0xFFF);
当(i>2 并且 i<2840)重复{
簇号=取下一簇号(i);
如果(簇号>2 并且 簇号<2840){
写入簇号(i, 0);
i=簇号;
}
}
返回 0;
}
【 ================================================================ 】
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。