/* -------------------------------------------------------------------------------------------

  • 版权声明:
  • 本代码为开源代码,作者拥有代码版权,但你可以在任何非商业用途程序中引用,但请标注出处,你
  • 也可以对代码进行更改。作者对代码中所包括的错误以及所造成的一切后果将不负任何责任。如果你发
  • 现代码中有任何问题或错误,请与我联系。
  • 联系方法: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;

}
【 ================================================================ 】


费先生
1 声望0 粉丝