您的位置:首页 > 其它

jpcsp源码解读5:umd光盘镜像(.iso)

2012-03-19 16:15 162 查看
这次的状况稍显复杂。

首先说一下umd光盘镜像文件的内部组织方式。注意,这些内容全部是从源码解读而来,而不是来自关于这种文件格式的标准文档。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

java科普之文件操作:

fileReader = new RandomAccessFile(umdFilename,"r");

这样的语句就可以打开一个镜像文件,参数"r"应该表示只读方式打开。读文件最常用的操作有两个,seek和read,seek可以指定文件的当前位置,read可以从文件的当前位置读取一定数量的内容,同时将当前位置更新为读完后的位置。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

光盘盘面上被物理的分为一个个扇区,每个扇区中存放固定数量的数据。



压缩镜像的组织结构

iso9660光盘镜像文件的最前面24个字节中包含了一些信息:

是否压缩(如果读出字符串是ciso,则表示压缩),文件总长度,扇区大小,offsetShift,offsetData数组

文件总长度除以扇区大小可以得到总的扇区数。

如果是压缩的镜像,则24个字节之后紧接着是各个扇区在这个镜像文件中的起始位置,也就是offsetData数组。数组中的第i个元素表示第i个扇区的起始位置。但是这个数据要左移offsetShift,才是该扇区实际的起始位置。后一个扇区的起始位置到前一个扇区的起始位置之间,就是这个扇区的压缩内容。

如果是非压缩的镜像,则在镜像文件中,每个扇区占固定数目的字节,也就是扇区大小。

光盘中的有效数据是从第16个扇区开始存放的。(为什么是16,大概标准就这么定的,代码中startSector被初始化为16这个常量)。

到这里为止,我们已经可以从镜像文件(.iso)中解析出该文件是否压缩,该文件有多少个扇区,并且可以在这个打开的镜像文件中定位任意的扇区了。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

光盘是用来存放文件的,下一步就是要从光盘镜像中找到文件。

一个文件由两部分组成,文件头和文件体,任何文件的文件体都在光盘上连续存放,并且是从某个扇区的起始位置开始存放。

注意,文件夹也是文件,他有文件头和文件体。这个文件的文件体中的内容就是一组文件的文件头。



文件头与文件体

文件头中的信息包括:

文件体所在第一个扇区,文件体大小(按字节计),文件属性(我们只关心这个文件是否是文件夹,由属性中的一个bit表示),文件名,时间戳

可以看出,根据这些信息,加上根目录的文件头,结合上文中已经可以从光盘镜像中得到任意扇区的内容,就足以找出光盘上的所有文件。

起始扇区,也就是第16个扇区,这里存放了根目录(根目录当然是一个文件夹)的文件头。

根据根目录的文件头,可以得到根目录的文件体,这里就是所有一级子目录的文件头。

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

现在回到源码,看各个类和上文中提到的内容的对应关系。

位于顶层的是public class UmdIsoReader,这个类中包含了光盘镜像文件的句柄。他在构建函数中,根据传入的文件名,打开相应的光盘镜像文件,如果是压缩镜像,就初始化offsetData数组。

他提供一个方法,将光盘上的指定扇区读取到传入的buffer数组的指定位置上:

public void readSector(int sectorNumber, byte[] buffer, int offset)

注意,上文只是提到可以取得光盘上指定扇区的数据,但是对于压缩镜像,这里得到的数据是经过压缩的,该函数调用了java已有的类库完成数据的解压。

这个方法又进一步封装,从光盘上读取指定的一个扇区,并返回包含这个扇区数据的buffer数组:

public byte[] readSector(int sectorNumber)

他也可以被封装为读取连续的多个扇区到指定的buffer的指定位置:

public int readSectors(int sectorNumber, int numberSectors, byte[] buffer, int offset)

//////////////////////////////////////////////////////////////////////////////////////////////

public class UmdIsoFile extends SeekableInputStream

这个类表示光盘上某个文件的文件体。他为光盘上的文件体提供标准的文件操作接口,如read和seek。

这个类从一个UmdIsoReader构建,因为UmdIsoReader中包含光盘的镜像,而光盘上文件显然都在这个光盘镜像里。

构建函数还要传入需要构建的文件的文件头信息,如 文件名,时间戳,起始扇区,文件大小 等,并且将这些信息作为成员变量保存下来。

这个类提供了read和seek方法。

该类只保留一个扇区的数据buffer,也就是当前读取或seek到的那个位置所在的扇区。

对于read方法,

先调用checkSectorAvailable(),注意这个方法是private的

检查当前扇区内偏移currentOffset是否已经处于当前扇区currentSector(也就是保留了buffer的那个扇区)的末尾,是的话则将buffer更新为下一个扇区

currentSectorNumber++;

currentSector = internalReader.readSector(currentSectorNumber);

并将当前扇区内偏移清0

sectorOffset = 0;

检查并更新的操作完毕,返回buffer中的数据,并将currentOffset++。

对于seek方法:

计算seek到的目标位置所在的扇区,以及扇区内的偏移,并用计算到的值更新这两个成员变量。如果目标扇区和原先的扇区不同,就从光盘镜像中提取目标扇区的数据到buffer中。

//////////////////////////////////////////////////////////////////////////////////////////////

public class Iso9660File

这个类是文件头,从一个字节数组构建,他从传入的字节数组中提取出文件信息,包括文件体起始扇区,文件大小,文件属性,时间戳。

这个类提供了一些取得文件信息的接口。

//////////////////////////////////////////////////////////////////////////////////////////////

public class Iso9660Directory

这个类是文件夹这种文件的文件体。

构建函数:

public Iso9660Directory(UmdIsoReader r, int directorySector, int directorySize)

他先根据传入的必要信息,实例化了一个UmdIsoFile,也就是光盘中文件的文件体。然后从这个文件夹文件的实例中解析出一组文件头,存于一个链表中。

该类提供的接口:

public int getFileIndex(String fileName)

根据文件名,取得该文件头在内部链表中的序号

public Iso9660File getEntryByIndex(int index)

取得链表上相应序号的文件头

注记:这里似乎可以直接根据文件名取得文件头,不知道为什么要多此一举,先取得内部序号,再根据内部序号取得文件头。

public String[] getFileList()

返回文件列表,返回信息中只包含文件名

//////////////////////////////////////////////////////////////////////////////////////////////

public class Iso9660Handler extends Iso9660Directory

这个类是 Iso9660Directory的子类,也就是说,他也是文件夹这种文件的文件体。特别的,他是根目录的文件体。

构建函数:

public Iso9660Handler(UmdIsoReader r)

从UmdIsoReader的startSector提取根目录文件的文件头,然后根据提取到的信息,实例构建出一个Iso9660Directory,作为internalDir。

这个类实际并没有继承父类的任何东西,而是把父类的一个实例作为成员对象,并且把父类的方法全部重载了,以便在用户那里可以把这个子类当父类来使用。

//////////////////////////////////////////////////////////////////////////////////////////////

现在再回到UmdIsoReader

当面向上述的 文件头 文件体 等用户时,他提供的接口可以获取光盘上的字节数据。

而当面向高层用户时,他通过文件头,文件体这些类,将光盘镜像组织成一个完整的文件系统(也就是可以读取其中所有的文件),提供的接口包括:

public UmdIsoFile getFile(String filePath)

根据路径,取得光盘内的一个文件的文件体

public String[] listDirectory(String filePath)

列出指定路径下的所有文件。注意,当传入的路径参数为空时,列出根目录下的所有文件

public String getFileName(int fileStartSector)

返回指定扇区所属文件的文件名

实现上,是从根目录开始,在整个光盘内递归查找所有子目录下文件,看是否有一个文件的起始扇区等于指定的fileStartSector

有则返回这个文件的文件名

没找到则返回null

public String resolveSectorPath(int start, long length)

返回指定扇区所属文件的文件名。行为同前一个函数,实际实现上也是调用的前一个函数。不同之处见下文:

实现上,是调用getFileName(start),如果返回null,则start减1,重复这个调用。因为文件在光盘上是连续存放的,所以如果当前扇区不是某个文件的起始扇区,那么这个扇区应该处于某个文件中靠后的位置,其前的某个扇区应该就是这个文件的起始扇区。

public int getFileProperties(String filePath)

取得由路径指定的文件的文件属性

public boolean isDirectory(String filePath)

判定一个路径指定的文件是不是文件夹

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

关于umd光盘镜像的内容就这么多了,其中略过了少量细节,在我手上的源码中已经加入必要的注释。

因为解读工作只能算刚刚开始,所以注释版的源码就不公布出来了,在积累到一定量的时候再说。不过有兴趣的话随时可以向我索要最新的注释源码,呵呵。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: