show app icons on disk image chooser
This commit is contained in:
parent
6235aa8fe8
commit
bac652d451
|
@ -0,0 +1,6 @@
|
|||
[submodule "libmfs"]
|
||||
path = libmfs
|
||||
url = https://github.com/zydeco/libmfs.git
|
||||
[submodule "libres"]
|
||||
path = libres
|
||||
url = https://github.com/zydeco/libres.git
|
|
@ -39,8 +39,58 @@
|
|||
28F676CB1CD15E0B00FC6FA6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28F676C91CD15E0B00FC6FA6 /* Main.storyboard */; };
|
||||
28F676CD1CD15E0B00FC6FA6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 28F676CC1CD15E0B00FC6FA6 /* Assets.xcassets */; };
|
||||
28F676D01CD15E0B00FC6FA6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 28F676CE1CD15E0B00FC6FA6 /* LaunchScreen.storyboard */; };
|
||||
28F6B4521CF07C48002D76D0 /* UIImage+DiskImageIcon.m in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4511CF07C48002D76D0 /* UIImage+DiskImageIcon.m */; };
|
||||
28F6B4971CF07E00002D76D0 /* block.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4611CF07CC9002D76D0 /* block.c */; };
|
||||
28F6B4981CF07E00002D76D0 /* btree.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4631CF07CC9002D76D0 /* btree.c */; };
|
||||
28F6B4991CF07E00002D76D0 /* data.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4661CF07CC9002D76D0 /* data.c */; };
|
||||
28F6B49A1CF07E00002D76D0 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4681CF07CC9002D76D0 /* file.c */; };
|
||||
28F6B49B1CF07E00002D76D0 /* hfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B46A1CF07CC9002D76D0 /* hfs.c */; };
|
||||
28F6B49C1CF07E00002D76D0 /* low.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B46D1CF07CC9002D76D0 /* low.c */; };
|
||||
28F6B49D1CF07E00002D76D0 /* medium.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B46F1CF07CC9002D76D0 /* medium.c */; };
|
||||
28F6B49E1CF07E00002D76D0 /* memcmp.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4711CF07CC9002D76D0 /* memcmp.c */; };
|
||||
28F6B49F1CF07E00002D76D0 /* node.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4721CF07CC9002D76D0 /* node.c */; };
|
||||
28F6B4A01CF07E00002D76D0 /* record.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4771CF07CC9002D76D0 /* record.c */; };
|
||||
28F6B4A11CF07E00002D76D0 /* version.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4791CF07CC9002D76D0 /* version.c */; };
|
||||
28F6B4A21CF07E00002D76D0 /* volume.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B47B1CF07CC9002D76D0 /* volume.c */; };
|
||||
28F6B4A31CF07E08002D76D0 /* unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4751CF07CC9002D76D0 /* unix.c */; };
|
||||
28F6B4B11CF07ED9002D76D0 /* mfs.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B45C1CF07CBF002D76D0 /* mfs.c */; };
|
||||
28F6B4BF1CF07F39002D76D0 /* res.c in Sources */ = {isa = PBXBuildFile; fileRef = 28F6B4571CF07CB3002D76D0 /* res.c */; };
|
||||
28F6B4C01CF07F5C002D76D0 /* liblibhfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */; };
|
||||
28F6B4C11CF07F5C002D76D0 /* liblibmfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */; };
|
||||
28F6B4C21CF07F5C002D76D0 /* liblibres.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F6B4B61CF07F32002D76D0 /* liblibres.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
28F6B48C1CF07DDD002D76D0 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B4A61CF07EC9002D76D0 /* Copy Files */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
name = "Copy Files";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B4B41CF07F32002D76D0 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
28848B601CDE97D600B86C45 /* InsertDiskViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InsertDiskViewController.h; sourceTree = "<group>"; };
|
||||
28848B611CDE97D600B86C45 /* InsertDiskViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InsertDiskViewController.m; sourceTree = "<group>"; };
|
||||
|
@ -113,10 +163,75 @@
|
|||
28F676CC1CD15E0B00FC6FA6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
28F676CF1CD15E0B00FC6FA6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
28F676D11CD15E0B00FC6FA6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
28F6B4501CF07C48002D76D0 /* UIImage+DiskImageIcon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+DiskImageIcon.h"; sourceTree = "<group>"; };
|
||||
28F6B4511CF07C48002D76D0 /* UIImage+DiskImageIcon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+DiskImageIcon.m"; sourceTree = "<group>"; };
|
||||
28F6B4561CF07CB3002D76D0 /* libres_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libres_internal.h; sourceTree = "<group>"; };
|
||||
28F6B4571CF07CB3002D76D0 /* res.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = res.c; sourceTree = "<group>"; };
|
||||
28F6B4581CF07CB3002D76D0 /* res.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = res.h; sourceTree = "<group>"; };
|
||||
28F6B45A1CF07CBF002D76D0 /* appledouble.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = appledouble.h; sourceTree = "<group>"; };
|
||||
28F6B45B1CF07CBF002D76D0 /* fobj.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fobj.h; sourceTree = "<group>"; };
|
||||
28F6B45C1CF07CBF002D76D0 /* mfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mfs.c; sourceTree = "<group>"; };
|
||||
28F6B45D1CF07CBF002D76D0 /* mfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mfs.h; sourceTree = "<group>"; };
|
||||
28F6B45F1CF07CC9002D76D0 /* acconfig.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = acconfig.h; sourceTree = "<group>"; };
|
||||
28F6B4601CF07CC9002D76D0 /* apple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apple.h; sourceTree = "<group>"; };
|
||||
28F6B4611CF07CC9002D76D0 /* block.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = block.c; sourceTree = "<group>"; };
|
||||
28F6B4621CF07CC9002D76D0 /* block.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = block.h; sourceTree = "<group>"; };
|
||||
28F6B4631CF07CC9002D76D0 /* btree.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = btree.c; sourceTree = "<group>"; };
|
||||
28F6B4641CF07CC9002D76D0 /* btree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = btree.h; sourceTree = "<group>"; };
|
||||
28F6B4651CF07CC9002D76D0 /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = "<group>"; };
|
||||
28F6B4661CF07CC9002D76D0 /* data.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = data.c; sourceTree = "<group>"; };
|
||||
28F6B4671CF07CC9002D76D0 /* data.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = data.h; sourceTree = "<group>"; };
|
||||
28F6B4681CF07CC9002D76D0 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
|
||||
28F6B4691CF07CC9002D76D0 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
|
||||
28F6B46A1CF07CC9002D76D0 /* hfs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hfs.c; sourceTree = "<group>"; };
|
||||
28F6B46B1CF07CC9002D76D0 /* hfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hfs.h; sourceTree = "<group>"; };
|
||||
28F6B46C1CF07CC9002D76D0 /* libhfs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = libhfs.h; sourceTree = "<group>"; };
|
||||
28F6B46D1CF07CC9002D76D0 /* low.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = low.c; sourceTree = "<group>"; };
|
||||
28F6B46E1CF07CC9002D76D0 /* low.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = low.h; sourceTree = "<group>"; };
|
||||
28F6B46F1CF07CC9002D76D0 /* medium.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = medium.c; sourceTree = "<group>"; };
|
||||
28F6B4701CF07CC9002D76D0 /* medium.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = medium.h; sourceTree = "<group>"; };
|
||||
28F6B4711CF07CC9002D76D0 /* memcmp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = memcmp.c; sourceTree = "<group>"; };
|
||||
28F6B4721CF07CC9002D76D0 /* node.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = node.c; sourceTree = "<group>"; };
|
||||
28F6B4731CF07CC9002D76D0 /* node.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = node.h; sourceTree = "<group>"; };
|
||||
28F6B4751CF07CC9002D76D0 /* unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unix.c; sourceTree = "<group>"; };
|
||||
28F6B4761CF07CC9002D76D0 /* os.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = os.h; sourceTree = "<group>"; };
|
||||
28F6B4771CF07CC9002D76D0 /* record.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = record.c; sourceTree = "<group>"; };
|
||||
28F6B4781CF07CC9002D76D0 /* record.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = record.h; sourceTree = "<group>"; };
|
||||
28F6B4791CF07CC9002D76D0 /* version.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = version.c; sourceTree = "<group>"; };
|
||||
28F6B47A1CF07CC9002D76D0 /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
|
||||
28F6B47B1CF07CC9002D76D0 /* volume.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = volume.c; sourceTree = "<group>"; };
|
||||
28F6B47C1CF07CC9002D76D0 /* volume.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = volume.h; sourceTree = "<group>"; };
|
||||
28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibhfs.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibmfs.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
28F6B4B61CF07F32002D76D0 /* liblibres.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = liblibres.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
28F676BA1CD15E0B00FC6FA6 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
28F6B4C01CF07F5C002D76D0 /* liblibhfs.a in Frameworks */,
|
||||
28F6B4C11CF07F5C002D76D0 /* liblibmfs.a in Frameworks */,
|
||||
28F6B4C21CF07F5C002D76D0 /* liblibres.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B48B1CF07DDD002D76D0 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B4A51CF07EC9002D76D0 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B4B31CF07F32002D76D0 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
|
@ -207,6 +322,9 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
28F676BD1CD15E0B00FC6FA6 /* Mini vMac.app */,
|
||||
28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */,
|
||||
28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */,
|
||||
28F6B4B61CF07F32002D76D0 /* liblibres.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
|
@ -239,6 +357,8 @@
|
|||
28CE8E8E1CD4C3B200FE25A8 /* mnvm_core */,
|
||||
28CE8E871CD4C33E00FE25A8 /* mnvm_cfg */,
|
||||
28F676C01CD15E0B00FC6FA6 /* Supporting Files */,
|
||||
28F6B4501CF07C48002D76D0 /* UIImage+DiskImageIcon.h */,
|
||||
28F6B4511CF07C48002D76D0 /* UIImage+DiskImageIcon.m */,
|
||||
);
|
||||
path = "Mini vMac";
|
||||
sourceTree = "<group>";
|
||||
|
@ -246,11 +366,82 @@
|
|||
28F676C01CD15E0B00FC6FA6 /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28F6B4551CF07C9A002D76D0 /* libhfs */,
|
||||
28F6B4541CF07C8D002D76D0 /* libmfs */,
|
||||
28F6B4531CF07C83002D76D0 /* libres */,
|
||||
28F676C11CD15E0B00FC6FA6 /* main.m */,
|
||||
);
|
||||
name = "Supporting Files";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28F6B4531CF07C83002D76D0 /* libres */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28F6B4561CF07CB3002D76D0 /* libres_internal.h */,
|
||||
28F6B4571CF07CB3002D76D0 /* res.c */,
|
||||
28F6B4581CF07CB3002D76D0 /* res.h */,
|
||||
);
|
||||
name = libres;
|
||||
path = ../libres;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28F6B4541CF07C8D002D76D0 /* libmfs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28F6B45A1CF07CBF002D76D0 /* appledouble.h */,
|
||||
28F6B45B1CF07CBF002D76D0 /* fobj.h */,
|
||||
28F6B45C1CF07CBF002D76D0 /* mfs.c */,
|
||||
28F6B45D1CF07CBF002D76D0 /* mfs.h */,
|
||||
);
|
||||
name = libmfs;
|
||||
path = ../libmfs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28F6B4551CF07C9A002D76D0 /* libhfs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28F6B45F1CF07CC9002D76D0 /* acconfig.h */,
|
||||
28F6B4601CF07CC9002D76D0 /* apple.h */,
|
||||
28F6B4611CF07CC9002D76D0 /* block.c */,
|
||||
28F6B4621CF07CC9002D76D0 /* block.h */,
|
||||
28F6B4631CF07CC9002D76D0 /* btree.c */,
|
||||
28F6B4641CF07CC9002D76D0 /* btree.h */,
|
||||
28F6B4651CF07CC9002D76D0 /* config.h */,
|
||||
28F6B4661CF07CC9002D76D0 /* data.c */,
|
||||
28F6B4671CF07CC9002D76D0 /* data.h */,
|
||||
28F6B4681CF07CC9002D76D0 /* file.c */,
|
||||
28F6B4691CF07CC9002D76D0 /* file.h */,
|
||||
28F6B46A1CF07CC9002D76D0 /* hfs.c */,
|
||||
28F6B46B1CF07CC9002D76D0 /* hfs.h */,
|
||||
28F6B46C1CF07CC9002D76D0 /* libhfs.h */,
|
||||
28F6B46D1CF07CC9002D76D0 /* low.c */,
|
||||
28F6B46E1CF07CC9002D76D0 /* low.h */,
|
||||
28F6B46F1CF07CC9002D76D0 /* medium.c */,
|
||||
28F6B4701CF07CC9002D76D0 /* medium.h */,
|
||||
28F6B4711CF07CC9002D76D0 /* memcmp.c */,
|
||||
28F6B4721CF07CC9002D76D0 /* node.c */,
|
||||
28F6B4731CF07CC9002D76D0 /* node.h */,
|
||||
28F6B4741CF07CC9002D76D0 /* os */,
|
||||
28F6B4761CF07CC9002D76D0 /* os.h */,
|
||||
28F6B4771CF07CC9002D76D0 /* record.c */,
|
||||
28F6B4781CF07CC9002D76D0 /* record.h */,
|
||||
28F6B4791CF07CC9002D76D0 /* version.c */,
|
||||
28F6B47A1CF07CC9002D76D0 /* version.h */,
|
||||
28F6B47B1CF07CC9002D76D0 /* volume.c */,
|
||||
28F6B47C1CF07CC9002D76D0 /* volume.h */,
|
||||
);
|
||||
name = libhfs;
|
||||
path = ../libhfs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
28F6B4741CF07CC9002D76D0 /* os */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
28F6B4751CF07CC9002D76D0 /* unix.c */,
|
||||
);
|
||||
path = os;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -271,6 +462,57 @@
|
|||
productReference = 28F676BD1CD15E0B00FC6FA6 /* Mini vMac.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
28F6B48D1CF07DDD002D76D0 /* libhfs */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 28F6B4941CF07DDD002D76D0 /* Build configuration list for PBXNativeTarget "libhfs" */;
|
||||
buildPhases = (
|
||||
28F6B48A1CF07DDD002D76D0 /* Sources */,
|
||||
28F6B48B1CF07DDD002D76D0 /* Frameworks */,
|
||||
28F6B48C1CF07DDD002D76D0 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = libhfs;
|
||||
productName = libhfs;
|
||||
productReference = 28F6B48E1CF07DDD002D76D0 /* liblibhfs.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
28F6B4A71CF07EC9002D76D0 /* libmfs */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 28F6B4AE1CF07EC9002D76D0 /* Build configuration list for PBXNativeTarget "libmfs" */;
|
||||
buildPhases = (
|
||||
28F6B4A41CF07EC9002D76D0 /* Sources */,
|
||||
28F6B4A51CF07EC9002D76D0 /* Frameworks */,
|
||||
28F6B4A61CF07EC9002D76D0 /* Copy Files */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = libmfs;
|
||||
productName = libmfs;
|
||||
productReference = 28F6B4A81CF07EC9002D76D0 /* liblibmfs.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
28F6B4B51CF07F32002D76D0 /* libres */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 28F6B4BC1CF07F32002D76D0 /* Build configuration list for PBXNativeTarget "libres" */;
|
||||
buildPhases = (
|
||||
28F6B4B21CF07F32002D76D0 /* Sources */,
|
||||
28F6B4B31CF07F32002D76D0 /* Frameworks */,
|
||||
28F6B4B41CF07F32002D76D0 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = libres;
|
||||
productName = libres;
|
||||
productReference = 28F6B4B61CF07F32002D76D0 /* liblibres.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
|
@ -284,6 +526,15 @@
|
|||
CreatedOnToolsVersion = 7.3;
|
||||
DevelopmentTeam = UJXNDZ5TNU;
|
||||
};
|
||||
28F6B48D1CF07DDD002D76D0 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
28F6B4A71CF07EC9002D76D0 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
28F6B4B51CF07F32002D76D0 = {
|
||||
CreatedOnToolsVersion = 7.3.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 28F676B81CD15E0B00FC6FA6 /* Build configuration list for PBXProject "Mini vMac" */;
|
||||
|
@ -300,6 +551,9 @@
|
|||
projectRoot = "";
|
||||
targets = (
|
||||
28F676BC1CD15E0B00FC6FA6 /* Mini vMac */,
|
||||
28F6B48D1CF07DDD002D76D0 /* libhfs */,
|
||||
28F6B4A71CF07EC9002D76D0 /* libmfs */,
|
||||
28F6B4B51CF07F32002D76D0 /* libres */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
@ -338,6 +592,7 @@
|
|||
28CE8EB81CD4C3B200FE25A8 /* M68KITAB.c in Sources */,
|
||||
28848B651CDE97E900B86C45 /* SettingsViewController.m in Sources */,
|
||||
28BA89881CE73FBC00A98104 /* MNVMApplication.m in Sources */,
|
||||
28F6B4521CF07C48002D76D0 /* UIImage+DiskImageIcon.m in Sources */,
|
||||
28CE8EB71CD4C3B200FE25A8 /* KBRDEMDV.c in Sources */,
|
||||
28CE8EBC1CD4C3B200FE25A8 /* ROMEMDEV.c in Sources */,
|
||||
28BA897F1CE7315400A98104 /* KBKeyboardLayout.m in Sources */,
|
||||
|
@ -354,6 +609,42 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B48A1CF07DDD002D76D0 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
28F6B49A1CF07E00002D76D0 /* file.c in Sources */,
|
||||
28F6B4981CF07E00002D76D0 /* btree.c in Sources */,
|
||||
28F6B49E1CF07E00002D76D0 /* memcmp.c in Sources */,
|
||||
28F6B49D1CF07E00002D76D0 /* medium.c in Sources */,
|
||||
28F6B4A11CF07E00002D76D0 /* version.c in Sources */,
|
||||
28F6B49C1CF07E00002D76D0 /* low.c in Sources */,
|
||||
28F6B49F1CF07E00002D76D0 /* node.c in Sources */,
|
||||
28F6B4991CF07E00002D76D0 /* data.c in Sources */,
|
||||
28F6B49B1CF07E00002D76D0 /* hfs.c in Sources */,
|
||||
28F6B4A21CF07E00002D76D0 /* volume.c in Sources */,
|
||||
28F6B4A01CF07E00002D76D0 /* record.c in Sources */,
|
||||
28F6B4A31CF07E08002D76D0 /* unix.c in Sources */,
|
||||
28F6B4971CF07E00002D76D0 /* block.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B4A41CF07EC9002D76D0 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
28F6B4B11CF07ED9002D76D0 /* mfs.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
28F6B4B21CF07F32002D76D0 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
28F6B4BF1CF07F39002D76D0 /* res.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
|
@ -466,8 +757,10 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)";
|
||||
INFOPLIST_FILE = "Mini vMac/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_CFLAGS = "-DUSE_LIBRES";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.namedfork.minivmac;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
|
@ -480,14 +773,80 @@
|
|||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)";
|
||||
INFOPLIST_FILE = "Mini vMac/Info.plist";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
OTHER_CFLAGS = "-DUSE_LIBRES";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.namedfork.minivmac;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
28F6B4951CF07DDD002D76D0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = NO;
|
||||
OTHER_CFLAGS = "-DHAVE_CONFIG_H";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
28F6B4961CF07DDD002D76D0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ENABLE_MODULES = NO;
|
||||
OTHER_CFLAGS = "-DHAVE_CONFIG_H";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
28F6B4AF1CF07EC9002D76D0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = NO;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)";
|
||||
OTHER_CFLAGS = "-DUSE_LIBRES";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
28F6B4B01CF07EC9002D76D0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = NO;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)";
|
||||
OTHER_CFLAGS = "-DUSE_LIBRES";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
28F6B4BD1CF07F32002D76D0 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
28F6B4BE1CF07F32002D76D0 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
|
@ -509,6 +868,30 @@
|
|||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
28F6B4941CF07DDD002D76D0 /* Build configuration list for PBXNativeTarget "libhfs" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
28F6B4951CF07DDD002D76D0 /* Debug */,
|
||||
28F6B4961CF07DDD002D76D0 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
28F6B4AE1CF07EC9002D76D0 /* Build configuration list for PBXNativeTarget "libmfs" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
28F6B4AF1CF07EC9002D76D0 /* Debug */,
|
||||
28F6B4B01CF07EC9002D76D0 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
28F6B4BC1CF07F32002D76D0 /* Build configuration list for PBXNativeTarget "libres" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
28F6B4BD1CF07F32002D76D0 /* Debug */,
|
||||
28F6B4BE1CF07F32002D76D0 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 28F676B51CD15E0B00FC6FA6 /* Project object */;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#import "InsertDiskViewController.h"
|
||||
#import "AppDelegate.h"
|
||||
#import "UIImage+DiskImageIcon.h"
|
||||
|
||||
@interface InsertDiskViewController () <UITextFieldDelegate>
|
||||
|
||||
|
@ -420,7 +421,15 @@
|
|||
NSDictionary *attributes = [[NSURL fileURLWithPath:filePath] resourceValuesForKeys:@[NSURLTotalFileSizeKey] error:NULL];
|
||||
if (attributes && attributes[NSURLTotalFileSizeKey]) {
|
||||
BOOL isDiskImage = [[AppDelegate sharedInstance].diskImageExtensions containsObject:fileName.pathExtension.lowercaseString];
|
||||
self.imageView.image = [UIImage imageNamed:isDiskImage ? @"floppy" : @"document"];
|
||||
if (isDiskImage) {
|
||||
UIImage *icon = [UIImage imageWithIconForDiskImage:filePath];
|
||||
if (icon == nil) {
|
||||
icon = [UIImage imageNamed:@"floppy"];
|
||||
}
|
||||
self.imageView.image = icon;
|
||||
} else {
|
||||
self.imageView.image = [UIImage imageNamed:@"document"];
|
||||
}
|
||||
NSString *sizeString = [NSByteCountFormatter stringFromByteCount:[attributes[NSURLTotalFileSizeKey] longLongValue] countStyle:NSByteCountFormatterCountStyleBinary];
|
||||
self.detailTextLabel.text = sizeString;
|
||||
} else {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// UIImage+DiskImageIcon.h
|
||||
// Mini vMac
|
||||
//
|
||||
// Created by Jesús A. Álvarez on 21/05/2016.
|
||||
// Copyright © 2016 namedfork. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (DiskImageIcon)
|
||||
|
||||
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path;
|
||||
|
||||
@end
|
|
@ -0,0 +1,602 @@
|
|||
//
|
||||
// UIImage+DiskImageIcon.m
|
||||
// Mini vMac
|
||||
//
|
||||
// Created by Jesús A. Álvarez on 21/05/2016.
|
||||
// Copyright © 2016 namedfork. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIImage+DiskImageIcon.h"
|
||||
#import "libhfs.h"
|
||||
#import "res.h"
|
||||
#import "mfs.h"
|
||||
|
||||
#define kDiskImageHasDC42Header 1 << 0
|
||||
#define RSHORT(base, offset) ntohs(*((short *)((base) + (offset))))
|
||||
#define RLONG(base, offset) ntohl(*((long *)((base) + (offset))))
|
||||
#define RCSTR(base, offset) ((char *)((base) + (offset)))
|
||||
|
||||
@interface DiskImageIconReader : NSObject
|
||||
|
||||
- (UIImage *)iconForDiskImage:(NSString *)path;
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIImage (DiskImageIcon)
|
||||
|
||||
+ (UIImage *)imageWithIconForDiskImage:(NSString *)path {
|
||||
return [[DiskImageIconReader new] iconForDiskImage:path];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
// Mac OS 1 bit palette
|
||||
static uint32_t ctb1[2] = {0xFFFFFF, 0x000000};
|
||||
|
||||
// Mac OS 4 bit palette
|
||||
static uint32_t ctb4[16] = {
|
||||
0xFFFFFF, 0xFFFF00, 0xFF6600, 0xDD0000, 0xFF0099, 0x330099, 0x0000DD, 0x0099FF,
|
||||
0x00BB00, 0x006600, 0x663300, 0x996633, 0xCCCCCC, 0x888888, 0x444444, 0x000000};
|
||||
// Mac OS 8 bit palette
|
||||
static uint32_t ctb8[256] = {
|
||||
0xFFFFFF, 0xFFFFCC, 0xFFFF99, 0xFFFF66, 0xFFFF33, 0xFFFF00, 0xFFCCFF, 0xFFCCCC,
|
||||
0xFFCC99, 0xFFCC66, 0xFFCC33, 0xFFCC00, 0xFF99FF, 0xFF99CC, 0xFF9999, 0xFF9966,
|
||||
0xFF9933, 0xFF9900, 0xFF66FF, 0xFF66CC, 0xFF6699, 0xFF6666, 0xFF6633, 0xFF6600,
|
||||
0xFF33FF, 0xFF33CC, 0xFF3399, 0xFF3366, 0xFF3333, 0xFF3300, 0xFF00FF, 0xFF00CC,
|
||||
0xFF0099, 0xFF0066, 0xFF0033, 0xFF0000, 0xCCFFFF, 0xCCFFCC, 0xCCFF99, 0xCCFF66,
|
||||
0xCCFF33, 0xCCFF00, 0xCCCCFF, 0xCCCCCC, 0xCCCC99, 0xCCCC66, 0xCCCC33, 0xCCCC00,
|
||||
0xCC99FF, 0xCC99CC, 0xCC9999, 0xCC9966, 0xCC9933, 0xCC9900, 0xCC66FF, 0xCC66CC,
|
||||
0xCC6699, 0xCC6666, 0xCC6633, 0xCC6600, 0xCC33FF, 0xCC33CC, 0xCC3399, 0xCC3366,
|
||||
0xCC3333, 0xCC3300, 0xCC00FF, 0xCC00CC, 0xCC0099, 0xCC0066, 0xCC0033, 0xCC0000,
|
||||
0x99FFFF, 0x99FFCC, 0x99FF99, 0x99FF66, 0x99FF33, 0x99FF00, 0x99CCFF, 0x99CCCC,
|
||||
0x99CC99, 0x99CC66, 0x99CC33, 0x99CC00, 0x9999FF, 0x9999CC, 0x999999, 0x999966,
|
||||
0x999933, 0x999900, 0x9966FF, 0x9966CC, 0x996699, 0x996666, 0x996633, 0x996600,
|
||||
0x9933FF, 0x9933CC, 0x993399, 0x993366, 0x993333, 0x993300, 0x9900FF, 0x9900CC,
|
||||
0x990099, 0x990066, 0x990033, 0x990000, 0x66FFFF, 0x66FFCC, 0x66FF99, 0x66FF66,
|
||||
0x66FF33, 0x66FF00, 0x66CCFF, 0x66CCCC, 0x66CC99, 0x66CC66, 0x66CC33, 0x66CC00,
|
||||
0x6699FF, 0x6699CC, 0x669999, 0x669966, 0x669933, 0x669900, 0x6666FF, 0x6666CC,
|
||||
0x666699, 0x666666, 0x666633, 0x666600, 0x6633FF, 0x6633CC, 0x663399, 0x663366,
|
||||
0x663333, 0x663300, 0x6600FF, 0x6600CC, 0x660099, 0x660066, 0x660033, 0x660000,
|
||||
0x33FFFF, 0x33FFCC, 0x33FF99, 0x33FF66, 0x33FF33, 0x33FF00, 0x33CCFF, 0x33CCCC,
|
||||
0x33CC99, 0x33CC66, 0x33CC33, 0x33CC00, 0x3399FF, 0x3399CC, 0x339999, 0x339966,
|
||||
0x339933, 0x339900, 0x3366FF, 0x3366CC, 0x336699, 0x336666, 0x336633, 0x336600,
|
||||
0x3333FF, 0x3333CC, 0x333399, 0x333366, 0x333333, 0x333300, 0x3300FF, 0x3300CC,
|
||||
0x330099, 0x330066, 0x330033, 0x330000, 0x00FFFF, 0x00FFCC, 0x00FF99, 0x00FF66,
|
||||
0x00FF33, 0x00FF00, 0x00CCFF, 0x00CCCC, 0x00CC99, 0x00CC66, 0x00CC33, 0x00CC00,
|
||||
0x0099FF, 0x0099CC, 0x009999, 0x009966, 0x009933, 0x009900, 0x0066FF, 0x0066CC,
|
||||
0x006699, 0x006666, 0x006633, 0x006600, 0x0033FF, 0x0033CC, 0x003399, 0x003366,
|
||||
0x003333, 0x003300, 0x0000FF, 0x0000CC, 0x000099, 0x000066, 0x000033, 0xEE0000,
|
||||
0xDD0000, 0xBB0000, 0xAA0000, 0x880000, 0x770000, 0x550000, 0x440000, 0x220000,
|
||||
0x110000, 0x00EE00, 0x00DD00, 0x00BB00, 0x00AA00, 0x008800, 0x007700, 0x005500,
|
||||
0x004400, 0x002200, 0x001100, 0x0000EE, 0x0000DD, 0x0000BB, 0x0000AA, 0x000088,
|
||||
0x000077, 0x000055, 0x000044, 0x000022, 0x000011, 0xEEEEEE, 0xDDDDDD, 0xBBBBBB,
|
||||
0xAAAAAA, 0x888888, 0x777777, 0x555555, 0x444444, 0x222222, 0x111111, 0x000000};
|
||||
|
||||
@implementation DiskImageIconReader
|
||||
|
||||
- (UIImage *)iconForDiskImage:(NSString *)path {
|
||||
// determine format and offset of disk image
|
||||
NSFileHandle *fh = [NSFileHandle fileHandleForReadingAtPath:path];
|
||||
if (fh == nil) {
|
||||
return nil;
|
||||
}
|
||||
[fh seekToFileOffset:1024];
|
||||
NSData *checkHeader = [fh readDataOfLength:128];
|
||||
[fh closeFile];
|
||||
const unsigned char *chb = [checkHeader bytes];
|
||||
|
||||
// determine type from header
|
||||
if ((chb[0] == 0x42) && (chb[1] == 0x44)) {
|
||||
/* hfs */
|
||||
return [self iconForHFSDiskImage:path options:0];
|
||||
} else if ((chb[0] == 0xD2) && (chb[1] == 0xD7)) {
|
||||
/* mfs */
|
||||
return [self iconForMFSDiskImage:path options:0];
|
||||
} else if ((chb[84] == 0x42) && (chb[85] == 0x44)) {
|
||||
/* hfs, dc42 header */
|
||||
return [self iconForHFSDiskImage:path options:kDiskImageHasDC42Header];
|
||||
} else if ((chb[84] == 0xD2) && (chb[85] == 0xD7)) {
|
||||
/* mfs, dc42 header */
|
||||
return [self iconForMFSDiskImage:path options:kDiskImageHasDC42Header];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - MFS
|
||||
|
||||
- (UIImage *)iconForMFSDiskImage:(NSString *)path options:(int)options {
|
||||
// open disk image
|
||||
size_t offset = (options & kDiskImageHasDC42Header) ? 84 : 0;
|
||||
MFSVolume *vol = mfs_vopen([path fileSystemRepresentation], (size_t)offset, 0);
|
||||
if (vol == NULL) {
|
||||
NSLog(@"Can't open MFS volume at %@", path);
|
||||
return nil;
|
||||
}
|
||||
NSString *volName = [NSString stringWithCString:vol->name encoding:NSMacOSRomanStringEncoding];
|
||||
NSString *volComment;
|
||||
char *const volCommentBytes = mfs_comment(vol, NULL);
|
||||
if (volCommentBytes) {
|
||||
volComment = [NSString stringWithCString:volCommentBytes encoding:NSMacOSRomanStringEncoding];
|
||||
free(volCommentBytes);
|
||||
}
|
||||
|
||||
// find applications
|
||||
MFSDirectoryRecord *rec;
|
||||
NSMutableArray *apps = [NSMutableArray arrayWithCapacity:5];
|
||||
for (int i = 0; vol->directory[i]; i++) {
|
||||
rec = vol->directory[i];
|
||||
if (ntohl(rec->flUsrWds.type) != 'APPL') {
|
||||
continue;
|
||||
}
|
||||
[apps addObject:[NSNumber numberWithInt:i]];
|
||||
}
|
||||
|
||||
// if there's more than one app, find one that looks matching
|
||||
if ([apps count] == 0) {
|
||||
return nil;
|
||||
} else if ([apps count] > 1) {
|
||||
rec = NULL;
|
||||
for (NSNumber *num in apps) {
|
||||
rec = vol->directory[[num intValue]];
|
||||
NSString *appName = [[NSString alloc] initWithCString:rec->flCName encoding:NSMacOSRomanStringEncoding];
|
||||
if (![self chooseApp:appName inVolume:volName hint:volComment]) {
|
||||
rec = NULL;
|
||||
}
|
||||
if (rec) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rec == NULL) {
|
||||
return nil;
|
||||
}
|
||||
} else {
|
||||
rec = vol->directory[[[apps objectAtIndex:0] intValue]];
|
||||
}
|
||||
|
||||
// open resource fork
|
||||
MFSFork *rsrcFork = mfs_fkopen(vol, rec, kMFSForkRsrc, 0);
|
||||
RFILE *rfile = res_open_funcs(rsrcFork, mfs_fkseek, mfs_fkread);
|
||||
|
||||
// get icon
|
||||
CGImageRef iconImage = [self appIconForResourceFile:rfile creator:ntohl(rec->flUsrWds.creator)];
|
||||
UIImage *icon = nil;
|
||||
if (iconImage) {
|
||||
icon = [UIImage imageWithCGImage:iconImage];
|
||||
CGImageRelease(iconImage);
|
||||
}
|
||||
|
||||
// close stuff
|
||||
res_close(rfile);
|
||||
mfs_fkclose(rsrcFork);
|
||||
mfs_vclose(vol);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
#pragma mark - HFS
|
||||
|
||||
- (UIImage *)iconForHFSDiskImage:(NSString *)path options:(int)options {
|
||||
// open disk image
|
||||
int mountFlags = HFS_MODE_RDONLY;
|
||||
if (options & kDiskImageHasDC42Header) {
|
||||
mountFlags |= HFS_OPT_DC42HEADER;
|
||||
}
|
||||
hfsvol *vol = hfs_mount([path fileSystemRepresentation], 0, mountFlags);
|
||||
if (vol == NULL) {
|
||||
NSLog(@"Can't open HFS volume at %@ with flags %x", path, mountFlags);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// try volume icon
|
||||
UIImage *volumeIcon = [self iconFromHFSVolumeIcon:vol];
|
||||
if (volumeIcon) {
|
||||
hfs_umount(vol);
|
||||
return volumeIcon;
|
||||
}
|
||||
|
||||
// find best application
|
||||
UIImage *icon = nil;
|
||||
NSString *appPath = [self findAppInHFSVolume:vol];
|
||||
hfsfile *hfile = NULL;
|
||||
RFILE *rfile = NULL;
|
||||
if (appPath == nil) {
|
||||
hfs_umount(vol);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// open resource fork
|
||||
hfile = hfs_open(vol, [appPath cStringUsingEncoding:NSMacOSRomanStringEncoding]);
|
||||
if (hfile == NULL) {
|
||||
hfs_umount(vol);
|
||||
return nil;
|
||||
}
|
||||
hfs_setfork(hfile, 1);
|
||||
rfile = res_open_funcs(hfile, (res_seek_func)hfs_seek, (res_read_func)hfs_read);
|
||||
if (rfile == NULL) {
|
||||
hfs_close(hfile);
|
||||
hfs_umount(vol);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// get icon
|
||||
hfsdirent ent;
|
||||
if (hfs_stat(vol, [appPath cStringUsingEncoding:NSMacOSRomanStringEncoding], &ent)) {
|
||||
res_close(rfile);
|
||||
hfs_close(hfile);
|
||||
hfs_umount(vol);
|
||||
return nil;
|
||||
}
|
||||
CGImageRef iconImage = [self appIconForResourceFile:rfile creator:ntohl(*(uint32_t *)ent.u.file.creator)];
|
||||
if (iconImage) {
|
||||
icon = [UIImage imageWithCGImage:iconImage];
|
||||
CGImageRelease(iconImage);
|
||||
}
|
||||
|
||||
// close stuff
|
||||
res_close(rfile);
|
||||
hfs_close(hfile);
|
||||
hfs_umount(vol);
|
||||
return icon;
|
||||
}
|
||||
|
||||
- (NSString *)findAppInHFSVolume:(hfsvol *)vol {
|
||||
// get disk name
|
||||
hfsvolent volEnt;
|
||||
hfs_vstat(vol, &volEnt);
|
||||
NSString *volName = [NSString stringWithCString:volEnt.name encoding:NSMacOSRomanStringEncoding];
|
||||
NSString *volComment = [self commentForHFSVolume:vol];
|
||||
|
||||
// find apps
|
||||
NSMutableArray *apps = [[NSMutableArray alloc] initWithCapacity:5];
|
||||
[self findApps:apps inDirectory:HFS_CNID_ROOTDIR ofHFSVolume:vol skipFolder:volEnt.blessed];
|
||||
|
||||
// decide which one to use
|
||||
NSString *myApp = nil;
|
||||
NSString *appName = nil;
|
||||
if ([apps count] == 1) {
|
||||
myApp = [apps objectAtIndex:0];
|
||||
} else if ([apps count] > 1) {
|
||||
for (NSString *appPath in apps) {
|
||||
// choose an app
|
||||
appName = [appPath componentsSeparatedByString:@":"].lastObject;
|
||||
if (![self chooseApp:appName inVolume:volName hint:volComment]) {
|
||||
continue;
|
||||
}
|
||||
myApp = appPath;
|
||||
}
|
||||
}
|
||||
|
||||
return myApp;
|
||||
}
|
||||
|
||||
- (void)findApps:(NSMutableArray *)apps inDirectory:(unsigned long)cnid ofHFSVolume:(hfsvol *)vol skipFolder:(unsigned long)skipCNID {
|
||||
if (hfs_setcwd(vol, cnid)) {
|
||||
return;
|
||||
}
|
||||
hfsdir *dir = hfs_opendir(vol, ":");
|
||||
if (dir == NULL) {
|
||||
return;
|
||||
}
|
||||
hfsdirent ent;
|
||||
while (hfs_readdir(dir, &ent) == 0) {
|
||||
if (ent.flags & HFS_ISDIR && ent.cnid != skipCNID) {
|
||||
[self findApps:apps inDirectory:ent.cnid ofHFSVolume:vol skipFolder:skipCNID];
|
||||
} else if (ntohl(*(uint32_t *)ent.u.file.type) == 'APPL') {
|
||||
// Found an app
|
||||
[apps addObject:[self pathToDirEntry:&ent ofHFSVolume:vol]];
|
||||
}
|
||||
}
|
||||
hfs_closedir(dir);
|
||||
}
|
||||
|
||||
- (NSString *)pathToDirEntry:(const hfsdirent *)ent ofHFSVolume:(hfsvol *)vol {
|
||||
NSMutableString *path = [NSMutableString stringWithCString:ent->name encoding:NSMacOSRomanStringEncoding];
|
||||
NSString *entName;
|
||||
char name[HFS_MAX_FLEN + 1];
|
||||
unsigned long cnid = ent->parid;
|
||||
while (cnid != HFS_CNID_ROOTPAR) {
|
||||
if (hfs_dirinfo(vol, &cnid, name)) {
|
||||
return nil;
|
||||
}
|
||||
entName = [[NSString alloc] initWithCString:name encoding:NSMacOSRomanStringEncoding];
|
||||
[path insertString:@":" atIndex:0];
|
||||
[path insertString:entName atIndex:0];
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
- (NSString *)commentForHFSVolume:(hfsvol *)vol {
|
||||
hfsvolent vent;
|
||||
hfsdirent dent;
|
||||
NSString *comment = nil;
|
||||
|
||||
// get comment ID
|
||||
if (hfs_vstat(vol, &vent) || hfs_stat(vol, ":", &dent)) {
|
||||
return nil;
|
||||
}
|
||||
unsigned short cmtID = dent.fdcomment;
|
||||
|
||||
// open desktop
|
||||
hfsfile *hfile = NULL;
|
||||
RFILE *rfile = NULL;
|
||||
hfs_chdir(vol, vent.name);
|
||||
hfile = hfs_open(vol, "Desktop");
|
||||
if (hfile == NULL) {
|
||||
return nil;
|
||||
}
|
||||
hfs_setfork(hfile, 1);
|
||||
rfile = res_open_funcs(hfile, (res_seek_func)hfs_seek, (res_read_func)hfs_read);
|
||||
if (rfile == NULL) {
|
||||
hfs_close(hfile);
|
||||
return nil;
|
||||
}
|
||||
|
||||
// read resource
|
||||
unsigned char cmtLen;
|
||||
size_t readBytes;
|
||||
res_read(rfile, 'FCMT', cmtID, &cmtLen, 0, 1, &readBytes, NULL);
|
||||
if (readBytes == 0) {
|
||||
res_close(rfile);
|
||||
hfs_close(hfile);
|
||||
return nil;
|
||||
}
|
||||
char cmtBytes[256];
|
||||
res_read(rfile, 'FCMT', cmtID, cmtBytes, 1, cmtLen, &readBytes, NULL);
|
||||
cmtBytes[cmtLen] = '\0';
|
||||
comment = [NSString stringWithCString:cmtBytes encoding:NSMacOSRomanStringEncoding];
|
||||
|
||||
// close
|
||||
res_close(rfile);
|
||||
hfs_close(hfile);
|
||||
return comment;
|
||||
}
|
||||
|
||||
- (UIImage *)iconFromHFSVolumeIcon:(hfsvol *)vol {
|
||||
UIImage *icon = nil;
|
||||
hfsvolent vent;
|
||||
if (hfs_vstat(vol, &vent)) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// open icon file
|
||||
hfs_chdir(vol, vent.name);
|
||||
hfsfile *hfile = NULL;
|
||||
RFILE *rfile = NULL;
|
||||
hfile = hfs_open(vol, "Icon\x0D");
|
||||
if (hfile == NULL) {
|
||||
res_close(rfile);
|
||||
return nil;
|
||||
}
|
||||
hfs_setfork(hfile, 1);
|
||||
rfile = res_open_funcs(hfile, (res_seek_func)hfs_seek, (res_read_func)hfs_read);
|
||||
if (rfile == NULL) {
|
||||
if (hfile) {
|
||||
hfs_close(hfile);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
// read icon family
|
||||
NSDictionary *iconFamily = [self iconFamilyID:-16455 inResourceFile:rfile];
|
||||
|
||||
// create image
|
||||
CGImageRef iconImage = [self iconImageFromFamily:iconFamily];
|
||||
if (iconImage) {
|
||||
icon = [UIImage imageWithCGImage:iconImage];
|
||||
CGImageRelease(iconImage);
|
||||
}
|
||||
|
||||
res_close(rfile);
|
||||
if (hfile) {
|
||||
hfs_close(hfile);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
#pragma mark - App Selection
|
||||
|
||||
- (BOOL)chooseApp:(NSString *)appName inVolume:(NSString *)volName hint:(NSString *)hint {
|
||||
return ([appName hasPrefix:volName] ||
|
||||
[volName hasPrefix:appName] ||
|
||||
[volName isEqualToString:appName] ||
|
||||
[appName isEqualToString:hint]);
|
||||
}
|
||||
|
||||
#pragma mark - Resource Access
|
||||
|
||||
- (CGImageRef)appIconForResourceFile:(RFILE *)rfile creator:(OSType)creator {
|
||||
// load bundle
|
||||
size_t numBundles;
|
||||
ResAttr *bundles = res_list(rfile, 'BNDL', NULL, 0, 0, &numBundles, NULL);
|
||||
void *bundle = NULL;
|
||||
if (numBundles == 0 || bundles == NULL) {
|
||||
return nil;
|
||||
}
|
||||
for (int i = 0; i < numBundles; i++) {
|
||||
bundle = res_read(rfile, 'BNDL', bundles[i].ID, NULL, 0, 0, NULL, NULL);
|
||||
if (bundle == NULL || ntohl(*(OSType *)bundle) == creator) {
|
||||
break;
|
||||
}
|
||||
free(bundle);
|
||||
bundle = NULL;
|
||||
}
|
||||
free(bundles);
|
||||
if (bundle == NULL) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// read bundle
|
||||
int iconID = [self iconFamilyIDForType:'APPL' inBundle:bundle inResourceFile:rfile];
|
||||
free(bundle);
|
||||
if (iconID == NSNotFound) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
// read icon family
|
||||
NSDictionary *iconFamily = [self iconFamilyID:iconID inResourceFile:rfile];
|
||||
|
||||
// create image
|
||||
return [self iconImageFromFamily:iconFamily];
|
||||
}
|
||||
|
||||
- (NSDictionary *)iconFamilyID:(int16_t)famID inResourceFile:(RFILE *)rfile {
|
||||
NSMutableDictionary *iconFamily = [NSMutableDictionary dictionaryWithCapacity:6];
|
||||
NSData *iconData, *maskData;
|
||||
void *iconRsrc;
|
||||
size_t resSize;
|
||||
|
||||
// separate resources
|
||||
const uint32_t iconResourceTypes[] = {'ICN#', 'icl4', 'icl8', 'ics#', 'ics4', 'ics8', 0};
|
||||
for (int i = 0; iconResourceTypes[i]; i++) {
|
||||
iconRsrc = res_read(rfile, iconResourceTypes[i], famID, NULL, 0, 0, &resSize, NULL);
|
||||
if (iconRsrc == NULL) {
|
||||
continue;
|
||||
}
|
||||
[iconFamily setObject:[NSData dataWithBytes:iconRsrc length:resSize] forKey:[NSString stringWithFormat:@"%c%c%c%c", TYPECHARS(iconResourceTypes[i])]];
|
||||
free(iconRsrc);
|
||||
}
|
||||
|
||||
// mask pseudo-resources
|
||||
if ((iconData = [iconFamily objectForKey:@"ICN#"])) {
|
||||
maskData = [iconData subdataWithRange:NSMakeRange(0x80, 0x80)];
|
||||
[iconFamily setObject:maskData forKey:@"IMK#"];
|
||||
}
|
||||
if ((iconData = [iconFamily objectForKey:@"ics#"])) {
|
||||
maskData = [iconData subdataWithRange:NSMakeRange(0x20, 0x20)];
|
||||
[iconFamily setObject:maskData forKey:@"imk#"];
|
||||
}
|
||||
|
||||
return iconFamily;
|
||||
}
|
||||
|
||||
- (int)iconFamilyIDForType:(OSType)type inBundle:(void *)bndl inResourceFile:(RFILE *)rfile {
|
||||
short numIconFamilies = RSHORT(bndl, 0x0C) + 1;
|
||||
short *iconFamily = (short *)(bndl + 0x0E);
|
||||
short numFileRefs = RSHORT(bndl, (numIconFamilies * 4) + 0x12) + 1;
|
||||
short *fileRef = (short *)(bndl + (numIconFamilies * 4) + 0x14);
|
||||
|
||||
// find FREF for APPL type
|
||||
short localIconID;
|
||||
void *FREF = NULL;
|
||||
for (int i = 0; i < 2 * numFileRefs; i += 2) {
|
||||
FREF = res_read(rfile, 'FREF', (int)ntohs(fileRef[i + 1]), NULL, 0, 0, NULL, NULL);
|
||||
if (FREF == NULL || RLONG(FREF, 0) == 'APPL') {
|
||||
break;
|
||||
}
|
||||
free(FREF);
|
||||
FREF = NULL;
|
||||
}
|
||||
if (FREF == NULL) {
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
// read FREF
|
||||
localIconID = RSHORT(FREF, 4);
|
||||
free(FREF);
|
||||
|
||||
// find resource ID for local ID
|
||||
for (int i = 0; i < 2 * numIconFamilies; i += 2) {
|
||||
if (ntohs(iconFamily[i]) == localIconID) {
|
||||
return (int)ntohs(iconFamily[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
- (CGImageRef)iconImageFromFamily:(NSDictionary *)iconFamily {
|
||||
NSData *iconData, *iconMask;
|
||||
if ((iconMask = [iconFamily objectForKey:@"IMK#"])) {
|
||||
// has large mask, find best large icon
|
||||
if ((iconData = [iconFamily objectForKey:@"icl8"])) {
|
||||
return [self iconImageWithData:iconData mask:iconMask size:32 depth:8];
|
||||
} else if ((iconData = [iconFamily objectForKey:@"icl4"])) {
|
||||
return [self iconImageWithData:iconData mask:iconMask size:32 depth:4];
|
||||
} else {
|
||||
iconData = [iconFamily objectForKey:@"ICN#"];
|
||||
}
|
||||
return [self iconImageWithData:iconData mask:iconMask size:32 depth:1];
|
||||
} else if ((iconMask = [iconFamily objectForKey:@"imk#"])) {
|
||||
// has small mask, find best small icon
|
||||
if ((iconData = [iconFamily objectForKey:@"ics8"])) {
|
||||
return [self iconImageWithData:iconData mask:iconMask size:32 depth:8];
|
||||
} else if ((iconData = [iconFamily objectForKey:@"ics4"])) {
|
||||
return [self iconImageWithData:iconData mask:iconMask size:32 depth:4];
|
||||
} else {
|
||||
iconData = [iconFamily objectForKey:@"ics#"];
|
||||
}
|
||||
return [self iconImageWithData:iconData mask:iconMask size:32 depth:1];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
- (CGImageRef)iconImageWithData:(NSData *)iconData mask:(NSData *)iconMask size:(int)size depth:(int)depth {
|
||||
if (iconData == nil || iconMask == nil) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// convert to ARGB
|
||||
#define _iSETPIXELRGB(px, py, sa, srgb) \
|
||||
data[(4 * (px + (py * size))) + 0] = sa; \
|
||||
data[(4 * (px + (py * size))) + 1] = ((srgb >> 16) & 0xFF); \
|
||||
data[(4 * (px + (py * size))) + 2] = ((srgb >> 8) & 0xFF); \
|
||||
data[(4 * (px + (py * size))) + 3] = (srgb & 0xFF)
|
||||
|
||||
CFMutableDataRef pixels = CFDataCreateMutable(kCFAllocatorDefault, 4 * size * size);
|
||||
CFDataSetLength(pixels, 4 * size * size);
|
||||
unsigned char *data = CFDataGetMutableBytePtr(pixels);
|
||||
const unsigned char *pixelData = [iconData bytes];
|
||||
const unsigned char *maskData = [iconMask bytes];
|
||||
int m, mxy, pxy, rgb;
|
||||
if (pixels == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
switch (depth) {
|
||||
case 1:
|
||||
// 1-bit
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
mxy = pxy = (y * (size / 8)) + (x / 8);
|
||||
m = ((maskData[mxy] >> (7 - (x % 8))) & 0x01) ? 0xFF : 0x00;
|
||||
rgb = ctb1[((pixelData[pxy] >> (7 - (x % 8))) & 0x01)];
|
||||
_iSETPIXELRGB(x, y, m, rgb);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
// 4-bit
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
mxy = (y * (size / 8)) + (x / 8);
|
||||
pxy = (y * (size / 2)) + (x / 2);
|
||||
m = ((maskData[mxy] >> (7 - (x % 8))) & 0x01) ? 0xFF : 0x00;
|
||||
rgb = ctb4[(pixelData[pxy] >> 4 * (1 - x % 2)) & 0x0F];
|
||||
_iSETPIXELRGB(x, y, m, rgb);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
// 8-bit
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
mxy = (y * (size / 8)) + (x / 8);
|
||||
pxy = (y * size) + x;
|
||||
m = ((maskData[mxy] >> (7 - (x % 8))) & 0x01) ? 0xFF : 0x00;
|
||||
rgb = ctb8[pixelData[pxy]];
|
||||
_iSETPIXELRGB(x, y, m, rgb);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// create image
|
||||
CGDataProviderRef provider = CGDataProviderCreateWithCFData(pixels);
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGImageRef image = CGImageCreate(size, size, 8, 32, size * 4, colorSpace, kCGImageAlphaFirst | kCGBitmapByteOrder32Big, provider, NULL, false, kCGRenderingIntentDefault);
|
||||
CGDataProviderRelease(provider);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
CFRelease(pixels);
|
||||
return image;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: acconfig.h,v 1.5 1998/04/11 08:27:11 rob Exp $
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Definitions selected automatically by `configure' *
|
||||
*****************************************************************************/
|
||||
@TOP@
|
||||
|
||||
/* Define if you want to enable diagnostic debugging support. */
|
||||
#undef DEBUG
|
||||
|
||||
@BOTTOM@
|
||||
|
||||
/*****************************************************************************
|
||||
* End of automatically configured definitions *
|
||||
*****************************************************************************/
|
||||
|
||||
# ifdef DEBUG
|
||||
# include <stdio.h>
|
||||
# endif
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: apple.h,v 1.1 1998/04/11 08:27:11 rob Exp $
|
||||
*/
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#define Str15 _Str15
|
||||
#define Str31 _Str31
|
||||
#define OSType _OSType
|
||||
#define Rect _Rect
|
||||
#define Point _Point
|
||||
#endif
|
||||
|
||||
typedef signed char Char;
|
||||
typedef unsigned char UChar;
|
||||
typedef signed char SignedByte;
|
||||
typedef signed short Integer;
|
||||
typedef unsigned short UInteger;
|
||||
typedef signed long LongInt;
|
||||
typedef unsigned long ULongInt;
|
||||
typedef char Str15[16];
|
||||
typedef char Str31[32];
|
||||
typedef long OSType;
|
||||
|
||||
typedef struct {
|
||||
Integer sbSig; /* device signature (should be 0x4552) */
|
||||
Integer sbBlkSize; /* block size of the device (in bytes) */
|
||||
LongInt sbBlkCount; /* number of blocks on the device */
|
||||
Integer sbDevType; /* reserved */
|
||||
Integer sbDevId; /* reserved */
|
||||
LongInt sbData; /* reserved */
|
||||
Integer sbDrvrCount; /* number of driver descriptor entries */
|
||||
LongInt ddBlock; /* first driver's starting block */
|
||||
Integer ddSize; /* size of the driver, in 512-byte blocks */
|
||||
Integer ddType; /* driver operating system type (MacOS = 1) */
|
||||
Integer ddPad[243]; /* additional drivers, if any */
|
||||
} Block0;
|
||||
|
||||
typedef struct {
|
||||
Integer pmSig; /* partition signature (0x504d or 0x5453) */
|
||||
Integer pmSigPad; /* reserved */
|
||||
LongInt pmMapBlkCnt; /* number of blocks in partition map */
|
||||
LongInt pmPyPartStart; /* first physical block of partition */
|
||||
LongInt pmPartBlkCnt; /* number of blocks in partition */
|
||||
Char pmPartName[33]; /* partition name */
|
||||
Char pmParType[33]; /* partition type */
|
||||
LongInt pmLgDataStart; /* first logical block of data area */
|
||||
LongInt pmDataCnt; /* number of blocks in data area */
|
||||
LongInt pmPartStatus; /* partition status information */
|
||||
LongInt pmLgBootStart; /* first logical block of boot code */
|
||||
LongInt pmBootSize; /* size of boot code, in bytes */
|
||||
LongInt pmBootAddr; /* boot code load address */
|
||||
LongInt pmBootAddr2; /* reserved */
|
||||
LongInt pmBootEntry; /* boot code entry point */
|
||||
LongInt pmBootEntry2; /* reserved */
|
||||
LongInt pmBootCksum; /* boot code checksum */
|
||||
Char pmProcessor[17];/* processor type */
|
||||
Integer pmPad[188]; /* reserved */
|
||||
} Partition;
|
||||
|
||||
typedef struct {
|
||||
Integer bbID; /* boot blocks signature */
|
||||
LongInt bbEntry; /* entry point to boot code */
|
||||
Integer bbVersion; /* boot blocks version number */
|
||||
Integer bbPageFlags; /* used internally */
|
||||
Str15 bbSysName; /* System filename */
|
||||
Str15 bbShellName; /* Finder filename */
|
||||
Str15 bbDbg1Name; /* debugger filename */
|
||||
Str15 bbDbg2Name; /* debugger filename */
|
||||
Str15 bbScreenName; /* name of startup screen */
|
||||
Str15 bbHelloName; /* name of startup program */
|
||||
Str15 bbScrapName; /* name of system scrap file */
|
||||
Integer bbCntFCBs; /* number of FCBs to allocate */
|
||||
Integer bbCntEvts; /* number of event queue elements */
|
||||
LongInt bb128KSHeap; /* system heap size on 128K Mac */
|
||||
LongInt bb256KSHeap; /* used internally */
|
||||
LongInt bbSysHeapSize; /* system heap size on all machines */
|
||||
Integer filler; /* reserved */
|
||||
LongInt bbSysHeapExtra; /* additional system heap space */
|
||||
LongInt bbSysHeapFract; /* fraction of RAM for system heap */
|
||||
} BootBlkHdr;
|
||||
|
||||
typedef struct {
|
||||
UInteger xdrStABN; /* first allocation block */
|
||||
UInteger xdrNumABlks; /* number of allocation blocks */
|
||||
} ExtDescriptor;
|
||||
|
||||
typedef ExtDescriptor ExtDataRec[3];
|
||||
|
||||
typedef struct {
|
||||
SignedByte xkrKeyLen; /* key length */
|
||||
SignedByte xkrFkType; /* fork type (0x00/0xff == data/resource */
|
||||
ULongInt xkrFNum; /* file number */
|
||||
UInteger xkrFABN; /* starting file allocation block */
|
||||
} ExtKeyRec;
|
||||
|
||||
typedef struct {
|
||||
SignedByte ckrKeyLen; /* key length */
|
||||
SignedByte ckrResrv1; /* reserved */
|
||||
ULongInt ckrParID; /* parent directory ID */
|
||||
Str31 ckrCName; /* catalog node name */
|
||||
} CatKeyRec;
|
||||
|
||||
typedef struct {
|
||||
Integer v; /* vertical coordinate */
|
||||
Integer h; /* horizontal coordinate */
|
||||
} Point;
|
||||
|
||||
typedef struct {
|
||||
Integer top; /* top edge of rectangle */
|
||||
Integer left; /* left edge */
|
||||
Integer bottom; /* bottom edge */
|
||||
Integer right; /* right edge */
|
||||
} Rect;
|
||||
|
||||
typedef struct {
|
||||
Rect frRect; /* folder's rectangle */
|
||||
Integer frFlags; /* flags */
|
||||
Point frLocation; /* folder's location */
|
||||
Integer frView; /* folder's view */
|
||||
} DInfo;
|
||||
|
||||
typedef struct {
|
||||
Point frScroll; /* scroll position */
|
||||
LongInt frOpenChain; /* directory ID chain of open folders */
|
||||
Integer frUnused; /* reserved */
|
||||
Integer frComment; /* comment ID */
|
||||
LongInt frPutAway; /* directory ID */
|
||||
} DXInfo;
|
||||
|
||||
typedef struct {
|
||||
OSType fdType; /* file type */
|
||||
OSType fdCreator; /* file's creator */
|
||||
Integer fdFlags; /* flags */
|
||||
Point fdLocation; /* file's location */
|
||||
Integer fdFldr; /* file's window */
|
||||
} FInfo;
|
||||
|
||||
typedef struct {
|
||||
Integer fdIconID; /* icon ID */
|
||||
Integer fdUnused[4]; /* reserved */
|
||||
Integer fdComment; /* comment ID */
|
||||
LongInt fdPutAway; /* home directory ID */
|
||||
} FXInfo;
|
||||
|
||||
typedef struct {
|
||||
Integer drSigWord; /* volume signature (0x4244 for HFS) */
|
||||
LongInt drCrDate; /* date and time of volume creation */
|
||||
LongInt drLsMod; /* date and time of last modification */
|
||||
Integer drAtrb; /* volume attributes */
|
||||
UInteger drNmFls; /* number of files in root directory */
|
||||
UInteger drVBMSt; /* first block of volume bit map (always 3) */
|
||||
UInteger drAllocPtr; /* start of next allocation search */
|
||||
UInteger drNmAlBlks; /* number of allocation blocks in volume */
|
||||
ULongInt drAlBlkSiz; /* size (in bytes) of allocation blocks */
|
||||
ULongInt drClpSiz; /* default clump size */
|
||||
UInteger drAlBlSt; /* first allocation block in volume */
|
||||
LongInt drNxtCNID; /* next unused catalog node ID (dir/file ID) */
|
||||
UInteger drFreeBks; /* number of unused allocation blocks */
|
||||
char drVN[28]; /* volume name (1-27 chars) */
|
||||
LongInt drVolBkUp; /* date and time of last backup */
|
||||
Integer drVSeqNum; /* volume backup sequence number */
|
||||
ULongInt drWrCnt; /* volume write count */
|
||||
ULongInt drXTClpSiz; /* clump size for extents overflow file */
|
||||
ULongInt drCTClpSiz; /* clump size for catalog file */
|
||||
UInteger drNmRtDirs; /* number of directories in root directory */
|
||||
ULongInt drFilCnt; /* number of files in volume */
|
||||
ULongInt drDirCnt; /* number of directories in volume */
|
||||
LongInt drFndrInfo[8]; /* information used by the Finder */
|
||||
UInteger drEmbedSigWord; /* type of embedded volume */
|
||||
ExtDescriptor drEmbedExtent; /* location of embedded volume */
|
||||
ULongInt drXTFlSize; /* size (in bytes) of extents overflow file */
|
||||
ExtDataRec drXTExtRec; /* first extent record for extents file */
|
||||
ULongInt drCTFlSize; /* size (in bytes) of catalog file */
|
||||
ExtDataRec drCTExtRec; /* first extent record for catalog file */
|
||||
} MDB;
|
||||
|
||||
typedef enum {
|
||||
cdrDirRec = 1,
|
||||
cdrFilRec = 2,
|
||||
cdrThdRec = 3,
|
||||
cdrFThdRec = 4
|
||||
} CatDataType;
|
||||
|
||||
typedef struct {
|
||||
SignedByte cdrType; /* record type */
|
||||
SignedByte cdrResrv2; /* reserved */
|
||||
union {
|
||||
struct { /* cdrDirRec */
|
||||
Integer dirFlags; /* directory flags */
|
||||
UInteger dirVal; /* directory valence */
|
||||
ULongInt dirDirID; /* directory ID */
|
||||
LongInt dirCrDat; /* date and time of creation */
|
||||
LongInt dirMdDat; /* date and time of last modification */
|
||||
LongInt dirBkDat; /* date and time of last backup */
|
||||
DInfo dirUsrInfo; /* Finder information */
|
||||
DXInfo dirFndrInfo; /* additional Finder information */
|
||||
LongInt dirResrv[4]; /* reserved */
|
||||
} dir;
|
||||
struct { /* cdrFilRec */
|
||||
SignedByte
|
||||
filFlags; /* file flags */
|
||||
SignedByte
|
||||
filTyp; /* file type */
|
||||
FInfo filUsrWds; /* Finder information */
|
||||
ULongInt filFlNum; /* file ID */
|
||||
UInteger filStBlk; /* first alloc block of data fork */
|
||||
ULongInt filLgLen; /* logical EOF of data fork */
|
||||
ULongInt filPyLen; /* physical EOF of data fork */
|
||||
UInteger filRStBlk; /* first alloc block of resource fork */
|
||||
ULongInt filRLgLen; /* logical EOF of resource fork */
|
||||
ULongInt filRPyLen; /* physical EOF of resource fork */
|
||||
LongInt filCrDat; /* date and time of creation */
|
||||
LongInt filMdDat; /* date and time of last modification */
|
||||
LongInt filBkDat; /* date and time of last backup */
|
||||
FXInfo filFndrInfo; /* additional Finder information */
|
||||
UInteger filClpSize; /* file clump size */
|
||||
ExtDataRec
|
||||
filExtRec; /* first data fork extent record */
|
||||
ExtDataRec
|
||||
filRExtRec; /* first resource fork extent record */
|
||||
LongInt filResrv; /* reserved */
|
||||
} fil;
|
||||
struct { /* cdrThdRec */
|
||||
LongInt thdResrv[2]; /* reserved */
|
||||
ULongInt thdParID; /* parent ID for this directory */
|
||||
Str31 thdCName; /* name of this directory */
|
||||
} dthd;
|
||||
struct { /* cdrFThdRec */
|
||||
LongInt fthdResrv[2]; /* reserved */
|
||||
ULongInt fthdParID; /* parent ID for this file */
|
||||
Str31 fthdCName; /* name of this file */
|
||||
} fthd;
|
||||
} u;
|
||||
} CatDataRec;
|
||||
|
||||
typedef struct {
|
||||
ULongInt ndFLink; /* forward link */
|
||||
ULongInt ndBLink; /* backward link */
|
||||
SignedByte ndType; /* node type */
|
||||
SignedByte ndNHeight; /* node level */
|
||||
UInteger ndNRecs; /* number of records in node */
|
||||
Integer ndResv2; /* reserved */
|
||||
} NodeDescriptor;
|
||||
|
||||
enum {
|
||||
ndIndxNode = (SignedByte) 0x00,
|
||||
ndHdrNode = (SignedByte) 0x01,
|
||||
ndMapNode = (SignedByte) 0x02,
|
||||
ndLeafNode = (SignedByte) 0xff
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
UInteger bthDepth; /* current depth of tree */
|
||||
ULongInt bthRoot; /* number of root node */
|
||||
ULongInt bthNRecs; /* number of leaf records in tree */
|
||||
ULongInt bthFNode; /* number of first leaf node */
|
||||
ULongInt bthLNode; /* number of last leaf node */
|
||||
UInteger bthNodeSize; /* size of a node */
|
||||
UInteger bthKeyLen; /* maximum length of a key */
|
||||
ULongInt bthNNodes; /* total number of nodes in tree */
|
||||
ULongInt bthFree; /* number of free nodes */
|
||||
SignedByte bthResv[76]; /* reserved */
|
||||
} BTHdrRec;
|
|
@ -0,0 +1,807 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: block.c,v 1.11 1998/11/02 22:08:52 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <errno.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "volume.h"
|
||||
# include "block.h"
|
||||
# include "os.h"
|
||||
|
||||
# define INUSE(b) ((b)->flags & HFS_BUCKET_INUSE)
|
||||
# define DIRTY(b) ((b)->flags & HFS_BUCKET_DIRTY)
|
||||
|
||||
/*
|
||||
* NAME: block->init()
|
||||
* DESCRIPTION: initialize a volume's block cache
|
||||
*/
|
||||
int b_init(hfsvol *vol)
|
||||
{
|
||||
bcache *cache;
|
||||
int i;
|
||||
|
||||
ASSERT(vol->cache == 0);
|
||||
|
||||
cache = ALLOC(bcache, 1);
|
||||
if (cache == 0)
|
||||
ERROR(ENOMEM, 0);
|
||||
|
||||
vol->cache = cache;
|
||||
|
||||
cache->vol = vol;
|
||||
cache->tail = &cache->chain[HFS_CACHESZ - 1];
|
||||
|
||||
cache->hits = 0;
|
||||
cache->misses = 0;
|
||||
|
||||
for (i = 0; i < HFS_CACHESZ; ++i)
|
||||
{
|
||||
bucket *b = &cache->chain[i];
|
||||
|
||||
b->flags = 0;
|
||||
b->count = 0;
|
||||
|
||||
b->bnum = 0;
|
||||
b->data = &cache->pool[i];
|
||||
|
||||
b->cnext = b + 1;
|
||||
b->cprev = b - 1;
|
||||
|
||||
b->hnext = 0;
|
||||
b->hprev = 0;
|
||||
}
|
||||
|
||||
cache->chain[0].cprev = cache->tail;
|
||||
cache->tail->cnext = &cache->chain[0];
|
||||
|
||||
for (i = 0; i < HFS_HASHSZ; ++i)
|
||||
cache->hash[i] = 0;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
# ifdef DEBUG
|
||||
/*
|
||||
* NAME: block->showstats()
|
||||
* DESCRIPTION: output cache hit/miss ratio
|
||||
*/
|
||||
void b_showstats(const bcache *cache)
|
||||
{
|
||||
fprintf(stderr, "BLOCK: CACHE vol 0x%lx \"%s\" hit/miss ratio = %.3f\n",
|
||||
(unsigned long) cache->vol, cache->vol->mdb.drVN,
|
||||
(float) cache->hits / (float) cache->misses);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->dumpcache()
|
||||
* DESCRIPTION: dump the cache tables for a volume
|
||||
*/
|
||||
void b_dumpcache(const bcache *cache)
|
||||
{
|
||||
const bucket *b;
|
||||
int i;
|
||||
|
||||
fprintf(stderr, "BLOCK CACHE DUMP:\n");
|
||||
|
||||
for (i = 0, b = cache->tail->cnext; i < HFS_CACHESZ; ++i, b = b->cnext)
|
||||
{
|
||||
if (INUSE(b))
|
||||
{
|
||||
fprintf(stderr, "\t %lu", b->bnum);
|
||||
if (DIRTY(b))
|
||||
fprintf(stderr, "*");
|
||||
|
||||
fprintf(stderr, ":%u", b->count);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
fprintf(stderr, "BLOCK HASH DUMP:\n");
|
||||
|
||||
for (i = 0; i < HFS_HASHSZ; ++i)
|
||||
{
|
||||
int seen = 0;
|
||||
|
||||
for (b = cache->hash[i]; b; b = b->hnext)
|
||||
{
|
||||
if (! seen)
|
||||
fprintf(stderr, " %d:", i);
|
||||
|
||||
if (INUSE(b))
|
||||
{
|
||||
fprintf(stderr, " %lu", b->bnum);
|
||||
if (DIRTY(b))
|
||||
fprintf(stderr, "*");
|
||||
|
||||
fprintf(stderr, ":%u", b->count);
|
||||
}
|
||||
|
||||
seen = 1;
|
||||
}
|
||||
|
||||
if (seen)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
# endif
|
||||
|
||||
/*
|
||||
* NAME: fillchain()
|
||||
* DESCRIPTION: fill a chain of bucket buffers with a single read
|
||||
*/
|
||||
static
|
||||
int fillchain(hfsvol *vol, bucket **bptr, unsigned int *count)
|
||||
{
|
||||
bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
|
||||
unsigned long bnum;
|
||||
unsigned int len, i;
|
||||
|
||||
for (len = 0; len < HFS_BLOCKBUFSZ &&
|
||||
(unsigned int) (bptr - start) < *count; ++bptr)
|
||||
{
|
||||
if (INUSE(*bptr))
|
||||
continue;
|
||||
|
||||
if (len > 0 && (*bptr)->bnum != bnum)
|
||||
break;
|
||||
|
||||
blist[len++] = *bptr;
|
||||
bnum = (*bptr)->bnum + 1;
|
||||
}
|
||||
|
||||
*count = bptr - start;
|
||||
|
||||
if (len == 0)
|
||||
goto done;
|
||||
else if (len == 1)
|
||||
{
|
||||
if (b_readpb(vol, vol->vstart + blist[0]->bnum,
|
||||
blist[0]->data, 1) == -1)
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
block buffer[HFS_BLOCKBUFSZ];
|
||||
|
||||
if (b_readpb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
memcpy(blist[i]->data, buffer[i], HFS_BLOCKSZ);
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
{
|
||||
blist[i]->flags |= HFS_BUCKET_INUSE;
|
||||
blist[i]->flags &= ~HFS_BUCKET_DIRTY;
|
||||
}
|
||||
|
||||
done:
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: flushchain()
|
||||
* DESCRIPTION: store a chain of bucket buffers with a single write
|
||||
*/
|
||||
static
|
||||
int flushchain(hfsvol *vol, bucket **bptr, unsigned int *count)
|
||||
{
|
||||
bucket *blist[HFS_BLOCKBUFSZ], **start = bptr;
|
||||
unsigned long bnum;
|
||||
unsigned int len, i;
|
||||
|
||||
for (len = 0; len < HFS_BLOCKBUFSZ &&
|
||||
(unsigned int) (bptr - start) < *count; ++bptr)
|
||||
{
|
||||
if (! INUSE(*bptr) || ! DIRTY(*bptr))
|
||||
continue;
|
||||
|
||||
if (len > 0 && (*bptr)->bnum != bnum)
|
||||
break;
|
||||
|
||||
blist[len++] = *bptr;
|
||||
bnum = (*bptr)->bnum + 1;
|
||||
}
|
||||
|
||||
*count = bptr - start;
|
||||
|
||||
if (len == 0)
|
||||
goto done;
|
||||
else if (len == 1)
|
||||
{
|
||||
if (b_writepb(vol, vol->vstart + blist[0]->bnum,
|
||||
blist[0]->data, 1) == -1)
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
block buffer[HFS_BLOCKBUFSZ];
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
memcpy(buffer[i], blist[i]->data, HFS_BLOCKSZ);
|
||||
|
||||
if (b_writepb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; ++i)
|
||||
blist[i]->flags &= ~HFS_BUCKET_DIRTY;
|
||||
|
||||
done:
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: compare()
|
||||
* DESCRIPTION: comparison function for qsort of cache bucket pointers
|
||||
*/
|
||||
static
|
||||
int compare(const bucket **b1, const bucket **b2)
|
||||
{
|
||||
long diff;
|
||||
|
||||
diff = (*b1)->bnum - (*b2)->bnum;
|
||||
|
||||
if (diff < 0)
|
||||
return -1;
|
||||
else if (diff > 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: dobuckets()
|
||||
* DESCRIPTION: fill or flush an array of cache buckets to a volume
|
||||
*/
|
||||
static
|
||||
int dobuckets(hfsvol *vol, bucket **chain, unsigned int len,
|
||||
int (*func)(hfsvol *, bucket **, unsigned int *))
|
||||
{
|
||||
unsigned int count, i;
|
||||
int result = 0;
|
||||
|
||||
qsort(chain, len, sizeof(*chain),
|
||||
(int (*)(const void *, const void *)) compare);
|
||||
|
||||
for (i = 0; i < len; i += count)
|
||||
{
|
||||
count = len - i;
|
||||
if (func(vol, chain + i, &count) == -1)
|
||||
result = -1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
# define fillbuckets(vol, chain, len) dobuckets(vol, chain, len, fillchain)
|
||||
# define flushbuckets(vol, chain, len) dobuckets(vol, chain, len, flushchain)
|
||||
|
||||
/*
|
||||
* NAME: block->flush()
|
||||
* DESCRIPTION: commit dirty cache blocks to a volume
|
||||
*/
|
||||
int b_flush(hfsvol *vol)
|
||||
{
|
||||
bcache *cache = vol->cache;
|
||||
bucket *chain[HFS_CACHESZ];
|
||||
int i;
|
||||
|
||||
if (cache == 0 || (vol->flags & HFS_VOL_READONLY))
|
||||
goto done;
|
||||
|
||||
for (i = 0; i < HFS_CACHESZ; ++i)
|
||||
chain[i] = &cache->chain[i];
|
||||
|
||||
if (flushbuckets(vol, chain, HFS_CACHESZ) == -1)
|
||||
goto fail;
|
||||
|
||||
done:
|
||||
# ifdef DEBUG
|
||||
if (cache)
|
||||
b_showstats(cache);
|
||||
# endif
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->finish()
|
||||
* DESCRIPTION: commit and free a volume's block cache
|
||||
*/
|
||||
int b_finish(hfsvol *vol)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (vol->cache == 0)
|
||||
goto done;
|
||||
|
||||
# ifdef DEBUG
|
||||
b_dumpcache(vol->cache);
|
||||
# endif
|
||||
|
||||
result = b_flush(vol);
|
||||
|
||||
FREE(vol->cache);
|
||||
vol->cache = 0;
|
||||
|
||||
done:
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: findbucket()
|
||||
* DESCRIPTION: locate a bucket in the cache, and/or its hash slot
|
||||
*/
|
||||
static
|
||||
bucket *findbucket(bcache *cache, unsigned long bnum, bucket ***hslot)
|
||||
{
|
||||
bucket *b;
|
||||
|
||||
*hslot = &cache->hash[bnum & (HFS_HASHSZ - 1)];
|
||||
|
||||
for (b = **hslot; b; b = b->hnext)
|
||||
{
|
||||
if (INUSE(b) && b->bnum == bnum)
|
||||
break;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: reuse()
|
||||
* DESCRIPTION: free a bucket for reuse, flushing if necessary
|
||||
*/
|
||||
static
|
||||
int reuse(bcache *cache, bucket *b, unsigned long bnum)
|
||||
{
|
||||
bucket *chain[HFS_BLOCKBUFSZ], *bptr;
|
||||
int i;
|
||||
|
||||
# ifdef DEBUG
|
||||
if (INUSE(b))
|
||||
fprintf(stderr, "BLOCK: CACHE reusing bucket containing "
|
||||
"vol 0x%lx block %lu:%u\n",
|
||||
(unsigned long) cache->vol, b->bnum, b->count);
|
||||
# endif
|
||||
|
||||
if (INUSE(b) && DIRTY(b))
|
||||
{
|
||||
/* flush most recently unused buckets */
|
||||
|
||||
for (bptr = b, i = 0; i < HFS_BLOCKBUFSZ; ++i)
|
||||
{
|
||||
chain[i] = bptr;
|
||||
bptr = bptr->cprev;
|
||||
}
|
||||
|
||||
if (flushbuckets(cache->vol, chain, HFS_BLOCKBUFSZ) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
b->flags &= ~HFS_BUCKET_INUSE;
|
||||
b->count = 1;
|
||||
b->bnum = bnum;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: cplace()
|
||||
* DESCRIPTION: move a bucket to an appropriate place near head of the chain
|
||||
*/
|
||||
static
|
||||
void cplace(bcache *cache, bucket *b)
|
||||
{
|
||||
bucket *p;
|
||||
|
||||
for (p = cache->tail->cnext; p->count > 1; p = p->cnext)
|
||||
--p->count;
|
||||
|
||||
b->cnext->cprev = b->cprev;
|
||||
b->cprev->cnext = b->cnext;
|
||||
|
||||
if (cache->tail == b)
|
||||
cache->tail = b->cprev;
|
||||
|
||||
b->cprev = p->cprev;
|
||||
b->cnext = p;
|
||||
|
||||
p->cprev->cnext = b;
|
||||
p->cprev = b;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: hplace()
|
||||
* DESCRIPTION: move a bucket to the head of its hash slot
|
||||
*/
|
||||
static
|
||||
void hplace(bucket **hslot, bucket *b)
|
||||
{
|
||||
if (*hslot != b)
|
||||
{
|
||||
if (b->hprev)
|
||||
*b->hprev = b->hnext;
|
||||
if (b->hnext)
|
||||
b->hnext->hprev = b->hprev;
|
||||
|
||||
b->hprev = hslot;
|
||||
b->hnext = *hslot;
|
||||
|
||||
if (*hslot)
|
||||
(*hslot)->hprev = &b->hnext;
|
||||
|
||||
*hslot = b;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: getbucket()
|
||||
* DESCRIPTION: fetch a bucket from the cache, or an empty one to be filled
|
||||
*/
|
||||
static
|
||||
bucket *getbucket(bcache *cache, unsigned long bnum, int fill)
|
||||
{
|
||||
bucket **hslot, *b, *p, *bptr,
|
||||
*chain[HFS_BLOCKBUFSZ], **slots[HFS_BLOCKBUFSZ];
|
||||
|
||||
b = findbucket(cache, bnum, &hslot);
|
||||
|
||||
if (b)
|
||||
{
|
||||
/* cache hit; move towards head of cache chain */
|
||||
|
||||
++cache->hits;
|
||||
|
||||
if (++b->count > b->cprev->count &&
|
||||
b != cache->tail->cnext)
|
||||
{
|
||||
p = b->cprev;
|
||||
|
||||
p->cprev->cnext = b;
|
||||
b->cnext->cprev = p;
|
||||
|
||||
p->cnext = b->cnext;
|
||||
b->cprev = p->cprev;
|
||||
|
||||
p->cprev = b;
|
||||
b->cnext = p;
|
||||
|
||||
if (cache->tail == b)
|
||||
cache->tail = p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* cache miss; reuse least-used cache bucket */
|
||||
|
||||
++cache->misses;
|
||||
|
||||
b = cache->tail;
|
||||
|
||||
if (reuse(cache, b, bnum) == -1)
|
||||
goto fail;
|
||||
|
||||
if (fill)
|
||||
{
|
||||
unsigned int len = 0;
|
||||
|
||||
chain[len] = b;
|
||||
slots[len++] = hslot;
|
||||
|
||||
for (bptr = b->cprev;
|
||||
len < (HFS_BLOCKBUFSZ >> 1) && ++bnum < cache->vol->vlen;
|
||||
bptr = bptr->cprev)
|
||||
{
|
||||
if (findbucket(cache, bnum, &hslot))
|
||||
break;
|
||||
|
||||
if (reuse(cache, bptr, bnum) == -1)
|
||||
goto fail;
|
||||
|
||||
chain[len] = bptr;
|
||||
slots[len++] = hslot;
|
||||
}
|
||||
|
||||
if (fillbuckets(cache->vol, chain, len) == -1)
|
||||
goto fail;
|
||||
|
||||
while (--len)
|
||||
{
|
||||
cplace(cache, chain[len]);
|
||||
hplace(slots[len], chain[len]);
|
||||
}
|
||||
|
||||
hslot = slots[0];
|
||||
}
|
||||
|
||||
/* move bucket to appropriate place in chain */
|
||||
|
||||
cplace(cache, b);
|
||||
}
|
||||
|
||||
/* insert at front of hash chain */
|
||||
|
||||
hplace(hslot, b);
|
||||
|
||||
return b;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->readpb()
|
||||
* DESCRIPTION: read blocks from the physical medium (bypassing cache)
|
||||
*/
|
||||
int b_readpb(hfsvol *vol, unsigned long bnum, block *bp, unsigned int blen)
|
||||
{
|
||||
unsigned long nblocks;
|
||||
|
||||
# ifdef DEBUG
|
||||
fprintf(stderr, "BLOCK: READ vol 0x%lx block %lu",
|
||||
(unsigned long) vol, bnum);
|
||||
if (blen > 1)
|
||||
fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
|
||||
else
|
||||
fprintf(stderr, "\n");
|
||||
# endif
|
||||
|
||||
nblocks = os_seek(&vol->priv, vol->base, bnum);
|
||||
if (nblocks == (unsigned long) -1)
|
||||
goto fail;
|
||||
|
||||
if (nblocks != bnum)
|
||||
ERROR(EIO, "block seek failed for read");
|
||||
|
||||
nblocks = os_read(&vol->priv, bp, blen);
|
||||
if (nblocks == (unsigned long) -1)
|
||||
goto fail;
|
||||
|
||||
if (nblocks != blen)
|
||||
ERROR(EIO, "incomplete block read");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->writepb()
|
||||
* DESCRIPTION: write blocks to the physical medium (bypassing cache)
|
||||
*/
|
||||
int b_writepb(hfsvol *vol, unsigned long bnum, const block *bp,
|
||||
unsigned int blen)
|
||||
{
|
||||
unsigned long nblocks;
|
||||
|
||||
# ifdef DEBUG
|
||||
fprintf(stderr, "BLOCK: WRITE vol 0x%lx block %lu",
|
||||
(unsigned long) vol, bnum);
|
||||
if (blen > 1)
|
||||
fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1);
|
||||
else
|
||||
fprintf(stderr, "\n");
|
||||
# endif
|
||||
|
||||
nblocks = os_seek(&vol->priv, vol->base, bnum);
|
||||
if (nblocks == (unsigned long) -1)
|
||||
goto fail;
|
||||
|
||||
if (nblocks != bnum)
|
||||
ERROR(EIO, "block seek failed for write");
|
||||
|
||||
nblocks = os_write(&vol->priv, bp, blen);
|
||||
if (nblocks == (unsigned long) -1)
|
||||
goto fail;
|
||||
|
||||
if (nblocks != blen)
|
||||
ERROR(EIO, "incomplete block write");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->readlb()
|
||||
* DESCRIPTION: read a logical block from a volume (or from the cache)
|
||||
*/
|
||||
int b_readlb(hfsvol *vol, unsigned long bnum, block *bp)
|
||||
{
|
||||
if (vol->vlen > 0 && bnum >= vol->vlen)
|
||||
ERROR(EIO, "read nonexistent logical block");
|
||||
|
||||
if (vol->cache)
|
||||
{
|
||||
bucket *b;
|
||||
|
||||
b = getbucket(vol->cache, bnum, 1);
|
||||
if (b == 0)
|
||||
goto fail;
|
||||
|
||||
memcpy(bp, b->data, HFS_BLOCKSZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b_readpb(vol, vol->vstart + bnum, bp, 1) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->writelb()
|
||||
* DESCRIPTION: write a logical block to a volume (or to the cache)
|
||||
*/
|
||||
int b_writelb(hfsvol *vol, unsigned long bnum, const block *bp)
|
||||
{
|
||||
if (vol->vlen > 0 && bnum >= vol->vlen)
|
||||
ERROR(EIO, "write nonexistent logical block");
|
||||
|
||||
if (vol->cache)
|
||||
{
|
||||
bucket *b;
|
||||
|
||||
b = getbucket(vol->cache, bnum, 0);
|
||||
if (b == 0)
|
||||
goto fail;
|
||||
|
||||
if (! INUSE(b) ||
|
||||
memcmp(b->data, bp, HFS_BLOCKSZ) != 0)
|
||||
{
|
||||
memcpy(b->data, bp, HFS_BLOCKSZ);
|
||||
b->flags |= HFS_BUCKET_INUSE | HFS_BUCKET_DIRTY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (b_writepb(vol, vol->vstart + bnum, bp, 1) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->readab()
|
||||
* DESCRIPTION: read a block from an allocation block from a volume
|
||||
*/
|
||||
int b_readab(hfsvol *vol, unsigned int anum, unsigned int index, block *bp)
|
||||
{
|
||||
/* verify the allocation block exists and is marked as in-use */
|
||||
|
||||
if (anum >= vol->mdb.drNmAlBlks)
|
||||
ERROR(EIO, "read nonexistent allocation block");
|
||||
else if (vol->vbm && ! BMTST(vol->vbm, anum))
|
||||
ERROR(EIO, "read unallocated block");
|
||||
|
||||
return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->writeab()
|
||||
* DESCRIPTION: write a block to an allocation block to a volume
|
||||
*/
|
||||
int b_writeab(hfsvol *vol,
|
||||
unsigned int anum, unsigned int index, const block *bp)
|
||||
{
|
||||
/* verify the allocation block exists and is marked as in-use */
|
||||
|
||||
if (anum >= vol->mdb.drNmAlBlks)
|
||||
ERROR(EIO, "write nonexistent allocation block");
|
||||
else if (vol->vbm && ! BMTST(vol->vbm, anum))
|
||||
ERROR(EIO, "write unallocated block");
|
||||
|
||||
if (v_dirty(vol) == -1)
|
||||
goto fail;
|
||||
|
||||
return b_writelb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + index, bp);
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: block->size()
|
||||
* DESCRIPTION: return the number of physical blocks on a volume's medium
|
||||
*/
|
||||
unsigned long b_size(hfsvol *vol)
|
||||
{
|
||||
unsigned long low, high, mid;
|
||||
block b;
|
||||
|
||||
high = os_seek(&vol->priv, vol->base, -1);
|
||||
|
||||
if (high != (unsigned long) -1 && high > 0)
|
||||
return high;
|
||||
|
||||
/* manual size detection: first check there is at least 1 block in medium */
|
||||
|
||||
if (b_readpb(vol, 0, &b, 1) == -1)
|
||||
ERROR(EIO, "size of medium indeterminable or empty");
|
||||
|
||||
for (low = 0, high = 2880;
|
||||
high > 0 && b_readpb(vol, high - 1, &b, 1) != -1;
|
||||
high <<= 1)
|
||||
low = high - 1;
|
||||
|
||||
if (high == 0)
|
||||
ERROR(EIO, "size of medium indeterminable or too large");
|
||||
|
||||
/* common case: 1440K floppy */
|
||||
|
||||
if (low == 2879 && b_readpb(vol, 2880, &b, 1) == -1)
|
||||
return 2880;
|
||||
|
||||
/* binary search for other sizes */
|
||||
|
||||
while (low < high - 1)
|
||||
{
|
||||
mid = (low + high) >> 1;
|
||||
|
||||
if (b_readpb(vol, mid, &b, 1) == -1)
|
||||
high = mid;
|
||||
else
|
||||
low = mid;
|
||||
}
|
||||
|
||||
return low + 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: block.h,v 1.10 1998/11/02 22:08:53 rob Exp $
|
||||
*/
|
||||
|
||||
int b_init(hfsvol *);
|
||||
int b_flush(hfsvol *);
|
||||
int b_finish(hfsvol *);
|
||||
|
||||
int b_readpb(hfsvol *, unsigned long, block *, unsigned int);
|
||||
int b_writepb(hfsvol *, unsigned long, const block *, unsigned int);
|
||||
|
||||
int b_readlb(hfsvol *, unsigned long, block *);
|
||||
int b_writelb(hfsvol *, unsigned long, const block *);
|
||||
|
||||
int b_readab(hfsvol *, unsigned int, unsigned int, block *);
|
||||
int b_writeab(hfsvol *, unsigned int, unsigned int, const block *);
|
||||
|
||||
unsigned long b_size(hfsvol *);
|
||||
|
||||
# ifdef DEBUG
|
||||
void b_showstats(const bcache *);
|
||||
void b_dumpcache(const bcache *);
|
||||
# endif
|
|
@ -0,0 +1,700 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: btree.c,v 1.10 1998/11/02 22:08:54 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <errno.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "btree.h"
|
||||
# include "data.h"
|
||||
# include "file.h"
|
||||
# include "block.h"
|
||||
# include "node.h"
|
||||
|
||||
/*
|
||||
* NAME: btree->getnode()
|
||||
* DESCRIPTION: retrieve a numbered node from a B*-tree file
|
||||
*/
|
||||
int bt_getnode(node *np, btree *bt, unsigned long nnum)
|
||||
{
|
||||
block *bp = &np->data;
|
||||
const byte *ptr;
|
||||
int i;
|
||||
|
||||
np->bt = bt;
|
||||
np->nnum = nnum;
|
||||
|
||||
# if 0
|
||||
fprintf(stderr, "BTREE: GET vol \"%s\" btree \"%s\" node %lu\n",
|
||||
bt->f.vol->mdb.drVN, bt->f.name, np->nnum);
|
||||
# endif
|
||||
|
||||
/* verify the node exists and is marked as in-use */
|
||||
|
||||
if (nnum > 0 && nnum >= bt->hdr.bthNNodes)
|
||||
ERROR(EIO, "read nonexistent b*-tree node");
|
||||
else if (bt->map && ! BMTST(bt->map, nnum))
|
||||
ERROR(EIO, "read unallocated b*-tree node");
|
||||
|
||||
if (f_getblock(&bt->f, nnum, bp) == -1)
|
||||
goto fail;
|
||||
|
||||
ptr = *bp;
|
||||
|
||||
d_fetchul(&ptr, &np->nd.ndFLink);
|
||||
d_fetchul(&ptr, &np->nd.ndBLink);
|
||||
d_fetchsb(&ptr, &np->nd.ndType);
|
||||
d_fetchsb(&ptr, &np->nd.ndNHeight);
|
||||
d_fetchuw(&ptr, &np->nd.ndNRecs);
|
||||
d_fetchsw(&ptr, &np->nd.ndResv2);
|
||||
|
||||
if (np->nd.ndNRecs > HFS_MAX_NRECS)
|
||||
ERROR(EIO, "too many b*-tree node records");
|
||||
|
||||
i = np->nd.ndNRecs + 1;
|
||||
|
||||
ptr = *bp + HFS_BLOCKSZ - (2 * i);
|
||||
|
||||
while (i--)
|
||||
d_fetchuw(&ptr, &np->roff[i]);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: btree->putnode()
|
||||
* DESCRIPTION: store a numbered node into a B*-tree file
|
||||
*/
|
||||
int bt_putnode(node *np)
|
||||
{
|
||||
btree *bt = np->bt;
|
||||
block *bp = &np->data;
|
||||
byte *ptr;
|
||||
int i;
|
||||
|
||||
# if 0
|
||||
fprintf(stderr, "BTREE: PUT vol \"%s\" btree \"%s\" node %lu\n",
|
||||
bt->f.vol->mdb.drVN, bt->f.name, np->nnum);
|
||||
# endif
|
||||
|
||||
/* verify the node exists and is marked as in-use */
|
||||
|
||||
if (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes)
|
||||
ERROR(EIO, "write nonexistent b*-tree node");
|
||||
else if (bt->map && ! BMTST(bt->map, np->nnum))
|
||||
ERROR(EIO, "write unallocated b*-tree node");
|
||||
|
||||
ptr = *bp;
|
||||
|
||||
d_storeul(&ptr, np->nd.ndFLink);
|
||||
d_storeul(&ptr, np->nd.ndBLink);
|
||||
d_storesb(&ptr, np->nd.ndType);
|
||||
d_storesb(&ptr, np->nd.ndNHeight);
|
||||
d_storeuw(&ptr, np->nd.ndNRecs);
|
||||
d_storesw(&ptr, np->nd.ndResv2);
|
||||
|
||||
if (np->nd.ndNRecs > HFS_MAX_NRECS)
|
||||
ERROR(EIO, "too many b*-tree node records");
|
||||
|
||||
i = np->nd.ndNRecs + 1;
|
||||
|
||||
ptr = *bp + HFS_BLOCKSZ - (2 * i);
|
||||
|
||||
while (i--)
|
||||
d_storeuw(&ptr, np->roff[i]);
|
||||
|
||||
return f_putblock(&bt->f, np->nnum, bp);
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: btree->readhdr()
|
||||
* DESCRIPTION: read the header node of a B*-tree
|
||||
*/
|
||||
int bt_readhdr(btree *bt)
|
||||
{
|
||||
const byte *ptr;
|
||||
byte *map = 0;
|
||||
int i;
|
||||
unsigned long nnum;
|
||||
|
||||
if (bt_getnode(&bt->hdrnd, bt, 0) == -1)
|
||||
goto fail;
|
||||
|
||||
if (bt->hdrnd.nd.ndType != ndHdrNode ||
|
||||
bt->hdrnd.nd.ndNRecs != 3 ||
|
||||
bt->hdrnd.roff[0] != 0x00e ||
|
||||
bt->hdrnd.roff[1] != 0x078 ||
|
||||
bt->hdrnd.roff[2] != 0x0f8 ||
|
||||
bt->hdrnd.roff[3] != 0x1f8)
|
||||
ERROR(EIO, "malformed b*-tree header node");
|
||||
|
||||
/* read header record */
|
||||
|
||||
ptr = HFS_NODEREC(bt->hdrnd, 0);
|
||||
|
||||
d_fetchuw(&ptr, &bt->hdr.bthDepth);
|
||||
d_fetchul(&ptr, &bt->hdr.bthRoot);
|
||||
d_fetchul(&ptr, &bt->hdr.bthNRecs);
|
||||
d_fetchul(&ptr, &bt->hdr.bthFNode);
|
||||
d_fetchul(&ptr, &bt->hdr.bthLNode);
|
||||
d_fetchuw(&ptr, &bt->hdr.bthNodeSize);
|
||||
d_fetchuw(&ptr, &bt->hdr.bthKeyLen);
|
||||
d_fetchul(&ptr, &bt->hdr.bthNNodes);
|
||||
d_fetchul(&ptr, &bt->hdr.bthFree);
|
||||
|
||||
for (i = 0; i < 76; ++i)
|
||||
d_fetchsb(&ptr, &bt->hdr.bthResv[i]);
|
||||
|
||||
if (bt->hdr.bthNodeSize != HFS_BLOCKSZ)
|
||||
ERROR(EINVAL, "unsupported b*-tree node size");
|
||||
|
||||
/* read map record; construct btree bitmap */
|
||||
/* don't set bt->map until we're done, since getnode() checks it */
|
||||
|
||||
map = ALLOC(byte, HFS_MAP1SZ);
|
||||
if (map == 0)
|
||||
ERROR(ENOMEM, 0);
|
||||
|
||||
memcpy(map, HFS_NODEREC(bt->hdrnd, 2), HFS_MAP1SZ);
|
||||
bt->mapsz = HFS_MAP1SZ;
|
||||
|
||||
/* read continuation map records, if any */
|
||||
|
||||
nnum = bt->hdrnd.nd.ndFLink;
|
||||
|
||||
while (nnum)
|
||||
{
|
||||
node n;
|
||||
byte *newmap;
|
||||
|
||||
if (bt_getnode(&n, bt, nnum) == -1)
|
||||
goto fail;
|
||||
|
||||
if (n.nd.ndType != ndMapNode ||
|
||||
n.nd.ndNRecs != 1 ||
|
||||
n.roff[0] != 0x00e ||
|
||||
n.roff[1] != 0x1fa)
|
||||
ERROR(EIO, "malformed b*-tree map node");
|
||||
|
||||
newmap = REALLOC(map, byte, bt->mapsz + HFS_MAPXSZ);
|
||||
if (newmap == 0)
|
||||
ERROR(ENOMEM, 0);
|
||||
|
||||
map = newmap;
|
||||
|
||||
memcpy(map + bt->mapsz, HFS_NODEREC(n, 0), HFS_MAPXSZ);
|
||||
bt->mapsz += HFS_MAPXSZ;
|
||||
|
||||
nnum = n.nd.ndFLink;
|
||||
}
|
||||
|
||||
bt->map = map;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
FREE(map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: btree->writehdr()
|
||||
* DESCRIPTION: write the header node of a B*-tree
|
||||
*/
|
||||
int bt_writehdr(btree *bt)
|
||||
{
|
||||
byte *ptr, *map;
|
||||
unsigned long mapsz, nnum;
|
||||
int i;
|
||||
|
||||
ASSERT(bt->hdrnd.bt == bt &&
|
||||
bt->hdrnd.nnum == 0 &&
|
||||
bt->hdrnd.nd.ndType == ndHdrNode &&
|
||||
bt->hdrnd.nd.ndNRecs == 3);
|
||||
|
||||
ptr = HFS_NODEREC(bt->hdrnd, 0);
|
||||
|
||||
d_storeuw(&ptr, bt->hdr.bthDepth);
|
||||
d_storeul(&ptr, bt->hdr.bthRoot);
|
||||
d_storeul(&ptr, bt->hdr.bthNRecs);
|
||||
d_storeul(&ptr, bt->hdr.bthFNode);
|
||||
d_storeul(&ptr, bt->hdr.bthLNode);
|
||||
d_storeuw(&ptr, bt->hdr.bthNodeSize);
|
||||
d_storeuw(&ptr, bt->hdr.bthKeyLen);
|
||||
d_storeul(&ptr, bt->hdr.bthNNodes);
|
||||
d_storeul(&ptr, bt->hdr.bthFree);
|
||||
|
||||
for (i = 0; i < 76; ++i)
|
||||
d_storesb(&ptr, bt->hdr.bthResv[i]);
|
||||
|
||||
memcpy(HFS_NODEREC(bt->hdrnd, 2), bt->map, HFS_MAP1SZ);
|
||||
|
||||
if (bt_putnode(&bt->hdrnd) == -1)
|
||||
goto fail;
|
||||
|
||||
map = bt->map + HFS_MAP1SZ;
|
||||
mapsz = bt->mapsz - HFS_MAP1SZ;
|
||||
|
||||
nnum = bt->hdrnd.nd.ndFLink;
|
||||
|
||||
while (mapsz)
|
||||
{
|
||||
node n;
|
||||
|
||||
if (nnum == 0)
|
||||
ERROR(EIO, "truncated b*-tree map");
|
||||
|
||||
if (bt_getnode(&n, bt, nnum) == -1)
|
||||
goto fail;
|
||||
|
||||
if (n.nd.ndType != ndMapNode ||
|
||||
n.nd.ndNRecs != 1 ||
|
||||
n.roff[0] != 0x00e ||
|
||||
n.roff[1] != 0x1fa)
|
||||
ERROR(EIO, "malformed b*-tree map node");
|
||||
|
||||
memcpy(HFS_NODEREC(n, 0), map, HFS_MAPXSZ);
|
||||
|
||||
if (bt_putnode(&n) == -1)
|
||||
goto fail;
|
||||
|
||||
map += HFS_MAPXSZ;
|
||||
mapsz -= HFS_MAPXSZ;
|
||||
|
||||
nnum = n.nd.ndFLink;
|
||||
}
|
||||
|
||||
bt->flags &= ~HFS_BT_UPDATE_HDR;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* High-Level B*-Tree Routines ============================================= */
|
||||
|
||||
/*
|
||||
* NAME: btree->space()
|
||||
* DESCRIPTION: assert space for new records, or extend the file
|
||||
*/
|
||||
int bt_space(btree *bt, unsigned int nrecs)
|
||||
{
|
||||
unsigned int nnodes;
|
||||
long space;
|
||||
|
||||
nnodes = nrecs * (bt->hdr.bthDepth + 1);
|
||||
|
||||
if (nnodes <= bt->hdr.bthFree)
|
||||
goto done;
|
||||
|
||||
/* make sure the extents tree has room too */
|
||||
|
||||
if (bt != &bt->f.vol->ext)
|
||||
{
|
||||
if (bt_space(&bt->f.vol->ext, 1) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
space = f_alloc(&bt->f);
|
||||
if (space == -1)
|
||||
goto fail;
|
||||
|
||||
nnodes = space * (bt->f.vol->mdb.drAlBlkSiz / bt->hdr.bthNodeSize);
|
||||
|
||||
bt->hdr.bthNNodes += nnodes;
|
||||
bt->hdr.bthFree += nnodes;
|
||||
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
|
||||
bt->f.vol->flags |= HFS_VOL_UPDATE_ALTMDB;
|
||||
|
||||
while (bt->hdr.bthNNodes > bt->mapsz * 8)
|
||||
{
|
||||
byte *newmap;
|
||||
node mapnd;
|
||||
|
||||
/* extend tree map */
|
||||
|
||||
newmap = REALLOC(bt->map, byte, bt->mapsz + HFS_MAPXSZ);
|
||||
if (newmap == 0)
|
||||
ERROR(ENOMEM, 0);
|
||||
|
||||
memset(newmap + bt->mapsz, 0, HFS_MAPXSZ);
|
||||
|
||||
bt->map = newmap;
|
||||
bt->mapsz += HFS_MAPXSZ;
|
||||
|
||||
n_init(&mapnd, bt, ndMapNode, 0);
|
||||
if (n_new(&mapnd) == -1)
|
||||
goto fail;
|
||||
|
||||
mapnd.nd.ndNRecs = 1;
|
||||
mapnd.roff[1] = 0x1fa;
|
||||
|
||||
/* link the new map node */
|
||||
|
||||
if (bt->hdrnd.nd.ndFLink == 0)
|
||||
{
|
||||
bt->hdrnd.nd.ndFLink = mapnd.nnum;
|
||||
mapnd.nd.ndBLink = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
node n;
|
||||
unsigned long nnum;
|
||||
|
||||
nnum = bt->hdrnd.nd.ndFLink;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (bt_getnode(&n, bt, nnum) == -1)
|
||||
goto fail;
|
||||
|
||||
if (n.nd.ndFLink == 0)
|
||||
break;
|
||||
|
||||
nnum = n.nd.ndFLink;
|
||||
}
|
||||
|
||||
n.nd.ndFLink = mapnd.nnum;
|
||||
mapnd.nd.ndBLink = n.nnum;
|
||||
|
||||
if (bt_putnode(&n) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (bt_putnode(&mapnd) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
done:
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: insertx()
|
||||
* DESCRIPTION: recursively locate a node and insert a record
|
||||
*/
|
||||
static
|
||||
int insertx(node *np, byte *record, int *reclen)
|
||||
{
|
||||
node child;
|
||||
byte *rec;
|
||||
int result = 0;
|
||||
|
||||
if (n_search(np, record))
|
||||
ERROR(EIO, "b*-tree record already exists");
|
||||
|
||||
switch (np->nd.ndType)
|
||||
{
|
||||
case ndIndxNode:
|
||||
if (np->rnum == -1)
|
||||
rec = HFS_NODEREC(*np, 0);
|
||||
else
|
||||
rec = HFS_NODEREC(*np, np->rnum);
|
||||
|
||||
if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 ||
|
||||
insertx(&child, record, reclen) == -1)
|
||||
goto fail;
|
||||
|
||||
if (np->rnum == -1)
|
||||
{
|
||||
n_index(&child, rec, 0);
|
||||
if (*reclen == 0)
|
||||
{
|
||||
result = bt_putnode(np);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (*reclen)
|
||||
result = n_insert(np, record, reclen);
|
||||
|
||||
break;
|
||||
|
||||
case ndLeafNode:
|
||||
result = n_insert(np, record, reclen);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR(EIO, "unexpected b*-tree node");
|
||||
}
|
||||
|
||||
done:
|
||||
return result;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: btree->insert()
|
||||
* DESCRIPTION: insert a new node record into a tree
|
||||
*/
|
||||
int bt_insert(btree *bt, const byte *record, unsigned int reclen)
|
||||
{
|
||||
node root;
|
||||
byte newrec[HFS_MAX_RECLEN];
|
||||
|
||||
if (bt->hdr.bthRoot == 0)
|
||||
{
|
||||
/* create root node */
|
||||
|
||||
n_init(&root, bt, ndLeafNode, 1);
|
||||
if (n_new(&root) == -1 ||
|
||||
bt_putnode(&root) == -1)
|
||||
goto fail;
|
||||
|
||||
bt->hdr.bthDepth = 1;
|
||||
bt->hdr.bthRoot = root.nnum;
|
||||
bt->hdr.bthFNode = root.nnum;
|
||||
bt->hdr.bthLNode = root.nnum;
|
||||
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
}
|
||||
else if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1)
|
||||
goto fail;
|
||||
|
||||
memcpy(newrec, record, reclen);
|
||||
|
||||
if (insertx(&root, newrec, &reclen) == -1)
|
||||
goto fail;
|
||||
|
||||
if (reclen)
|
||||
{
|
||||
byte oroot[HFS_MAX_RECLEN];
|
||||
unsigned int orootlen;
|
||||
|
||||
/* root node was split; create a new root */
|
||||
|
||||
n_index(&root, oroot, &orootlen);
|
||||
|
||||
n_init(&root, bt, ndIndxNode, root.nd.ndNHeight + 1);
|
||||
if (n_new(&root) == -1)
|
||||
goto fail;
|
||||
|
||||
++bt->hdr.bthDepth;
|
||||
bt->hdr.bthRoot = root.nnum;
|
||||
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
|
||||
/* insert index records for new root */
|
||||
|
||||
n_search(&root, oroot);
|
||||
n_insertx(&root, oroot, orootlen);
|
||||
|
||||
n_search(&root, newrec);
|
||||
n_insertx(&root, newrec, reclen);
|
||||
|
||||
if (bt_putnode(&root) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
++bt->hdr.bthNRecs;
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: deletex()
|
||||
* DESCRIPTION: recursively locate a node and delete a record
|
||||
*/
|
||||
static
|
||||
int deletex(node *np, const byte *key, byte *record, int *flag)
|
||||
{
|
||||
node child;
|
||||
byte *rec;
|
||||
int found, result = 0;
|
||||
|
||||
found = n_search(np, key);
|
||||
|
||||
switch (np->nd.ndType)
|
||||
{
|
||||
case ndIndxNode:
|
||||
if (np->rnum == -1)
|
||||
ERROR(EIO, "b*-tree record not found");
|
||||
|
||||
rec = HFS_NODEREC(*np, np->rnum);
|
||||
|
||||
if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 ||
|
||||
deletex(&child, key, rec, flag) == -1)
|
||||
goto fail;
|
||||
|
||||
if (*flag)
|
||||
{
|
||||
*flag = 0;
|
||||
|
||||
if (HFS_RECKEYLEN(rec) == 0)
|
||||
{
|
||||
result = n_delete(np, record, flag);
|
||||
break;
|
||||
}
|
||||
|
||||
if (np->rnum == 0)
|
||||
{
|
||||
/* propagate index record change into parent */
|
||||
|
||||
n_index(np, record, 0);
|
||||
*flag = 1;
|
||||
}
|
||||
|
||||
result = bt_putnode(np);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ndLeafNode:
|
||||
if (found == 0)
|
||||
ERROR(EIO, "b*-tree record not found");
|
||||
|
||||
result = n_delete(np, record, flag);
|
||||
break;
|
||||
|
||||
default:
|
||||
ERROR(EIO, "unexpected b*-tree node");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: btree->delete()
|
||||
* DESCRIPTION: remove a node record from a tree
|
||||
*/
|
||||
int bt_delete(btree *bt, const byte *key)
|
||||
{
|
||||
node root;
|
||||
byte record[HFS_MAX_RECLEN];
|
||||
int flag = 0;
|
||||
|
||||
if (bt->hdr.bthRoot == 0)
|
||||
ERROR(EIO, "empty b*-tree");
|
||||
|
||||
if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1 ||
|
||||
deletex(&root, key, record, &flag) == -1)
|
||||
goto fail;
|
||||
|
||||
if (bt->hdr.bthDepth > 1 && root.nd.ndNRecs == 1)
|
||||
{
|
||||
const byte *rec;
|
||||
|
||||
/* root only has one record; eliminate it and decrease the tree depth */
|
||||
|
||||
rec = HFS_NODEREC(root, 0);
|
||||
|
||||
--bt->hdr.bthDepth;
|
||||
bt->hdr.bthRoot = d_getul(HFS_RECDATA(rec));
|
||||
|
||||
if (n_free(&root) == -1)
|
||||
goto fail;
|
||||
}
|
||||
else if (bt->hdr.bthDepth == 1 && root.nd.ndNRecs == 0)
|
||||
{
|
||||
/* root node was deleted */
|
||||
|
||||
bt->hdr.bthDepth = 0;
|
||||
bt->hdr.bthRoot = 0;
|
||||
}
|
||||
|
||||
--bt->hdr.bthNRecs;
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: btree->search()
|
||||
* DESCRIPTION: locate a data record given a search key
|
||||
*/
|
||||
int bt_search(btree *bt, const byte *key, node *np)
|
||||
{
|
||||
int found = 0;
|
||||
unsigned long nnum;
|
||||
|
||||
nnum = bt->hdr.bthRoot;
|
||||
|
||||
if (nnum == 0)
|
||||
ERROR(ENOENT, 0);
|
||||
|
||||
while (1)
|
||||
{
|
||||
const byte *rec;
|
||||
|
||||
if (bt_getnode(np, bt, nnum) == -1)
|
||||
{
|
||||
found = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
found = n_search(np, key);
|
||||
|
||||
switch (np->nd.ndType)
|
||||
{
|
||||
case ndIndxNode:
|
||||
if (np->rnum == -1)
|
||||
ERROR(ENOENT, 0);
|
||||
|
||||
rec = HFS_NODEREC(*np, np->rnum);
|
||||
nnum = d_getul(HFS_RECDATA(rec));
|
||||
|
||||
break;
|
||||
|
||||
case ndLeafNode:
|
||||
if (! found)
|
||||
ERROR(ENOENT, 0);
|
||||
|
||||
goto done;
|
||||
|
||||
default:
|
||||
found = -1;
|
||||
ERROR(EIO, "unexpected b*-tree node");
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
fail:
|
||||
return found;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: btree.h,v 1.8 1998/11/02 22:08:55 rob Exp $
|
||||
*/
|
||||
|
||||
int bt_getnode(node *, btree *, unsigned long);
|
||||
int bt_putnode(node *);
|
||||
|
||||
int bt_readhdr(btree *);
|
||||
int bt_writehdr(btree *);
|
||||
|
||||
int bt_space(btree *, unsigned int);
|
||||
|
||||
int bt_insert(btree *, const byte *, unsigned int);
|
||||
int bt_delete(btree *, const byte *);
|
||||
|
||||
int bt_search(btree *, const byte *, node *);
|
|
@ -0,0 +1,58 @@
|
|||
/* config.h. Generated automatically by configure. */
|
||||
/* config.h.in. Generated automatically from configure.in by autoheader. */
|
||||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: acconfig.h,v 1.5 1998/04/11 08:27:11 rob Exp $
|
||||
*/
|
||||
|
||||
/*****************************************************************************
|
||||
* Definitions selected automatically by `configure' *
|
||||
*****************************************************************************/
|
||||
|
||||
/* Define to empty if the keyword does not work. */
|
||||
/* #undef const */
|
||||
|
||||
/* Define to `unsigned' if <sys/types.h> doesn't define. */
|
||||
/* #undef size_t */
|
||||
|
||||
/* Define if you have the ANSI C header files. */
|
||||
#define STDC_HEADERS 1
|
||||
|
||||
/* Define if your <sys/time.h> declares struct tm. */
|
||||
/* #undef TM_IN_SYS_TIME */
|
||||
|
||||
/* Define if you want to enable diagnostic debugging support. */
|
||||
/* #undef DEBUG */
|
||||
|
||||
/* Define if you have the mktime function. */
|
||||
#define HAVE_MKTIME 1
|
||||
|
||||
/* Define if you have the <fcntl.h> header file. */
|
||||
#define HAVE_FCNTL_H 1
|
||||
|
||||
/* Define if you have the <unistd.h> header file. */
|
||||
#define HAVE_UNISTD_H 1
|
||||
|
||||
/*****************************************************************************
|
||||
* End of automatically configured definitions *
|
||||
*****************************************************************************/
|
||||
|
||||
# ifdef DEBUG
|
||||
# include <stdio.h>
|
||||
# endif
|
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: data.c,v 1.7 1998/11/02 22:08:57 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <string.h>
|
||||
# include <time.h>
|
||||
|
||||
# ifdef TM_IN_SYS_TIME
|
||||
# include <sys/time.h>
|
||||
# endif
|
||||
|
||||
# include "data.h"
|
||||
|
||||
# define TIMEDIFF 2082844800UL
|
||||
|
||||
static
|
||||
time_t tzdiff = -1;
|
||||
|
||||
const
|
||||
unsigned char hfs_charorder[256] = {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
|
||||
|
||||
0x20, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
|
||||
0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
|
||||
0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e,
|
||||
0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
||||
|
||||
0x47, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69,
|
||||
0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f,
|
||||
0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1,
|
||||
0xa3, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae,
|
||||
|
||||
0x54, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69,
|
||||
0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f,
|
||||
0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1,
|
||||
0xa3, 0xa5, 0xa8, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
|
||||
|
||||
0x4c, 0x50, 0x5c, 0x62, 0x7d, 0x81, 0x9a, 0x55,
|
||||
0x4a, 0x56, 0x4c, 0x4e, 0x50, 0x5c, 0x62, 0x64,
|
||||
0x65, 0x66, 0x6f, 0x70, 0x71, 0x72, 0x7d, 0x89,
|
||||
0x8a, 0x8b, 0x81, 0x83, 0x9c, 0x9d, 0x9e, 0x9a,
|
||||
|
||||
0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0x95,
|
||||
0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x52, 0x85,
|
||||
0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
|
||||
0xc9, 0xca, 0xcb, 0x57, 0x8c, 0xcc, 0x52, 0x85,
|
||||
|
||||
0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0x26,
|
||||
0x27, 0xd4, 0x20, 0x4a, 0x4e, 0x83, 0x87, 0x87,
|
||||
0xd5, 0xd6, 0x24, 0x25, 0x2d, 0x2e, 0xd7, 0xd8,
|
||||
0xa7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
|
||||
|
||||
0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
|
||||
0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
|
||||
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
|
||||
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
|
||||
};
|
||||
|
||||
/*
|
||||
* NAME: data->getsb()
|
||||
* DESCRIPTION: marshal 1 signed byte into local host format
|
||||
*/
|
||||
signed char d_getsb(register const unsigned char *ptr)
|
||||
{
|
||||
return ptr[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->getub()
|
||||
* DESCRIPTION: marshal 1 unsigned byte into local host format
|
||||
*/
|
||||
unsigned char d_getub(register const unsigned char *ptr)
|
||||
{
|
||||
return ptr[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->getsw()
|
||||
* DESCRIPTION: marshal 2 signed bytes into local host format
|
||||
*/
|
||||
signed short d_getsw(register const unsigned char *ptr)
|
||||
{
|
||||
return
|
||||
((( signed short) ptr[0] << 8) |
|
||||
((unsigned short) ptr[1] << 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->getuw()
|
||||
* DESCRIPTION: marshal 2 unsigned bytes into local host format
|
||||
*/
|
||||
unsigned short d_getuw(register const unsigned char *ptr)
|
||||
{
|
||||
return
|
||||
(((unsigned short) ptr[0] << 8) |
|
||||
((unsigned short) ptr[1] << 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->getsl()
|
||||
* DESCRIPTION: marshal 4 signed bytes into local host format
|
||||
*/
|
||||
signed long d_getsl(register const unsigned char *ptr)
|
||||
{
|
||||
return
|
||||
((( signed long) ptr[0] << 24) |
|
||||
((unsigned long) ptr[1] << 16) |
|
||||
((unsigned long) ptr[2] << 8) |
|
||||
((unsigned long) ptr[3] << 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->getul()
|
||||
* DESCRIPTION: marshal 4 unsigned bytes into local host format
|
||||
*/
|
||||
unsigned long d_getul(register const unsigned char *ptr)
|
||||
{
|
||||
return
|
||||
(((unsigned long) ptr[0] << 24) |
|
||||
((unsigned long) ptr[1] << 16) |
|
||||
((unsigned long) ptr[2] << 8) |
|
||||
((unsigned long) ptr[3] << 0));
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->putsb()
|
||||
* DESCRIPTION: marshal 1 signed byte out in big-endian format
|
||||
*/
|
||||
void d_putsb(register unsigned char *ptr,
|
||||
register signed char data)
|
||||
{
|
||||
*ptr = data;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->putub()
|
||||
* DESCRIPTION: marshal 1 unsigned byte out in big-endian format
|
||||
*/
|
||||
void d_putub(register unsigned char *ptr,
|
||||
register unsigned char data)
|
||||
{
|
||||
*ptr = data;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->putsw()
|
||||
* DESCRIPTION: marshal 2 signed bytes out in big-endian format
|
||||
*/
|
||||
void d_putsw(register unsigned char *ptr,
|
||||
register signed short data)
|
||||
{
|
||||
*ptr++ = ((unsigned short) data & 0xff00) >> 8;
|
||||
*ptr = ((unsigned short) data & 0x00ff) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->putuw()
|
||||
* DESCRIPTION: marshal 2 unsigned bytes out in big-endian format
|
||||
*/
|
||||
void d_putuw(register unsigned char *ptr,
|
||||
register unsigned short data)
|
||||
{
|
||||
*ptr++ = (data & 0xff00) >> 8;
|
||||
*ptr = (data & 0x00ff) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->putsl()
|
||||
* DESCRIPTION: marshal 4 signed bytes out in big-endian format
|
||||
*/
|
||||
void d_putsl(register unsigned char *ptr,
|
||||
register signed long data)
|
||||
{
|
||||
*ptr++ = ((unsigned long) data & 0xff000000UL) >> 24;
|
||||
*ptr++ = ((unsigned long) data & 0x00ff0000UL) >> 16;
|
||||
*ptr++ = ((unsigned long) data & 0x0000ff00UL) >> 8;
|
||||
*ptr = ((unsigned long) data & 0x000000ffUL) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->putul()
|
||||
* DESCRIPTION: marshal 4 unsigned bytes out in big-endian format
|
||||
*/
|
||||
void d_putul(register unsigned char *ptr,
|
||||
register unsigned long data)
|
||||
{
|
||||
*ptr++ = (data & 0xff000000UL) >> 24;
|
||||
*ptr++ = (data & 0x00ff0000UL) >> 16;
|
||||
*ptr++ = (data & 0x0000ff00UL) >> 8;
|
||||
*ptr = (data & 0x000000ffUL) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->fetchsb()
|
||||
* DESCRIPTION: incrementally retrieve a signed byte of data
|
||||
*/
|
||||
void d_fetchsb(register const unsigned char **ptr,
|
||||
register signed char *dest)
|
||||
{
|
||||
*dest = *(*ptr)++;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->fetchub()
|
||||
* DESCRIPTION: incrementally retrieve an unsigned byte of data
|
||||
*/
|
||||
void d_fetchub(register const unsigned char **ptr,
|
||||
register unsigned char *dest)
|
||||
{
|
||||
*dest = *(*ptr)++;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->fetchsw()
|
||||
* DESCRIPTION: incrementally retrieve a signed word of data
|
||||
*/
|
||||
void d_fetchsw(register const unsigned char **ptr,
|
||||
register signed short *dest)
|
||||
{
|
||||
*dest =
|
||||
((( signed short) (*ptr)[0] << 8) |
|
||||
((unsigned short) (*ptr)[1] << 0));
|
||||
*ptr += 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->fetchuw()
|
||||
* DESCRIPTION: incrementally retrieve an unsigned word of data
|
||||
*/
|
||||
void d_fetchuw(register const unsigned char **ptr,
|
||||
register unsigned short *dest)
|
||||
{
|
||||
*dest =
|
||||
(((unsigned short) (*ptr)[0] << 8) |
|
||||
((unsigned short) (*ptr)[1] << 0));
|
||||
*ptr += 2;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->fetchsl()
|
||||
* DESCRIPTION: incrementally retrieve a signed long word of data
|
||||
*/
|
||||
void d_fetchsl(register const unsigned char **ptr,
|
||||
register signed long *dest)
|
||||
{
|
||||
*dest =
|
||||
((( signed long) (*ptr)[0] << 24) |
|
||||
((unsigned long) (*ptr)[1] << 16) |
|
||||
((unsigned long) (*ptr)[2] << 8) |
|
||||
((unsigned long) (*ptr)[3] << 0));
|
||||
*ptr += 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->fetchul()
|
||||
* DESCRIPTION: incrementally retrieve an unsigned long word of data
|
||||
*/
|
||||
void d_fetchul(register const unsigned char **ptr,
|
||||
register unsigned long *dest)
|
||||
{
|
||||
*dest =
|
||||
(((unsigned long) (*ptr)[0] << 24) |
|
||||
((unsigned long) (*ptr)[1] << 16) |
|
||||
((unsigned long) (*ptr)[2] << 8) |
|
||||
((unsigned long) (*ptr)[3] << 0));
|
||||
*ptr += 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->storesb()
|
||||
* DESCRIPTION: incrementally store a signed byte of data
|
||||
*/
|
||||
void d_storesb(register unsigned char **ptr,
|
||||
register signed char data)
|
||||
{
|
||||
*(*ptr)++ = data;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->storeub()
|
||||
* DESCRIPTION: incrementally store an unsigned byte of data
|
||||
*/
|
||||
void d_storeub(register unsigned char **ptr,
|
||||
register unsigned char data)
|
||||
{
|
||||
*(*ptr)++ = data;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->storesw()
|
||||
* DESCRIPTION: incrementally store a signed word of data
|
||||
*/
|
||||
void d_storesw(register unsigned char **ptr,
|
||||
register signed short data)
|
||||
{
|
||||
*(*ptr)++ = ((unsigned short) data & 0xff00) >> 8;
|
||||
*(*ptr)++ = ((unsigned short) data & 0x00ff) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->storeuw()
|
||||
* DESCRIPTION: incrementally store an unsigned word of data
|
||||
*/
|
||||
void d_storeuw(register unsigned char **ptr,
|
||||
register unsigned short data)
|
||||
{
|
||||
*(*ptr)++ = (data & 0xff00) >> 8;
|
||||
*(*ptr)++ = (data & 0x00ff) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->storesl()
|
||||
* DESCRIPTION: incrementally store a signed long word of data
|
||||
*/
|
||||
void d_storesl(register unsigned char **ptr,
|
||||
register signed long data)
|
||||
{
|
||||
*(*ptr)++ = ((unsigned long) data & 0xff000000UL) >> 24;
|
||||
*(*ptr)++ = ((unsigned long) data & 0x00ff0000UL) >> 16;
|
||||
*(*ptr)++ = ((unsigned long) data & 0x0000ff00UL) >> 8;
|
||||
*(*ptr)++ = ((unsigned long) data & 0x000000ffUL) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->storeul()
|
||||
* DESCRIPTION: incrementally store an unsigned long word of data
|
||||
*/
|
||||
void d_storeul(register unsigned char **ptr,
|
||||
register unsigned long data)
|
||||
{
|
||||
*(*ptr)++ = (data & 0xff000000UL) >> 24;
|
||||
*(*ptr)++ = (data & 0x00ff0000UL) >> 16;
|
||||
*(*ptr)++ = (data & 0x0000ff00UL) >> 8;
|
||||
*(*ptr)++ = (data & 0x000000ffUL) >> 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->fetchstr()
|
||||
* DESCRIPTION: incrementally retrieve a string
|
||||
*/
|
||||
void d_fetchstr(const unsigned char **ptr, char *dest, unsigned size)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
len = d_getub(*ptr);
|
||||
|
||||
if (len > 0 && len < size)
|
||||
memcpy(dest, *ptr + 1, len);
|
||||
else
|
||||
len = 0;
|
||||
|
||||
dest[len] = 0;
|
||||
|
||||
*ptr += size;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->storestr()
|
||||
* DESCRIPTION: incrementally store a string
|
||||
*/
|
||||
void d_storestr(unsigned char **ptr, const char *src, unsigned size)
|
||||
{
|
||||
unsigned len;
|
||||
|
||||
len = strlen(src);
|
||||
if (len > --size)
|
||||
len = 0;
|
||||
|
||||
d_storeub(ptr, len);
|
||||
|
||||
memcpy(*ptr, src, len);
|
||||
memset(*ptr + len, 0, size - len);
|
||||
|
||||
*ptr += size;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->relstring()
|
||||
* DESCRIPTION: compare two strings as per MacOS for HFS
|
||||
*/
|
||||
int d_relstring(const char *str1, const char *str2)
|
||||
{
|
||||
register int diff;
|
||||
|
||||
while (*str1 && *str2)
|
||||
{
|
||||
diff = hfs_charorder[(unsigned char) *str1] -
|
||||
hfs_charorder[(unsigned char) *str2];
|
||||
|
||||
if (diff)
|
||||
return diff;
|
||||
|
||||
++str1, ++str2;
|
||||
}
|
||||
|
||||
if (! *str1 && *str2)
|
||||
return -1;
|
||||
else if (*str1 && ! *str2)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: calctzdiff()
|
||||
* DESCRIPTION: calculate the timezone difference between local time and UTC
|
||||
*/
|
||||
static
|
||||
void calctzdiff(void)
|
||||
{
|
||||
# ifdef HAVE_MKTIME
|
||||
|
||||
time_t t;
|
||||
int isdst;
|
||||
struct tm tm;
|
||||
const struct tm *tmp;
|
||||
|
||||
time(&t);
|
||||
isdst = localtime(&t)->tm_isdst;
|
||||
|
||||
tmp = gmtime(&t);
|
||||
if (tmp)
|
||||
{
|
||||
tm = *tmp;
|
||||
tm.tm_isdst = isdst;
|
||||
|
||||
tzdiff = t - mktime(&tm);
|
||||
}
|
||||
else
|
||||
tzdiff = 0;
|
||||
|
||||
# else
|
||||
|
||||
tzdiff = 0;
|
||||
|
||||
# endif
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->ltime()
|
||||
* DESCRIPTION: convert MacOS time to local time
|
||||
*/
|
||||
time_t d_ltime(unsigned long mtime)
|
||||
{
|
||||
if (tzdiff == -1)
|
||||
calctzdiff();
|
||||
|
||||
return (time_t) (mtime - TIMEDIFF) - tzdiff;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: data->mtime()
|
||||
* DESCRIPTION: convert local time to MacOS time
|
||||
*/
|
||||
unsigned long d_mtime(time_t ltime)
|
||||
{
|
||||
if (tzdiff == -1)
|
||||
calctzdiff();
|
||||
|
||||
return (unsigned long) (ltime + tzdiff) + TIMEDIFF;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: data.h,v 1.7 1998/11/02 22:08:58 rob Exp $
|
||||
*/
|
||||
|
||||
extern const unsigned char hfs_charorder[];
|
||||
|
||||
signed char d_getsb(register const unsigned char *);
|
||||
unsigned char d_getub(register const unsigned char *);
|
||||
signed short d_getsw(register const unsigned char *);
|
||||
unsigned short d_getuw(register const unsigned char *);
|
||||
signed long d_getsl(register const unsigned char *);
|
||||
unsigned long d_getul(register const unsigned char *);
|
||||
|
||||
void d_putsb(register unsigned char *, register signed char);
|
||||
void d_putub(register unsigned char *, register unsigned char);
|
||||
void d_putsw(register unsigned char *, register signed short);
|
||||
void d_putuw(register unsigned char *, register unsigned short);
|
||||
void d_putsl(register unsigned char *, register signed long);
|
||||
void d_putul(register unsigned char *, register unsigned long);
|
||||
|
||||
void d_fetchsb(register const unsigned char **, register signed char *);
|
||||
void d_fetchub(register const unsigned char **, register unsigned char *);
|
||||
void d_fetchsw(register const unsigned char **, register signed short *);
|
||||
void d_fetchuw(register const unsigned char **, register unsigned short *);
|
||||
void d_fetchsl(register const unsigned char **, register signed long *);
|
||||
void d_fetchul(register const unsigned char **, register unsigned long *);
|
||||
|
||||
void d_storesb(register unsigned char **, register signed char);
|
||||
void d_storeub(register unsigned char **, register unsigned char);
|
||||
void d_storesw(register unsigned char **, register signed short);
|
||||
void d_storeuw(register unsigned char **, register unsigned short);
|
||||
void d_storesl(register unsigned char **, register signed long);
|
||||
void d_storeul(register unsigned char **, register unsigned long);
|
||||
|
||||
void d_fetchstr(const unsigned char **, char *, unsigned);
|
||||
void d_storestr(unsigned char **, const char *, unsigned);
|
||||
|
||||
int d_relstring(const char *, const char *);
|
||||
|
||||
time_t d_ltime(unsigned long);
|
||||
unsigned long d_mtime(time_t);
|
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: file.c,v 1.9 1998/11/02 22:08:59 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <string.h>
|
||||
# include <errno.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "file.h"
|
||||
# include "btree.h"
|
||||
# include "record.h"
|
||||
# include "volume.h"
|
||||
|
||||
/*
|
||||
* NAME: file->init()
|
||||
* DESCRIPTION: initialize file structure
|
||||
*/
|
||||
void f_init(hfsfile *file, hfsvol *vol, long cnid, const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
file->vol = vol;
|
||||
file->parid = 0;
|
||||
|
||||
strcpy(file->name, name);
|
||||
|
||||
file->cat.cdrType = cdrFilRec;
|
||||
file->cat.cdrResrv2 = 0;
|
||||
|
||||
file->cat.u.fil.filFlags = 0;
|
||||
file->cat.u.fil.filTyp = 0;
|
||||
|
||||
file->cat.u.fil.filUsrWds.fdType = 0;
|
||||
file->cat.u.fil.filUsrWds.fdCreator = 0;
|
||||
file->cat.u.fil.filUsrWds.fdFlags = 0;
|
||||
file->cat.u.fil.filUsrWds.fdLocation.v = 0;
|
||||
file->cat.u.fil.filUsrWds.fdLocation.h = 0;
|
||||
file->cat.u.fil.filUsrWds.fdFldr = 0;
|
||||
|
||||
file->cat.u.fil.filFlNum = cnid;
|
||||
file->cat.u.fil.filStBlk = 0;
|
||||
file->cat.u.fil.filLgLen = 0;
|
||||
file->cat.u.fil.filPyLen = 0;
|
||||
file->cat.u.fil.filRStBlk = 0;
|
||||
file->cat.u.fil.filRLgLen = 0;
|
||||
file->cat.u.fil.filRPyLen = 0;
|
||||
file->cat.u.fil.filCrDat = 0;
|
||||
file->cat.u.fil.filMdDat = 0;
|
||||
file->cat.u.fil.filBkDat = 0;
|
||||
|
||||
file->cat.u.fil.filFndrInfo.fdIconID = 0;
|
||||
for (i = 0; i < 4; ++i)
|
||||
file->cat.u.fil.filFndrInfo.fdUnused[i] = 0;
|
||||
file->cat.u.fil.filFndrInfo.fdComment = 0;
|
||||
file->cat.u.fil.filFndrInfo.fdPutAway = 0;
|
||||
|
||||
file->cat.u.fil.filClpSize = 0;
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
file->cat.u.fil.filExtRec[i].xdrStABN = 0;
|
||||
file->cat.u.fil.filExtRec[i].xdrNumABlks = 0;
|
||||
|
||||
file->cat.u.fil.filRExtRec[i].xdrStABN = 0;
|
||||
file->cat.u.fil.filRExtRec[i].xdrNumABlks = 0;
|
||||
}
|
||||
|
||||
file->cat.u.fil.filResrv = 0;
|
||||
|
||||
f_selectfork(file, fkData);
|
||||
|
||||
file->flags = 0;
|
||||
|
||||
file->prev = 0;
|
||||
file->next = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: file->selectfork()
|
||||
* DESCRIPTION: choose a fork for file operations
|
||||
*/
|
||||
void f_selectfork(hfsfile *file, int fork)
|
||||
{
|
||||
file->fork = fork;
|
||||
|
||||
memcpy(&file->ext, fork == fkData ?
|
||||
&file->cat.u.fil.filExtRec : &file->cat.u.fil.filRExtRec,
|
||||
sizeof(ExtDataRec));
|
||||
|
||||
file->fabn = 0;
|
||||
file->pos = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: file->getptrs()
|
||||
* DESCRIPTION: make pointers to the current fork's lengths and extents
|
||||
*/
|
||||
void f_getptrs(hfsfile *file, ExtDataRec **extrec,
|
||||
unsigned long **lglen, unsigned long **pylen)
|
||||
{
|
||||
if (file->fork == fkData)
|
||||
{
|
||||
if (extrec)
|
||||
*extrec = &file->cat.u.fil.filExtRec;
|
||||
if (lglen)
|
||||
*lglen = &file->cat.u.fil.filLgLen;
|
||||
if (pylen)
|
||||
*pylen = &file->cat.u.fil.filPyLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extrec)
|
||||
*extrec = &file->cat.u.fil.filRExtRec;
|
||||
if (lglen)
|
||||
*lglen = &file->cat.u.fil.filRLgLen;
|
||||
if (pylen)
|
||||
*pylen = &file->cat.u.fil.filRPyLen;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: file->doblock()
|
||||
* DESCRIPTION: read or write a numbered block from a file
|
||||
*/
|
||||
int f_doblock(hfsfile *file, unsigned long num, block *bp,
|
||||
int (*func)(hfsvol *, unsigned int, unsigned int, block *))
|
||||
{
|
||||
unsigned int abnum;
|
||||
unsigned int blnum;
|
||||
unsigned int fabn;
|
||||
int i;
|
||||
|
||||
abnum = num / file->vol->lpa;
|
||||
blnum = num % file->vol->lpa;
|
||||
|
||||
/* locate the appropriate extent record */
|
||||
|
||||
fabn = file->fabn;
|
||||
|
||||
if (abnum < fabn)
|
||||
{
|
||||
ExtDataRec *extrec;
|
||||
|
||||
f_getptrs(file, &extrec, 0, 0);
|
||||
|
||||
fabn = file->fabn = 0;
|
||||
memcpy(&file->ext, extrec, sizeof(ExtDataRec));
|
||||
}
|
||||
else
|
||||
abnum -= fabn;
|
||||
|
||||
while (1)
|
||||
{
|
||||
unsigned int n;
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
n = file->ext[i].xdrNumABlks;
|
||||
|
||||
if (abnum < n)
|
||||
return func(file->vol, file->ext[i].xdrStABN + abnum, blnum, bp);
|
||||
|
||||
fabn += n;
|
||||
abnum -= n;
|
||||
}
|
||||
|
||||
if (v_extsearch(file, fabn, &file->ext, 0) <= 0)
|
||||
goto fail;
|
||||
|
||||
file->fabn = fabn;
|
||||
}
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: file->addextent()
|
||||
* DESCRIPTION: add an extent to a file
|
||||
*/
|
||||
int f_addextent(hfsfile *file, ExtDescriptor *blocks)
|
||||
{
|
||||
hfsvol *vol = file->vol;
|
||||
ExtDataRec *extrec;
|
||||
unsigned long *pylen;
|
||||
unsigned int start, end;
|
||||
node n;
|
||||
int i;
|
||||
|
||||
f_getptrs(file, &extrec, 0, &pylen);
|
||||
|
||||
start = file->fabn;
|
||||
end = *pylen / vol->mdb.drAlBlkSiz;
|
||||
|
||||
n.nnum = 0;
|
||||
i = -1;
|
||||
|
||||
while (start < end)
|
||||
{
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
unsigned int num;
|
||||
|
||||
num = file->ext[i].xdrNumABlks;
|
||||
start += num;
|
||||
|
||||
if (start == end)
|
||||
break;
|
||||
else if (start > end)
|
||||
ERROR(EIO, "file extents exceed file physical length");
|
||||
else if (num == 0)
|
||||
ERROR(EIO, "empty file extent");
|
||||
}
|
||||
|
||||
if (start == end)
|
||||
break;
|
||||
|
||||
if (v_extsearch(file, start, &file->ext, &n) <= 0)
|
||||
goto fail;
|
||||
|
||||
file->fabn = start;
|
||||
}
|
||||
|
||||
if (i >= 0 &&
|
||||
file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks->xdrStABN)
|
||||
file->ext[i].xdrNumABlks += blocks->xdrNumABlks;
|
||||
else
|
||||
{
|
||||
/* create a new extent descriptor */
|
||||
|
||||
if (++i < 3)
|
||||
file->ext[i] = *blocks;
|
||||
else
|
||||
{
|
||||
ExtKeyRec key;
|
||||
byte record[HFS_MAX_EXTRECLEN];
|
||||
unsigned int reclen;
|
||||
|
||||
/* record is full; create a new one */
|
||||
|
||||
file->ext[0] = *blocks;
|
||||
|
||||
for (i = 1; i < 3; ++i)
|
||||
{
|
||||
file->ext[i].xdrStABN = 0;
|
||||
file->ext[i].xdrNumABlks = 0;
|
||||
}
|
||||
|
||||
file->fabn = start;
|
||||
|
||||
r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end);
|
||||
r_packextrec(&key, &file->ext, record, &reclen);
|
||||
|
||||
if (bt_insert(&vol->ext, record, reclen) == -1)
|
||||
goto fail;
|
||||
|
||||
i = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (i >= 0)
|
||||
{
|
||||
/* store the modified extent record */
|
||||
|
||||
if (file->fabn)
|
||||
{
|
||||
if ((n.nnum == 0 &&
|
||||
v_extsearch(file, file->fabn, 0, &n) <= 0) ||
|
||||
v_putextrec(&file->ext, &n) == -1)
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
memcpy(extrec, &file->ext, sizeof(ExtDataRec));
|
||||
}
|
||||
|
||||
*pylen += blocks->xdrNumABlks * vol->mdb.drAlBlkSiz;
|
||||
|
||||
file->flags |= HFS_FILE_UPDATE_CATREC;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: file->alloc()
|
||||
* DESCRIPTION: reserve allocation blocks for a file
|
||||
*/
|
||||
long f_alloc(hfsfile *file)
|
||||
{
|
||||
hfsvol *vol = file->vol;
|
||||
unsigned long clumpsz;
|
||||
ExtDescriptor blocks;
|
||||
|
||||
clumpsz = file->cat.u.fil.filClpSize;
|
||||
if (clumpsz == 0)
|
||||
{
|
||||
if (file == &vol->ext.f)
|
||||
clumpsz = vol->mdb.drXTClpSiz;
|
||||
else if (file == &vol->cat.f)
|
||||
clumpsz = vol->mdb.drCTClpSiz;
|
||||
else
|
||||
clumpsz = vol->mdb.drClpSiz;
|
||||
}
|
||||
|
||||
blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz;
|
||||
|
||||
if (v_allocblocks(vol, &blocks) == -1)
|
||||
goto fail;
|
||||
|
||||
if (f_addextent(file, &blocks) == -1)
|
||||
{
|
||||
v_freeblocks(vol, &blocks);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return blocks.xdrNumABlks;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: file->trunc()
|
||||
* DESCRIPTION: release allocation blocks unneeded by a file
|
||||
*/
|
||||
int f_trunc(hfsfile *file)
|
||||
{
|
||||
hfsvol *vol = file->vol;
|
||||
ExtDataRec *extrec;
|
||||
unsigned long *lglen, *pylen, alblksz, newpylen;
|
||||
unsigned int dlen, start, end;
|
||||
node n;
|
||||
int i;
|
||||
|
||||
if (vol->flags & HFS_VOL_READONLY)
|
||||
goto done;
|
||||
|
||||
f_getptrs(file, &extrec, &lglen, &pylen);
|
||||
|
||||
alblksz = vol->mdb.drAlBlkSiz;
|
||||
newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz;
|
||||
|
||||
if (newpylen > *pylen)
|
||||
ERROR(EIO, "file size exceeds physical length");
|
||||
else if (newpylen == *pylen)
|
||||
goto done;
|
||||
|
||||
dlen = (*pylen - newpylen) / alblksz;
|
||||
|
||||
start = file->fabn;
|
||||
end = newpylen / alblksz;
|
||||
|
||||
if (start >= end)
|
||||
{
|
||||
start = file->fabn = 0;
|
||||
memcpy(&file->ext, extrec, sizeof(ExtDataRec));
|
||||
}
|
||||
|
||||
n.nnum = 0;
|
||||
i = -1;
|
||||
|
||||
while (start < end)
|
||||
{
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
unsigned int num;
|
||||
|
||||
num = file->ext[i].xdrNumABlks;
|
||||
start += num;
|
||||
|
||||
if (start >= end)
|
||||
break;
|
||||
else if (num == 0)
|
||||
ERROR(EIO, "empty file extent");
|
||||
}
|
||||
|
||||
if (start >= end)
|
||||
break;
|
||||
|
||||
if (v_extsearch(file, start, &file->ext, &n) <= 0)
|
||||
goto fail;
|
||||
|
||||
file->fabn = start;
|
||||
}
|
||||
|
||||
if (start > end)
|
||||
{
|
||||
ExtDescriptor blocks;
|
||||
|
||||
file->ext[i].xdrNumABlks -= start - end;
|
||||
dlen -= start - end;
|
||||
|
||||
blocks.xdrStABN = file->ext[i].xdrStABN + file->ext[i].xdrNumABlks;
|
||||
blocks.xdrNumABlks = start - end;
|
||||
|
||||
if (v_freeblocks(vol, &blocks) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
*pylen = newpylen;
|
||||
|
||||
file->flags |= HFS_FILE_UPDATE_CATREC;
|
||||
|
||||
do
|
||||
{
|
||||
while (dlen && ++i < 3)
|
||||
{
|
||||
unsigned int num;
|
||||
|
||||
num = file->ext[i].xdrNumABlks;
|
||||
start += num;
|
||||
|
||||
if (num == 0)
|
||||
ERROR(EIO, "empty file extent");
|
||||
else if (num > dlen)
|
||||
ERROR(EIO, "file extents exceed physical size");
|
||||
|
||||
dlen -= num;
|
||||
|
||||
if (v_freeblocks(vol, &file->ext[i]) == -1)
|
||||
goto fail;
|
||||
|
||||
file->ext[i].xdrStABN = 0;
|
||||
file->ext[i].xdrNumABlks = 0;
|
||||
}
|
||||
|
||||
if (file->fabn)
|
||||
{
|
||||
if (n.nnum == 0 &&
|
||||
v_extsearch(file, file->fabn, 0, &n) <= 0)
|
||||
goto fail;
|
||||
|
||||
if (file->ext[0].xdrNumABlks)
|
||||
{
|
||||
if (v_putextrec(&file->ext, &n) == -1)
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bt_delete(&vol->ext, HFS_NODEREC(n, n.rnum)) == -1)
|
||||
goto fail;
|
||||
|
||||
n.nnum = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
memcpy(extrec, &file->ext, sizeof(ExtDataRec));
|
||||
|
||||
if (dlen)
|
||||
{
|
||||
if (v_extsearch(file, start, &file->ext, &n) <= 0)
|
||||
goto fail;
|
||||
|
||||
file->fabn = start;
|
||||
i = -1;
|
||||
}
|
||||
}
|
||||
while (dlen);
|
||||
|
||||
done:
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: file->flush()
|
||||
* DESCRIPTION: flush all pending changes to an open file
|
||||
*/
|
||||
int f_flush(hfsfile *file)
|
||||
{
|
||||
hfsvol *vol = file->vol;
|
||||
|
||||
if (vol->flags & HFS_VOL_READONLY)
|
||||
goto done;
|
||||
|
||||
if (file->flags & HFS_FILE_UPDATE_CATREC)
|
||||
{
|
||||
node n;
|
||||
|
||||
file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN;
|
||||
file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN;
|
||||
|
||||
if (v_catsearch(vol, file->parid, file->name, 0, 0, &n) <= 0 ||
|
||||
v_putcatrec(&file->cat, &n) == -1)
|
||||
goto fail;
|
||||
|
||||
file->flags &= ~HFS_FILE_UPDATE_CATREC;
|
||||
}
|
||||
|
||||
done:
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: file.h,v 1.6 1998/04/11 08:27:12 rob Exp $
|
||||
*/
|
||||
|
||||
enum {
|
||||
fkData = 0x00,
|
||||
fkRsrc = 0xff
|
||||
};
|
||||
|
||||
void f_init(hfsfile *, hfsvol *, long, const char *);
|
||||
void f_selectfork(hfsfile *, int);
|
||||
void f_getptrs(hfsfile *, ExtDataRec **, unsigned long **, unsigned long **);
|
||||
|
||||
int f_doblock(hfsfile *, unsigned long, block *,
|
||||
int (*)(hfsvol *, unsigned int, unsigned int, block *));
|
||||
|
||||
# define f_getblock(file, num, bp) \
|
||||
f_doblock((file), (num), (bp), b_readab)
|
||||
# define f_putblock(file, num, bp) \
|
||||
f_doblock((file), (num), (bp), \
|
||||
(int (*)(hfsvol *, unsigned int, unsigned int, block *)) \
|
||||
b_writeab)
|
||||
|
||||
int f_addextent(hfsfile *, ExtDescriptor *);
|
||||
long f_alloc(hfsfile *);
|
||||
|
||||
int f_trunc(hfsfile *);
|
||||
int f_flush(hfsfile *);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: hfs.h,v 1.11 1998/11/02 22:09:01 rob Exp $
|
||||
*/
|
||||
|
||||
# include <time.h>
|
||||
|
||||
# define HFS_BLOCKSZ 512
|
||||
# define HFS_BLOCKSZ_BITS 9
|
||||
|
||||
# define HFS_MAX_FLEN 31
|
||||
# define HFS_MAX_VLEN 27
|
||||
|
||||
typedef struct _hfsvol_ hfsvol;
|
||||
typedef struct _hfsfile_ hfsfile;
|
||||
typedef struct _hfsdir_ hfsdir;
|
||||
|
||||
typedef struct {
|
||||
char name[HFS_MAX_VLEN + 1]; /* name of volume (MacOS Standard Roman) */
|
||||
int flags; /* volume flags */
|
||||
|
||||
unsigned long totbytes; /* total bytes on volume */
|
||||
unsigned long freebytes; /* free bytes on volume */
|
||||
|
||||
unsigned long alblocksz; /* volume allocation block size */
|
||||
unsigned long clumpsz; /* default file clump size */
|
||||
|
||||
unsigned long numfiles; /* number of files in volume */
|
||||
unsigned long numdirs; /* number of directories in volume */
|
||||
|
||||
time_t crdate; /* volume creation date */
|
||||
time_t mddate; /* last volume modification date */
|
||||
time_t bkdate; /* last volume backup date */
|
||||
|
||||
unsigned long blessed; /* CNID of MacOS System Folder */
|
||||
} hfsvolent;
|
||||
|
||||
typedef struct {
|
||||
char name[HFS_MAX_FLEN + 1]; /* catalog name (MacOS Standard Roman) */
|
||||
int flags; /* bit flags */
|
||||
unsigned long cnid; /* catalog node id (CNID) */
|
||||
unsigned long parid; /* CNID of parent directory */
|
||||
|
||||
time_t crdate; /* date of creation */
|
||||
time_t mddate; /* date of last modification */
|
||||
time_t bkdate; /* date of last backup */
|
||||
|
||||
short fdflags; /* Macintosh Finder flags */
|
||||
short fdcomment; /* Macintosh Comment ID */
|
||||
|
||||
struct {
|
||||
signed short v; /* Finder icon vertical coordinate */
|
||||
signed short h; /* horizontal coordinate */
|
||||
} fdlocation;
|
||||
|
||||
union {
|
||||
struct {
|
||||
unsigned long dsize; /* size of data fork */
|
||||
unsigned long rsize; /* size of resource fork */
|
||||
|
||||
char type[5]; /* file type code (plus null) */
|
||||
char creator[5]; /* file creator code (plus null) */
|
||||
} file;
|
||||
|
||||
struct {
|
||||
unsigned short valence; /* number of items in directory */
|
||||
|
||||
struct {
|
||||
signed short top; /* top edge of folder's rectangle */
|
||||
signed short left; /* left edge */
|
||||
signed short bottom; /* bottom edge */
|
||||
signed short right; /* right edge */
|
||||
} rect;
|
||||
} dir;
|
||||
} u;
|
||||
} hfsdirent;
|
||||
|
||||
# define HFS_ISDIR 0x0001
|
||||
# define HFS_ISLOCKED 0x0002
|
||||
|
||||
# define HFS_CNID_ROOTPAR 1
|
||||
# define HFS_CNID_ROOTDIR 2
|
||||
# define HFS_CNID_EXT 3
|
||||
# define HFS_CNID_CAT 4
|
||||
# define HFS_CNID_BADALLOC 5
|
||||
|
||||
# define HFS_FNDR_ISONDESK (1 << 0)
|
||||
# define HFS_FNDR_COLOR 0x0e
|
||||
# define HFS_FNDR_COLORRESERVED (1 << 4)
|
||||
# define HFS_FNDR_REQUIRESSWITCHLAUNCH (1 << 5)
|
||||
# define HFS_FNDR_ISSHARED (1 << 6)
|
||||
# define HFS_FNDR_HASNOINITS (1 << 7)
|
||||
# define HFS_FNDR_HASBEENINITED (1 << 8)
|
||||
# define HFS_FNDR_RESERVED (1 << 9)
|
||||
# define HFS_FNDR_HASCUSTOMICON (1 << 10)
|
||||
# define HFS_FNDR_ISSTATIONERY (1 << 11)
|
||||
# define HFS_FNDR_NAMELOCKED (1 << 12)
|
||||
# define HFS_FNDR_HASBUNDLE (1 << 13)
|
||||
# define HFS_FNDR_ISINVISIBLE (1 << 14)
|
||||
# define HFS_FNDR_ISALIAS (1 << 15)
|
||||
|
||||
extern const char *hfs_error;
|
||||
extern const unsigned char hfs_charorder[];
|
||||
|
||||
# define HFS_MODE_RDONLY 0
|
||||
# define HFS_MODE_RDWR 1
|
||||
# define HFS_MODE_ANY 2
|
||||
|
||||
# define HFS_MODE_MASK 0x0003
|
||||
|
||||
# define HFS_OPT_NOCACHE 0x0100
|
||||
# define HFS_OPT_2048 0x0200
|
||||
# define HFS_OPT_ZERO 0x0400
|
||||
# define HFS_OPT_DC42HEADER 0x0800
|
||||
|
||||
# define HFS_SEEK_SET 0
|
||||
# define HFS_SEEK_CUR 1
|
||||
# define HFS_SEEK_END 2
|
||||
|
||||
hfsvol *hfs_mount(const char *, int, int);
|
||||
int hfs_flush(hfsvol *);
|
||||
void hfs_flushall(void);
|
||||
int hfs_umount(hfsvol *);
|
||||
void hfs_umountall(void);
|
||||
hfsvol *hfs_getvol(const char *);
|
||||
void hfs_setvol(hfsvol *);
|
||||
|
||||
int hfs_vstat(hfsvol *, hfsvolent *);
|
||||
int hfs_vsetattr(hfsvol *, hfsvolent *);
|
||||
|
||||
int hfs_chdir(hfsvol *, const char *);
|
||||
unsigned long hfs_getcwd(hfsvol *);
|
||||
int hfs_setcwd(hfsvol *, unsigned long);
|
||||
int hfs_dirinfo(hfsvol *, unsigned long *, char *);
|
||||
|
||||
hfsdir *hfs_opendir(hfsvol *, const char *);
|
||||
int hfs_readdir(hfsdir *, hfsdirent *);
|
||||
int hfs_closedir(hfsdir *);
|
||||
|
||||
hfsfile *hfs_create(hfsvol *, const char *, const char *, const char *);
|
||||
hfsfile *hfs_open(hfsvol *, const char *);
|
||||
int hfs_setfork(hfsfile *, int);
|
||||
int hfs_getfork(hfsfile *);
|
||||
unsigned long hfs_read(hfsfile *, void *, unsigned long);
|
||||
unsigned long hfs_write(hfsfile *, const void *, unsigned long);
|
||||
int hfs_truncate(hfsfile *, unsigned long);
|
||||
unsigned long hfs_seek(hfsfile *, long, int);
|
||||
int hfs_close(hfsfile *);
|
||||
|
||||
int hfs_stat(hfsvol *, const char *, hfsdirent *);
|
||||
int hfs_fstat(hfsfile *, hfsdirent *);
|
||||
int hfs_setattr(hfsvol *, const char *, const hfsdirent *);
|
||||
int hfs_fsetattr(hfsfile *, const hfsdirent *);
|
||||
|
||||
int hfs_mkdir(hfsvol *, const char *);
|
||||
int hfs_rmdir(hfsvol *, const char *);
|
||||
|
||||
int hfs_delete(hfsvol *, const char *);
|
||||
int hfs_rename(hfsvol *, const char *, const char *);
|
||||
|
||||
int hfs_zero(const char *, unsigned int, unsigned long *);
|
||||
int hfs_mkpart(const char *, unsigned long);
|
||||
int hfs_nparts(const char *);
|
||||
|
||||
int hfs_format(const char *, int, int,
|
||||
const char *, unsigned int, const unsigned long []);
|
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: libhfs.h,v 1.7 1998/11/02 22:09:02 rob Exp $
|
||||
*/
|
||||
|
||||
# include "hfs.h"
|
||||
# include "apple.h"
|
||||
|
||||
extern int errno;
|
||||
|
||||
# define ERROR(code, str) \
|
||||
do { hfs_error = (str), errno = (code); goto fail; } while (0)
|
||||
|
||||
# ifdef DEBUG
|
||||
# define ASSERT(cond) do { if (! (cond)) abort(); } while (0)
|
||||
# else
|
||||
# define ASSERT(cond) /* nothing */
|
||||
# endif
|
||||
|
||||
# define SIZE(type, n) ((size_t) (sizeof(type) * (n)))
|
||||
# define ALLOC(type, n) ((type *) malloc(SIZE(type, n)))
|
||||
# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0)
|
||||
# define FREE(ptr) ((ptr) ? (void) free((void *) ptr) : (void) 0)
|
||||
|
||||
# define REALLOC(ptr, type, n) \
|
||||
((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n))))
|
||||
# define REALLOCX(ptr, type, n) \
|
||||
((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type *) 0))
|
||||
|
||||
# define BMTST(bm, num) \
|
||||
(((const byte *) (bm))[(num) >> 3] & (0x80 >> ((num) & 0x07)))
|
||||
# define BMSET(bm, num) \
|
||||
(((byte *) (bm))[(num) >> 3] |= (0x80 >> ((num) & 0x07)))
|
||||
# define BMCLR(bm, num) \
|
||||
(((byte *) (bm))[(num) >> 3] &= ~(0x80 >> ((num) & 0x07)))
|
||||
|
||||
# define STRINGIZE(x) #x
|
||||
# define STR(x) STRINGIZE(x)
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef byte block[HFS_BLOCKSZ];
|
||||
|
||||
typedef struct _bucket_ {
|
||||
int flags; /* bit flags */
|
||||
unsigned int count; /* number of times this block is requested */
|
||||
|
||||
unsigned long bnum; /* logical block number */
|
||||
block *data; /* pointer to block contents */
|
||||
|
||||
struct _bucket_ *cnext; /* next bucket in cache chain */
|
||||
struct _bucket_ *cprev; /* previous bucket in cache chain */
|
||||
|
||||
struct _bucket_ *hnext; /* next bucket in hash chain */
|
||||
struct _bucket_ **hprev; /* previous bucket's pointer to this bucket */
|
||||
} bucket;
|
||||
|
||||
# define HFS_BUCKET_INUSE 0x01
|
||||
# define HFS_BUCKET_DIRTY 0x02
|
||||
|
||||
# define HFS_CACHESZ 128
|
||||
# define HFS_HASHSZ 32
|
||||
# define HFS_BLOCKBUFSZ 16
|
||||
|
||||
typedef struct {
|
||||
struct _hfsvol_ *vol; /* volume to which cache belongs */
|
||||
bucket *tail; /* end of bucket chain */
|
||||
|
||||
unsigned int hits; /* number of cache hits */
|
||||
unsigned int misses; /* number of cache misses */
|
||||
|
||||
bucket chain[HFS_CACHESZ]; /* cache bucket chain */
|
||||
bucket *hash[HFS_HASHSZ]; /* hash table for bucket chain */
|
||||
|
||||
block pool[HFS_CACHESZ]; /* physical blocks in cache */
|
||||
} bcache;
|
||||
|
||||
# define HFS_MAP1SZ 256
|
||||
# define HFS_MAPXSZ 492
|
||||
|
||||
# define HFS_NODEREC(nd, rnum) ((nd).data + (nd).roff[rnum])
|
||||
# define HFS_RECLEN(nd, rnum) ((nd).roff[(rnum) + 1] - (nd).roff[rnum])
|
||||
|
||||
# define HFS_RECKEYLEN(ptr) (*(const byte *) (ptr))
|
||||
# define HFS_RECKEYSKIP(ptr) ((size_t) ((1 + HFS_RECKEYLEN(ptr) + 1) & ~1))
|
||||
# define HFS_RECDATA(ptr) ((ptr) + HFS_RECKEYSKIP(ptr))
|
||||
|
||||
# define HFS_SETKEYLEN(ptr, x) (*(byte *) (ptr) = (x))
|
||||
|
||||
# define HFS_CATDATALEN sizeof(CatDataRec)
|
||||
# define HFS_EXTDATALEN sizeof(ExtDataRec)
|
||||
# define HFS_MAX_DATALEN (HFS_CATDATALEN > HFS_EXTDATALEN ? \
|
||||
HFS_CATDATALEN : HFS_EXTDATALEN)
|
||||
|
||||
# define HFS_CATKEYLEN sizeof(CatKeyRec)
|
||||
# define HFS_EXTKEYLEN sizeof(ExtKeyRec)
|
||||
# define HFS_MAX_KEYLEN (HFS_CATKEYLEN > HFS_EXTKEYLEN ? \
|
||||
HFS_CATKEYLEN : HFS_EXTKEYLEN)
|
||||
|
||||
# define HFS_MAX_CATRECLEN (HFS_CATKEYLEN + HFS_CATDATALEN)
|
||||
# define HFS_MAX_EXTRECLEN (HFS_EXTKEYLEN + HFS_EXTDATALEN)
|
||||
# define HFS_MAX_RECLEN (HFS_MAX_KEYLEN + HFS_MAX_DATALEN)
|
||||
|
||||
# define HFS_SIGWORD 0x4244
|
||||
# define HFS_SIGWORD_MFS ((Integer) 0xd2d7)
|
||||
|
||||
# define HFS_ATRB_BUSY (1 << 6)
|
||||
# define HFS_ATRB_HLOCKED (1 << 7)
|
||||
# define HFS_ATRB_UMOUNTED (1 << 8)
|
||||
# define HFS_ATRB_BBSPARED (1 << 9)
|
||||
# define HFS_ATRB_BVINCONSIS (1 << 11)
|
||||
# define HFS_ATRB_COPYPROT (1 << 14)
|
||||
# define HFS_ATRB_SLOCKED (1 << 15)
|
||||
|
||||
struct _hfsfile_ {
|
||||
struct _hfsvol_ *vol; /* pointer to volume descriptor */
|
||||
unsigned long parid; /* parent directory ID of this file */
|
||||
char name[HFS_MAX_FLEN + 1]; /* catalog name of this file */
|
||||
CatDataRec cat; /* catalog information */
|
||||
ExtDataRec ext; /* current extent record */
|
||||
unsigned int fabn; /* starting file allocation block number */
|
||||
int fork; /* current selected fork for I/O */
|
||||
unsigned long pos; /* current file seek pointer */
|
||||
int flags; /* bit flags */
|
||||
|
||||
struct _hfsfile_ *prev;
|
||||
struct _hfsfile_ *next;
|
||||
};
|
||||
|
||||
# define HFS_FILE_UPDATE_CATREC 0x01
|
||||
|
||||
# define HFS_MAX_NRECS 35 /* maximum based on minimum record size */
|
||||
|
||||
typedef struct _node_ {
|
||||
struct _btree_ *bt; /* btree to which this node belongs */
|
||||
unsigned long nnum; /* node index */
|
||||
NodeDescriptor nd; /* node descriptor */
|
||||
int rnum; /* current record index */
|
||||
UInteger roff[HFS_MAX_NRECS + 1];
|
||||
/* record offsets */
|
||||
block data; /* raw contents of node */
|
||||
} node;
|
||||
|
||||
struct _hfsdir_ {
|
||||
struct _hfsvol_ *vol; /* associated volume */
|
||||
unsigned long dirid; /* directory ID of interest (or 0) */
|
||||
|
||||
node n; /* current B*-tree node */
|
||||
struct _hfsvol_ *vptr; /* current volume pointer */
|
||||
|
||||
struct _hfsdir_ *prev;
|
||||
struct _hfsdir_ *next;
|
||||
};
|
||||
|
||||
typedef void (*keyunpackfunc)(const byte *, void *);
|
||||
typedef int (*keycomparefunc)(const void *, const void *);
|
||||
|
||||
typedef struct _btree_ {
|
||||
hfsfile f; /* subset file information */
|
||||
node hdrnd; /* header node */
|
||||
BTHdrRec hdr; /* header record */
|
||||
byte *map; /* usage bitmap */
|
||||
unsigned long mapsz; /* number of bytes in bitmap */
|
||||
int flags; /* bit flags */
|
||||
|
||||
keyunpackfunc keyunpack; /* key unpacking function */
|
||||
keycomparefunc keycompare; /* key comparison function */
|
||||
} btree;
|
||||
|
||||
# define HFS_BT_UPDATE_HDR 0x01
|
||||
|
||||
struct _hfsvol_ {
|
||||
void *priv; /* OS-dependent private descriptor data */
|
||||
long base; /* base of volume, for disk images with header */
|
||||
int flags; /* bit flags */
|
||||
|
||||
int pnum; /* ordinal HFS partition number */
|
||||
unsigned long vstart; /* logical block offset to start of volume */
|
||||
unsigned long vlen; /* number of logical blocks in volume */
|
||||
unsigned int lpa; /* number of logical blocks per allocation block */
|
||||
|
||||
bcache *cache; /* cache of recently used blocks */
|
||||
|
||||
MDB mdb; /* master directory block */
|
||||
block *vbm; /* volume bitmap */
|
||||
unsigned short vbmsz; /* number of blocks in bitmap */
|
||||
|
||||
btree ext; /* B*-tree control block for extents overflow file */
|
||||
btree cat; /* B*-tree control block for catalog file */
|
||||
|
||||
unsigned long cwd; /* directory id of current working directory */
|
||||
|
||||
int refs; /* number of external references to this volume */
|
||||
hfsfile *files; /* list of open files */
|
||||
hfsdir *dirs; /* list of open directories */
|
||||
|
||||
struct _hfsvol_ *prev;
|
||||
struct _hfsvol_ *next;
|
||||
};
|
||||
|
||||
# define HFS_VOL_OPEN 0x0001
|
||||
# define HFS_VOL_MOUNTED 0x0002
|
||||
# define HFS_VOL_READONLY 0x0004
|
||||
# define HFS_VOL_USINGCACHE 0x0008
|
||||
|
||||
# define HFS_VOL_UPDATE_MDB 0x0010
|
||||
# define HFS_VOL_UPDATE_ALTMDB 0x0020
|
||||
# define HFS_VOL_UPDATE_VBM 0x0040
|
||||
|
||||
# define HFS_VOL_OPT_MASK 0xff00
|
||||
|
||||
extern hfsvol *hfs_mounts;
|
|
@ -0,0 +1,470 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: low.c,v 1.8 1998/11/02 22:09:03 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <errno.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "low.h"
|
||||
# include "data.h"
|
||||
# include "block.h"
|
||||
# include "file.h"
|
||||
|
||||
/*
|
||||
* NAME: low->getddr()
|
||||
* DESCRIPTION: read a driver descriptor record
|
||||
*/
|
||||
int l_getddr(hfsvol *vol, Block0 *ddr)
|
||||
{
|
||||
block b;
|
||||
const byte *ptr = b;
|
||||
int i;
|
||||
|
||||
if (b_readpb(vol, 0, &b, 1) == -1)
|
||||
goto fail;
|
||||
|
||||
d_fetchsw(&ptr, &ddr->sbSig);
|
||||
d_fetchsw(&ptr, &ddr->sbBlkSize);
|
||||
d_fetchsl(&ptr, &ddr->sbBlkCount);
|
||||
d_fetchsw(&ptr, &ddr->sbDevType);
|
||||
d_fetchsw(&ptr, &ddr->sbDevId);
|
||||
d_fetchsl(&ptr, &ddr->sbData);
|
||||
d_fetchsw(&ptr, &ddr->sbDrvrCount);
|
||||
d_fetchsl(&ptr, &ddr->ddBlock);
|
||||
d_fetchsw(&ptr, &ddr->ddSize);
|
||||
d_fetchsw(&ptr, &ddr->ddType);
|
||||
|
||||
for (i = 0; i < 243; ++i)
|
||||
d_fetchsw(&ptr, &ddr->ddPad[i]);
|
||||
|
||||
ASSERT(ptr - b == HFS_BLOCKSZ);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: low->putddr()
|
||||
* DESCRIPTION: write a driver descriptor record
|
||||
*/
|
||||
int l_putddr(hfsvol *vol, const Block0 *ddr)
|
||||
{
|
||||
block b;
|
||||
byte *ptr = b;
|
||||
int i;
|
||||
|
||||
d_storesw(&ptr, ddr->sbSig);
|
||||
d_storesw(&ptr, ddr->sbBlkSize);
|
||||
d_storesl(&ptr, ddr->sbBlkCount);
|
||||
d_storesw(&ptr, ddr->sbDevType);
|
||||
d_storesw(&ptr, ddr->sbDevId);
|
||||
d_storesl(&ptr, ddr->sbData);
|
||||
d_storesw(&ptr, ddr->sbDrvrCount);
|
||||
d_storesl(&ptr, ddr->ddBlock);
|
||||
d_storesw(&ptr, ddr->ddSize);
|
||||
d_storesw(&ptr, ddr->ddType);
|
||||
|
||||
for (i = 0; i < 243; ++i)
|
||||
d_storesw(&ptr, ddr->ddPad[i]);
|
||||
|
||||
ASSERT(ptr - b == HFS_BLOCKSZ);
|
||||
|
||||
if (b_writepb(vol, 0, &b, 1) == -1)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: low->getpmentry()
|
||||
* DESCRIPTION: read a partition map entry
|
||||
*/
|
||||
int l_getpmentry(hfsvol *vol, Partition *map, unsigned long bnum)
|
||||
{
|
||||
block b;
|
||||
const byte *ptr = b;
|
||||
int i;
|
||||
|
||||
if (b_readpb(vol, bnum, &b, 1) == -1)
|
||||
goto fail;
|
||||
|
||||
d_fetchsw(&ptr, &map->pmSig);
|
||||
d_fetchsw(&ptr, &map->pmSigPad);
|
||||
d_fetchsl(&ptr, &map->pmMapBlkCnt);
|
||||
d_fetchsl(&ptr, &map->pmPyPartStart);
|
||||
d_fetchsl(&ptr, &map->pmPartBlkCnt);
|
||||
|
||||
strncpy((char *) map->pmPartName, (const char *) ptr, 32);
|
||||
map->pmPartName[32] = 0;
|
||||
ptr += 32;
|
||||
|
||||
strncpy((char *) map->pmParType, (const char *) ptr, 32);
|
||||
map->pmParType[32] = 0;
|
||||
ptr += 32;
|
||||
|
||||
d_fetchsl(&ptr, &map->pmLgDataStart);
|
||||
d_fetchsl(&ptr, &map->pmDataCnt);
|
||||
d_fetchsl(&ptr, &map->pmPartStatus);
|
||||
d_fetchsl(&ptr, &map->pmLgBootStart);
|
||||
d_fetchsl(&ptr, &map->pmBootSize);
|
||||
d_fetchsl(&ptr, &map->pmBootAddr);
|
||||
d_fetchsl(&ptr, &map->pmBootAddr2);
|
||||
d_fetchsl(&ptr, &map->pmBootEntry);
|
||||
d_fetchsl(&ptr, &map->pmBootEntry2);
|
||||
d_fetchsl(&ptr, &map->pmBootCksum);
|
||||
|
||||
strncpy((char *) map->pmProcessor, (const char *) ptr, 16);
|
||||
map->pmProcessor[16] = 0;
|
||||
ptr += 16;
|
||||
|
||||
for (i = 0; i < 188; ++i)
|
||||
d_fetchsw(&ptr, &map->pmPad[i]);
|
||||
|
||||
ASSERT(ptr - b == HFS_BLOCKSZ);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: low->putpmentry()
|
||||
* DESCRIPTION: write a partition map entry
|
||||
*/
|
||||
int l_putpmentry(hfsvol *vol, const Partition *map, unsigned long bnum)
|
||||
{
|
||||
block b;
|
||||
byte *ptr = b;
|
||||
int i;
|
||||
|
||||
d_storesw(&ptr, map->pmSig);
|
||||
d_storesw(&ptr, map->pmSigPad);
|
||||
d_storesl(&ptr, map->pmMapBlkCnt);
|
||||
d_storesl(&ptr, map->pmPyPartStart);
|
||||
d_storesl(&ptr, map->pmPartBlkCnt);
|
||||
|
||||
memset(ptr, 0, 32);
|
||||
strncpy((char *) ptr, (const char *) map->pmPartName, 32);
|
||||
ptr += 32;
|
||||
|
||||
memset(ptr, 0, 32);
|
||||
strncpy((char *) ptr, (const char *) map->pmParType, 32);
|
||||
ptr += 32;
|
||||
|
||||
d_storesl(&ptr, map->pmLgDataStart);
|
||||
d_storesl(&ptr, map->pmDataCnt);
|
||||
d_storesl(&ptr, map->pmPartStatus);
|
||||
d_storesl(&ptr, map->pmLgBootStart);
|
||||
d_storesl(&ptr, map->pmBootSize);
|
||||
d_storesl(&ptr, map->pmBootAddr);
|
||||
d_storesl(&ptr, map->pmBootAddr2);
|
||||
d_storesl(&ptr, map->pmBootEntry);
|
||||
d_storesl(&ptr, map->pmBootEntry2);
|
||||
d_storesl(&ptr, map->pmBootCksum);
|
||||
|
||||
memset(ptr, 0, 16);
|
||||
strncpy((char *) ptr, (const char *) map->pmProcessor, 16);
|
||||
ptr += 16;
|
||||
|
||||
for (i = 0; i < 188; ++i)
|
||||
d_storesw(&ptr, map->pmPad[i]);
|
||||
|
||||
ASSERT(ptr - b == HFS_BLOCKSZ);
|
||||
|
||||
if (b_writepb(vol, bnum, &b, 1) == -1)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: low->getbb()
|
||||
* DESCRIPTION: read a volume's boot blocks
|
||||
*/
|
||||
int l_getbb(hfsvol *vol, BootBlkHdr *bb, byte *bootcode)
|
||||
{
|
||||
block b;
|
||||
const byte *ptr = b;
|
||||
|
||||
if (b_readlb(vol, 0, &b) == -1)
|
||||
goto fail;
|
||||
|
||||
d_fetchsw(&ptr, &bb->bbID);
|
||||
d_fetchsl(&ptr, &bb->bbEntry);
|
||||
d_fetchsw(&ptr, &bb->bbVersion);
|
||||
d_fetchsw(&ptr, &bb->bbPageFlags);
|
||||
|
||||
d_fetchstr(&ptr, bb->bbSysName, sizeof(bb->bbSysName));
|
||||
d_fetchstr(&ptr, bb->bbShellName, sizeof(bb->bbShellName));
|
||||
d_fetchstr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name));
|
||||
d_fetchstr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name));
|
||||
d_fetchstr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName));
|
||||
d_fetchstr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName));
|
||||
d_fetchstr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName));
|
||||
|
||||
d_fetchsw(&ptr, &bb->bbCntFCBs);
|
||||
d_fetchsw(&ptr, &bb->bbCntEvts);
|
||||
d_fetchsl(&ptr, &bb->bb128KSHeap);
|
||||
d_fetchsl(&ptr, &bb->bb256KSHeap);
|
||||
d_fetchsl(&ptr, &bb->bbSysHeapSize);
|
||||
d_fetchsw(&ptr, &bb->filler);
|
||||
d_fetchsl(&ptr, &bb->bbSysHeapExtra);
|
||||
d_fetchsl(&ptr, &bb->bbSysHeapFract);
|
||||
|
||||
ASSERT(ptr - b == 148);
|
||||
|
||||
if (bootcode)
|
||||
{
|
||||
memcpy(bootcode, ptr, HFS_BOOTCODE1LEN);
|
||||
|
||||
if (b_readlb(vol, 1, &b) == -1)
|
||||
goto fail;
|
||||
|
||||
memcpy(bootcode + HFS_BOOTCODE1LEN, b, HFS_BOOTCODE2LEN);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: low->putbb()
|
||||
* DESCRIPTION: write a volume's boot blocks
|
||||
*/
|
||||
int l_putbb(hfsvol *vol, const BootBlkHdr *bb, const byte *bootcode)
|
||||
{
|
||||
block b;
|
||||
byte *ptr = b;
|
||||
|
||||
d_storesw(&ptr, bb->bbID);
|
||||
d_storesl(&ptr, bb->bbEntry);
|
||||
d_storesw(&ptr, bb->bbVersion);
|
||||
d_storesw(&ptr, bb->bbPageFlags);
|
||||
|
||||
d_storestr(&ptr, bb->bbSysName, sizeof(bb->bbSysName));
|
||||
d_storestr(&ptr, bb->bbShellName, sizeof(bb->bbShellName));
|
||||
d_storestr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name));
|
||||
d_storestr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name));
|
||||
d_storestr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName));
|
||||
d_storestr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName));
|
||||
d_storestr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName));
|
||||
|
||||
d_storesw(&ptr, bb->bbCntFCBs);
|
||||
d_storesw(&ptr, bb->bbCntEvts);
|
||||
d_storesl(&ptr, bb->bb128KSHeap);
|
||||
d_storesl(&ptr, bb->bb256KSHeap);
|
||||
d_storesl(&ptr, bb->bbSysHeapSize);
|
||||
d_storesw(&ptr, bb->filler);
|
||||
d_storesl(&ptr, bb->bbSysHeapExtra);
|
||||
d_storesl(&ptr, bb->bbSysHeapFract);
|
||||
|
||||
ASSERT(ptr - b == 148);
|
||||
|
||||
if (bootcode)
|
||||
memcpy(ptr, bootcode, HFS_BOOTCODE1LEN);
|
||||
else
|
||||
memset(ptr, 0, HFS_BOOTCODE1LEN);
|
||||
|
||||
if (b_writelb(vol, 0, &b) == -1)
|
||||
goto fail;
|
||||
|
||||
if (bootcode)
|
||||
memcpy(&b, bootcode + HFS_BOOTCODE1LEN, HFS_BOOTCODE2LEN);
|
||||
else
|
||||
memset(&b, 0, HFS_BOOTCODE2LEN);
|
||||
|
||||
if (b_writelb(vol, 1, &b) == -1)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: low->getmdb()
|
||||
* DESCRIPTION: read a master directory block
|
||||
*/
|
||||
int l_getmdb(hfsvol *vol, MDB *mdb, int backup)
|
||||
{
|
||||
block b;
|
||||
const byte *ptr = b;
|
||||
int i;
|
||||
|
||||
if (b_readlb(vol, backup ? vol->vlen - 2 : 2, &b) == -1)
|
||||
goto fail;
|
||||
|
||||
d_fetchsw(&ptr, &mdb->drSigWord);
|
||||
d_fetchsl(&ptr, &mdb->drCrDate);
|
||||
d_fetchsl(&ptr, &mdb->drLsMod);
|
||||
d_fetchsw(&ptr, &mdb->drAtrb);
|
||||
d_fetchuw(&ptr, &mdb->drNmFls);
|
||||
d_fetchuw(&ptr, &mdb->drVBMSt);
|
||||
d_fetchuw(&ptr, &mdb->drAllocPtr);
|
||||
d_fetchuw(&ptr, &mdb->drNmAlBlks);
|
||||
d_fetchul(&ptr, &mdb->drAlBlkSiz);
|
||||
d_fetchul(&ptr, &mdb->drClpSiz);
|
||||
d_fetchuw(&ptr, &mdb->drAlBlSt);
|
||||
d_fetchsl(&ptr, &mdb->drNxtCNID);
|
||||
d_fetchuw(&ptr, &mdb->drFreeBks);
|
||||
|
||||
d_fetchstr(&ptr, mdb->drVN, sizeof(mdb->drVN));
|
||||
|
||||
ASSERT(ptr - b == 64);
|
||||
|
||||
d_fetchsl(&ptr, &mdb->drVolBkUp);
|
||||
d_fetchsw(&ptr, &mdb->drVSeqNum);
|
||||
d_fetchul(&ptr, &mdb->drWrCnt);
|
||||
d_fetchul(&ptr, &mdb->drXTClpSiz);
|
||||
d_fetchul(&ptr, &mdb->drCTClpSiz);
|
||||
d_fetchuw(&ptr, &mdb->drNmRtDirs);
|
||||
d_fetchul(&ptr, &mdb->drFilCnt);
|
||||
d_fetchul(&ptr, &mdb->drDirCnt);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
d_fetchsl(&ptr, &mdb->drFndrInfo[i]);
|
||||
|
||||
ASSERT(ptr - b == 124);
|
||||
|
||||
d_fetchuw(&ptr, &mdb->drEmbedSigWord);
|
||||
d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrStABN);
|
||||
d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrNumABlks);
|
||||
|
||||
d_fetchul(&ptr, &mdb->drXTFlSize);
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrStABN);
|
||||
d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
ASSERT(ptr - b == 146);
|
||||
|
||||
d_fetchul(&ptr, &mdb->drCTFlSize);
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrStABN);
|
||||
d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
ASSERT(ptr - b == 162);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: low->putmdb()
|
||||
* DESCRIPTION: write master directory block(s)
|
||||
*/
|
||||
int l_putmdb(hfsvol *vol, const MDB *mdb, int backup)
|
||||
{
|
||||
block b;
|
||||
byte *ptr = b;
|
||||
int i;
|
||||
|
||||
d_storesw(&ptr, mdb->drSigWord);
|
||||
d_storesl(&ptr, mdb->drCrDate);
|
||||
d_storesl(&ptr, mdb->drLsMod);
|
||||
d_storesw(&ptr, mdb->drAtrb);
|
||||
d_storeuw(&ptr, mdb->drNmFls);
|
||||
d_storeuw(&ptr, mdb->drVBMSt);
|
||||
d_storeuw(&ptr, mdb->drAllocPtr);
|
||||
d_storeuw(&ptr, mdb->drNmAlBlks);
|
||||
d_storeul(&ptr, mdb->drAlBlkSiz);
|
||||
d_storeul(&ptr, mdb->drClpSiz);
|
||||
d_storeuw(&ptr, mdb->drAlBlSt);
|
||||
d_storesl(&ptr, mdb->drNxtCNID);
|
||||
d_storeuw(&ptr, mdb->drFreeBks);
|
||||
|
||||
d_storestr(&ptr, mdb->drVN, sizeof(mdb->drVN));
|
||||
|
||||
ASSERT(ptr - b == 64);
|
||||
|
||||
d_storesl(&ptr, mdb->drVolBkUp);
|
||||
d_storesw(&ptr, mdb->drVSeqNum);
|
||||
d_storeul(&ptr, mdb->drWrCnt);
|
||||
d_storeul(&ptr, mdb->drXTClpSiz);
|
||||
d_storeul(&ptr, mdb->drCTClpSiz);
|
||||
d_storeuw(&ptr, mdb->drNmRtDirs);
|
||||
d_storeul(&ptr, mdb->drFilCnt);
|
||||
d_storeul(&ptr, mdb->drDirCnt);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
d_storesl(&ptr, mdb->drFndrInfo[i]);
|
||||
|
||||
ASSERT(ptr - b == 124);
|
||||
|
||||
d_storeuw(&ptr, mdb->drEmbedSigWord);
|
||||
d_storeuw(&ptr, mdb->drEmbedExtent.xdrStABN);
|
||||
d_storeuw(&ptr, mdb->drEmbedExtent.xdrNumABlks);
|
||||
|
||||
d_storeul(&ptr, mdb->drXTFlSize);
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_storeuw(&ptr, mdb->drXTExtRec[i].xdrStABN);
|
||||
d_storeuw(&ptr, mdb->drXTExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
ASSERT(ptr - b == 146);
|
||||
|
||||
d_storeul(&ptr, mdb->drCTFlSize);
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_storeuw(&ptr, mdb->drCTExtRec[i].xdrStABN);
|
||||
d_storeuw(&ptr, mdb->drCTExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
ASSERT(ptr - b == 162);
|
||||
|
||||
memset(ptr, 0, HFS_BLOCKSZ - (ptr - b));
|
||||
|
||||
if (b_writelb(vol, 2, &b) == -1 ||
|
||||
(backup && b_writelb(vol, vol->vlen - 2, &b) == -1))
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: low.h,v 1.6 1998/04/11 08:27:13 rob Exp $
|
||||
*/
|
||||
|
||||
# define HFS_DDR_SIGWORD 0x4552
|
||||
|
||||
# define HFS_PM_SIGWORD 0x504d
|
||||
# define HFS_PM_SIGWORD_OLD 0x5453
|
||||
|
||||
# define HFS_BB_SIGWORD 0x4c4b
|
||||
|
||||
# define HFS_BOOTCODE1LEN (HFS_BLOCKSZ - 148)
|
||||
# define HFS_BOOTCODE2LEN HFS_BLOCKSZ
|
||||
|
||||
# define HFS_BOOTCODELEN (HFS_BOOTCODE1LEN + HFS_BOOTCODE2LEN)
|
||||
|
||||
int l_getddr(hfsvol *, Block0 *);
|
||||
int l_putddr(hfsvol *, const Block0 *);
|
||||
|
||||
int l_getpmentry(hfsvol *, Partition *, unsigned long);
|
||||
int l_putpmentry(hfsvol *, const Partition *, unsigned long);
|
||||
|
||||
int l_getbb(hfsvol *, BootBlkHdr *, byte *);
|
||||
int l_putbb(hfsvol *, const BootBlkHdr *, const byte *);
|
||||
|
||||
int l_getmdb(hfsvol *, MDB *, int);
|
||||
int l_putmdb(hfsvol *, const MDB *, int);
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: medium.c,v 1.4 1998/11/02 22:09:04 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <errno.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "block.h"
|
||||
# include "low.h"
|
||||
# include "medium.h"
|
||||
|
||||
/* Driver Descriptor Record Routines ======================================= */
|
||||
|
||||
/*
|
||||
* NAME: medium->zeroddr()
|
||||
* DESCRIPTION: write a new/empty driver descriptor record
|
||||
*/
|
||||
int m_zeroddr(hfsvol *vol)
|
||||
{
|
||||
Block0 ddr;
|
||||
int i;
|
||||
|
||||
ASSERT(vol->pnum == 0 && vol->vlen != 0);
|
||||
|
||||
ddr.sbSig = HFS_DDR_SIGWORD;
|
||||
ddr.sbBlkSize = HFS_BLOCKSZ;
|
||||
ddr.sbBlkCount = vol->vlen;
|
||||
|
||||
ddr.sbDevType = 0;
|
||||
ddr.sbDevId = 0;
|
||||
ddr.sbData = 0;
|
||||
|
||||
ddr.sbDrvrCount = 0;
|
||||
|
||||
ddr.ddBlock = 0;
|
||||
ddr.ddSize = 0;
|
||||
ddr.ddType = 0;
|
||||
|
||||
for (i = 0; i < 243; ++i)
|
||||
ddr.ddPad[i] = 0;
|
||||
|
||||
return l_putddr(vol, &ddr);
|
||||
}
|
||||
|
||||
/* Partition Map Routines ================================================== */
|
||||
|
||||
/*
|
||||
* NAME: medium->zeropm()
|
||||
* DESCRIPTION: write new/empty partition map
|
||||
*/
|
||||
int m_zeropm(hfsvol *vol, unsigned int maxparts)
|
||||
{
|
||||
Partition map;
|
||||
unsigned int i;
|
||||
|
||||
ASSERT(vol->pnum == 0 && vol->vlen != 0);
|
||||
|
||||
if (maxparts < 2)
|
||||
ERROR(EINVAL, "must allow at least 2 partitions");
|
||||
|
||||
/* first entry: partition map itself */
|
||||
|
||||
map.pmSig = HFS_PM_SIGWORD;
|
||||
map.pmSigPad = 0;
|
||||
map.pmMapBlkCnt = 2;
|
||||
|
||||
map.pmPyPartStart = 1;
|
||||
map.pmPartBlkCnt = maxparts;
|
||||
|
||||
strcpy((char *) map.pmPartName, "Apple");
|
||||
strcpy((char *) map.pmParType, "Apple_partition_map");
|
||||
|
||||
map.pmLgDataStart = 0;
|
||||
map.pmDataCnt = map.pmPartBlkCnt;
|
||||
|
||||
map.pmPartStatus = 0;
|
||||
|
||||
map.pmLgBootStart = 0;
|
||||
map.pmBootSize = 0;
|
||||
map.pmBootAddr = 0;
|
||||
map.pmBootAddr2 = 0;
|
||||
map.pmBootEntry = 0;
|
||||
map.pmBootEntry2 = 0;
|
||||
map.pmBootCksum = 0;
|
||||
|
||||
strcpy((char *) map.pmProcessor, "");
|
||||
|
||||
for (i = 0; i < 188; ++i)
|
||||
map.pmPad[i] = 0;
|
||||
|
||||
if (l_putpmentry(vol, &map, 1) == -1)
|
||||
goto fail;
|
||||
|
||||
/* second entry: rest of medium */
|
||||
|
||||
map.pmPyPartStart = 1 + maxparts;
|
||||
map.pmPartBlkCnt = vol->vlen - 1 - maxparts;
|
||||
|
||||
strcpy((char *) map.pmPartName, "Extra");
|
||||
strcpy((char *) map.pmParType, "Apple_Free");
|
||||
|
||||
map.pmDataCnt = map.pmPartBlkCnt;
|
||||
|
||||
if (l_putpmentry(vol, &map, 2) == -1)
|
||||
goto fail;
|
||||
|
||||
/* zero rest of partition map's partition */
|
||||
|
||||
if (maxparts > 2)
|
||||
{
|
||||
block b;
|
||||
|
||||
memset(&b, 0, sizeof(b));
|
||||
|
||||
for (i = 3; i <= maxparts; ++i)
|
||||
{
|
||||
if (b_writepb(vol, i, &b, 1) == -1)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: medium->findpmentry()
|
||||
* DESCRIPTION: locate a partition map entry
|
||||
*/
|
||||
int m_findpmentry(hfsvol *vol, const char *type,
|
||||
Partition *map, unsigned long *start)
|
||||
{
|
||||
unsigned long bnum;
|
||||
int found = 0;
|
||||
|
||||
if (start && *start > 0)
|
||||
{
|
||||
bnum = *start;
|
||||
|
||||
if (bnum++ >= (unsigned long) map->pmMapBlkCnt)
|
||||
ERROR(EINVAL, "partition not found");
|
||||
}
|
||||
else
|
||||
bnum = 1;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (l_getpmentry(vol, map, bnum) == -1)
|
||||
{
|
||||
found = -1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (map->pmSig != HFS_PM_SIGWORD)
|
||||
{
|
||||
found = -1;
|
||||
|
||||
if (map->pmSig == HFS_PM_SIGWORD_OLD)
|
||||
ERROR(EINVAL, "old partition map format not supported");
|
||||
else
|
||||
ERROR(EINVAL, "invalid partition map");
|
||||
}
|
||||
|
||||
if (strcmp((char *) map->pmParType, type) == 0)
|
||||
{
|
||||
found = 1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (bnum++ >= (unsigned long) map->pmMapBlkCnt)
|
||||
ERROR(EINVAL, "partition not found");
|
||||
}
|
||||
|
||||
done:
|
||||
if (start)
|
||||
*start = bnum;
|
||||
|
||||
fail:
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: medium->mkpart()
|
||||
* DESCRIPTION: create a new partition from available free space
|
||||
*/
|
||||
int m_mkpart(hfsvol *vol,
|
||||
const char *name, const char *type, unsigned long len)
|
||||
{
|
||||
Partition map;
|
||||
unsigned int nparts, maxparts;
|
||||
unsigned long bnum, start, remain;
|
||||
int found;
|
||||
|
||||
if (strlen(name) > 32 ||
|
||||
strlen(type) > 32)
|
||||
ERROR(EINVAL, "partition name/type can each be at most 32 chars");
|
||||
|
||||
if (len == 0)
|
||||
ERROR(EINVAL, "partition length must be > 0");
|
||||
|
||||
found = m_findpmentry(vol, "Apple_partition_map", &map, 0);
|
||||
if (found == -1)
|
||||
goto fail;
|
||||
|
||||
if (! found)
|
||||
ERROR(EIO, "cannot find partition map's partition");
|
||||
|
||||
nparts = map.pmMapBlkCnt;
|
||||
maxparts = map.pmPartBlkCnt;
|
||||
|
||||
bnum = 0;
|
||||
do
|
||||
{
|
||||
found = m_findpmentry(vol, "Apple_Free", &map, &bnum);
|
||||
if (found == -1)
|
||||
goto fail;
|
||||
|
||||
if (! found)
|
||||
ERROR(ENOSPC, "no available partitions");
|
||||
}
|
||||
while (len > (unsigned long) map.pmPartBlkCnt);
|
||||
|
||||
start = (unsigned long) map.pmPyPartStart + len;
|
||||
remain = (unsigned long) map.pmPartBlkCnt - len;
|
||||
|
||||
if (remain && nparts >= maxparts)
|
||||
ERROR(EINVAL, "must allocate all blocks in free space");
|
||||
|
||||
map.pmPartBlkCnt = len;
|
||||
|
||||
strcpy((char *) map.pmPartName, name);
|
||||
strcpy((char *) map.pmParType, type);
|
||||
|
||||
map.pmLgDataStart = 0;
|
||||
map.pmDataCnt = len;
|
||||
|
||||
map.pmPartStatus = 0;
|
||||
|
||||
if (l_putpmentry(vol, &map, bnum) == -1)
|
||||
goto fail;
|
||||
|
||||
if (remain)
|
||||
{
|
||||
map.pmPyPartStart = start;
|
||||
map.pmPartBlkCnt = remain;
|
||||
|
||||
strcpy((char *) map.pmPartName, "Extra");
|
||||
strcpy((char *) map.pmParType, "Apple_Free");
|
||||
|
||||
map.pmDataCnt = remain;
|
||||
|
||||
if (l_putpmentry(vol, &map, ++nparts) == -1)
|
||||
goto fail;
|
||||
|
||||
for (bnum = 1; bnum <= nparts; ++bnum)
|
||||
{
|
||||
if (l_getpmentry(vol, &map, bnum) == -1)
|
||||
goto fail;
|
||||
|
||||
map.pmMapBlkCnt = nparts;
|
||||
|
||||
if (l_putpmentry(vol, &map, bnum) == -1)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Boot Blocks Routines ==================================================== */
|
||||
|
||||
/*
|
||||
* NAME: medium->zerobb()
|
||||
* DESCRIPTION: write new/empty volume boot blocks
|
||||
*/
|
||||
int m_zerobb(hfsvol *vol)
|
||||
{
|
||||
block b;
|
||||
|
||||
memset(&b, 0, sizeof(b));
|
||||
|
||||
if (b_writelb(vol, 0, &b) == -1 ||
|
||||
b_writelb(vol, 1, &b) == -1)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: medium.h,v 1.3 1998/04/11 08:27:13 rob Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
* Partition Types:
|
||||
*
|
||||
* "Apple_partition_map" partition map
|
||||
* "Apple_Driver" device driver
|
||||
* "Apple_Driver43" SCSI Manager 4.3 device driver
|
||||
* "Apple_MFS" Macintosh 64K ROM filesystem
|
||||
* "Apple_HFS" Macintosh hierarchical filesystem
|
||||
* "Apple_Unix_SVR2" Unix filesystem
|
||||
* "Apple_PRODOS" ProDOS filesystem
|
||||
* "Apple_Free" unused
|
||||
* "Apple_Scratch" empty
|
||||
*/
|
||||
|
||||
int m_zeroddr(hfsvol *);
|
||||
|
||||
int m_zeropm(hfsvol *, unsigned int);
|
||||
int m_findpmentry(hfsvol *, const char *, Partition *, unsigned long *);
|
||||
int m_mkpart(hfsvol *, const char *, const char *, unsigned long);
|
||||
|
||||
int m_zerobb(hfsvol *);
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: memcmp.c,v 1.6 1998/04/11 16:22:48 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <sys/types.h>
|
||||
|
||||
/*
|
||||
* NAME: memcmp()
|
||||
* DESCRIPTION: compare memory areas
|
||||
*/
|
||||
int memcmp(const void *s1, const void *s2, size_t n)
|
||||
{
|
||||
register const unsigned char *c1, *c2;
|
||||
|
||||
c1 = s1;
|
||||
c2 = s2;
|
||||
|
||||
while (n--)
|
||||
{
|
||||
register int diff;
|
||||
|
||||
diff = *c1++ - *c2++;
|
||||
|
||||
if (diff)
|
||||
return diff;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: node.c,v 1.9 1998/11/02 22:09:05 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <stdlib.h>
|
||||
# include <string.h>
|
||||
# include <errno.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "node.h"
|
||||
# include "data.h"
|
||||
# include "btree.h"
|
||||
|
||||
/* total bytes used by records (NOT including record offsets) */
|
||||
|
||||
# define NODEUSED(n) \
|
||||
((size_t) ((n).roff[(n).nd.ndNRecs] - (n).roff[0]))
|
||||
|
||||
/* total bytes available for new records (INCLUDING record offsets) */
|
||||
|
||||
# define NODEFREE(n) \
|
||||
((size_t) (HFS_BLOCKSZ - (n).roff[(n).nd.ndNRecs] - \
|
||||
2 * ((n).nd.ndNRecs + 1)))
|
||||
|
||||
/*
|
||||
* NAME: node->init()
|
||||
* DESCRIPTION: construct an empty node
|
||||
*/
|
||||
void n_init(node *np, btree *bt, int type, int height)
|
||||
{
|
||||
np->bt = bt;
|
||||
np->nnum = (unsigned long) -1;
|
||||
|
||||
np->nd.ndFLink = 0;
|
||||
np->nd.ndBLink = 0;
|
||||
np->nd.ndType = type;
|
||||
np->nd.ndNHeight = height;
|
||||
np->nd.ndNRecs = 0;
|
||||
np->nd.ndResv2 = 0;
|
||||
|
||||
np->rnum = -1;
|
||||
np->roff[0] = 0x00e;
|
||||
|
||||
memset(&np->data, 0, sizeof(np->data));
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: node->new()
|
||||
* DESCRIPTION: allocate a new b*-tree node
|
||||
*/
|
||||
int n_new(node *np)
|
||||
{
|
||||
btree *bt = np->bt;
|
||||
unsigned long num;
|
||||
|
||||
if (bt->hdr.bthFree == 0)
|
||||
ERROR(EIO, "b*-tree full");
|
||||
|
||||
num = 0;
|
||||
while (num < bt->hdr.bthNNodes && BMTST(bt->map, num))
|
||||
++num;
|
||||
|
||||
if (num == bt->hdr.bthNNodes)
|
||||
ERROR(EIO, "free b*-tree node not found");
|
||||
|
||||
np->nnum = num;
|
||||
|
||||
BMSET(bt->map, num);
|
||||
--bt->hdr.bthFree;
|
||||
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: node->free()
|
||||
* DESCRIPTION: deallocate and remove a b*-tree node
|
||||
*/
|
||||
int n_free(node *np)
|
||||
{
|
||||
btree *bt = np->bt;
|
||||
node sib;
|
||||
|
||||
if (bt->hdr.bthFNode == np->nnum)
|
||||
bt->hdr.bthFNode = np->nd.ndFLink;
|
||||
|
||||
if (bt->hdr.bthLNode == np->nnum)
|
||||
bt->hdr.bthLNode = np->nd.ndBLink;
|
||||
|
||||
if (np->nd.ndFLink > 0)
|
||||
{
|
||||
if (bt_getnode(&sib, bt, np->nd.ndFLink) == -1)
|
||||
goto fail;
|
||||
|
||||
sib.nd.ndBLink = np->nd.ndBLink;
|
||||
|
||||
if (bt_putnode(&sib) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (np->nd.ndBLink > 0)
|
||||
{
|
||||
if (bt_getnode(&sib, bt, np->nd.ndBLink) == -1)
|
||||
goto fail;
|
||||
|
||||
sib.nd.ndFLink = np->nd.ndFLink;
|
||||
|
||||
if (bt_putnode(&sib) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
BMCLR(bt->map, np->nnum);
|
||||
++bt->hdr.bthFree;
|
||||
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: compact()
|
||||
* DESCRIPTION: clean up a node, removing deleted records
|
||||
*/
|
||||
static
|
||||
void compact(node *np)
|
||||
{
|
||||
byte *ptr;
|
||||
int offset, nrecs, i;
|
||||
|
||||
offset = 0x00e;
|
||||
ptr = np->data + offset;
|
||||
nrecs = 0;
|
||||
|
||||
for (i = 0; i < np->nd.ndNRecs; ++i)
|
||||
{
|
||||
const byte *rec;
|
||||
int reclen;
|
||||
|
||||
rec = HFS_NODEREC(*np, i);
|
||||
reclen = HFS_RECLEN(*np, i);
|
||||
|
||||
if (HFS_RECKEYLEN(rec) > 0)
|
||||
{
|
||||
np->roff[nrecs++] = offset;
|
||||
offset += reclen;
|
||||
|
||||
if (ptr == rec)
|
||||
ptr += reclen;
|
||||
else
|
||||
{
|
||||
while (reclen--)
|
||||
*ptr++ = *rec++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
np->roff[nrecs] = offset;
|
||||
np->nd.ndNRecs = nrecs;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: node->search()
|
||||
* DESCRIPTION: locate a record in a node, or the record it should follow
|
||||
*/
|
||||
int n_search(node *np, const byte *pkey)
|
||||
{
|
||||
const btree *bt = np->bt;
|
||||
byte key1[HFS_MAX_KEYLEN], key2[HFS_MAX_KEYLEN];
|
||||
int i, comp = -1;
|
||||
|
||||
bt->keyunpack(pkey, key2);
|
||||
|
||||
for (i = np->nd.ndNRecs; i--; )
|
||||
{
|
||||
const byte *rec;
|
||||
|
||||
rec = HFS_NODEREC(*np, i);
|
||||
|
||||
if (HFS_RECKEYLEN(rec) == 0)
|
||||
continue; /* deleted record */
|
||||
|
||||
bt->keyunpack(rec, key1);
|
||||
comp = bt->keycompare(key1, key2);
|
||||
|
||||
if (comp <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
np->rnum = i;
|
||||
|
||||
return comp == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: node->index()
|
||||
* DESCRIPTION: create an index record from a key and node pointer
|
||||
*/
|
||||
void n_index(const node *np, byte *record, unsigned int *reclen)
|
||||
{
|
||||
const byte *key = HFS_NODEREC(*np, 0);
|
||||
|
||||
if (np->bt == &np->bt->f.vol->cat)
|
||||
{
|
||||
/* force the key length to be 0x25 */
|
||||
|
||||
HFS_SETKEYLEN(record, 0x25);
|
||||
memset(record + 1, 0, 0x25);
|
||||
memcpy(record + 1, key + 1, HFS_RECKEYLEN(key));
|
||||
}
|
||||
else
|
||||
memcpy(record, key, HFS_RECKEYSKIP(key));
|
||||
|
||||
d_putul(HFS_RECDATA(record), np->nnum);
|
||||
|
||||
if (reclen)
|
||||
*reclen = HFS_RECKEYSKIP(record) + 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: split()
|
||||
* DESCRIPTION: divide a node into two and insert a record
|
||||
*/
|
||||
static
|
||||
int split(node *left, byte *record, unsigned int *reclen)
|
||||
{
|
||||
btree *bt = left->bt;
|
||||
node n, *right = &n, *side = 0;
|
||||
int mark, i;
|
||||
|
||||
/* create a second node by cloning the first */
|
||||
|
||||
*right = *left;
|
||||
|
||||
if (n_new(right) == -1)
|
||||
goto fail;
|
||||
|
||||
left->nd.ndFLink = right->nnum;
|
||||
right->nd.ndBLink = left->nnum;
|
||||
|
||||
/* divide all records evenly between the two nodes */
|
||||
|
||||
mark = (NODEUSED(*left) + 2 * left->nd.ndNRecs + *reclen + 2) >> 1;
|
||||
|
||||
if (left->rnum == -1)
|
||||
{
|
||||
side = left;
|
||||
mark -= *reclen + 2;
|
||||
}
|
||||
|
||||
for (i = 0; i < left->nd.ndNRecs; ++i)
|
||||
{
|
||||
node *np;
|
||||
byte *rec;
|
||||
|
||||
np = (mark > 0) ? right : left;
|
||||
rec = HFS_NODEREC(*np, i);
|
||||
|
||||
mark -= HFS_RECLEN(*np, i) + 2;
|
||||
|
||||
HFS_SETKEYLEN(rec, 0);
|
||||
|
||||
if (left->rnum == i)
|
||||
{
|
||||
side = (mark > 0) ? left : right;
|
||||
mark -= *reclen + 2;
|
||||
}
|
||||
}
|
||||
|
||||
compact(left);
|
||||
compact(right);
|
||||
|
||||
/* insert the new record and store the modified nodes */
|
||||
|
||||
ASSERT(side);
|
||||
|
||||
n_search(side, record);
|
||||
n_insertx(side, record, *reclen);
|
||||
|
||||
if (bt_putnode(left) == -1 ||
|
||||
bt_putnode(right) == -1)
|
||||
goto fail;
|
||||
|
||||
/* create an index record in the parent for the new node */
|
||||
|
||||
n_index(right, record, reclen);
|
||||
|
||||
/* update link pointers */
|
||||
|
||||
if (bt->hdr.bthLNode == left->nnum)
|
||||
{
|
||||
bt->hdr.bthLNode = right->nnum;
|
||||
bt->flags |= HFS_BT_UPDATE_HDR;
|
||||
}
|
||||
|
||||
if (right->nd.ndFLink > 0)
|
||||
{
|
||||
node sib;
|
||||
|
||||
if (bt_getnode(&sib, right->bt, right->nd.ndFLink) == -1)
|
||||
goto fail;
|
||||
|
||||
sib.nd.ndBLink = right->nnum;
|
||||
|
||||
if (bt_putnode(&sib) == -1)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: node->insertx()
|
||||
* DESCRIPTION: insert a record into a node (which must already have room)
|
||||
*/
|
||||
void n_insertx(node *np, const byte *record, unsigned int reclen)
|
||||
{
|
||||
int rnum, i;
|
||||
byte *ptr;
|
||||
|
||||
rnum = np->rnum + 1;
|
||||
|
||||
/* push other records down to make room */
|
||||
|
||||
for (ptr = HFS_NODEREC(*np, np->nd.ndNRecs) + reclen;
|
||||
ptr > HFS_NODEREC(*np, rnum) + reclen; --ptr)
|
||||
*(ptr - 1) = *(ptr - 1 - reclen);
|
||||
|
||||
++np->nd.ndNRecs;
|
||||
|
||||
for (i = np->nd.ndNRecs; i > rnum; --i)
|
||||
np->roff[i] = np->roff[i - 1] + reclen;
|
||||
|
||||
/* write the new record */
|
||||
|
||||
memcpy(HFS_NODEREC(*np, rnum), record, reclen);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: node->insert()
|
||||
* DESCRIPTION: insert a new record into a node; return a record for parent
|
||||
*/
|
||||
int n_insert(node *np, byte *record, unsigned int *reclen)
|
||||
{
|
||||
/* check for free space */
|
||||
|
||||
if (np->nd.ndNRecs >= HFS_MAX_NRECS ||
|
||||
*reclen + 2 > NODEFREE(*np))
|
||||
return split(np, record, reclen);
|
||||
|
||||
n_insertx(np, record, *reclen);
|
||||
*reclen = 0;
|
||||
|
||||
return bt_putnode(np);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: join()
|
||||
* DESCRIPTION: combine two nodes into a single node
|
||||
*/
|
||||
static
|
||||
int join(node *left, node *right, byte *record, int *flag)
|
||||
{
|
||||
int i, offset;
|
||||
|
||||
/* copy records and offsets */
|
||||
|
||||
memcpy(HFS_NODEREC(*left, left->nd.ndNRecs),
|
||||
HFS_NODEREC(*right, 0), NODEUSED(*right));
|
||||
|
||||
offset = left->roff[left->nd.ndNRecs] - right->roff[0];
|
||||
|
||||
for (i = 1; i <= right->nd.ndNRecs; ++i)
|
||||
left->roff[++left->nd.ndNRecs] = offset + right->roff[i];
|
||||
|
||||
if (bt_putnode(left) == -1)
|
||||
goto fail;
|
||||
|
||||
/* eliminate node and update link pointers */
|
||||
|
||||
if (n_free(right) == -1)
|
||||
goto fail;
|
||||
|
||||
HFS_SETKEYLEN(record, 0);
|
||||
*flag = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: node->delete()
|
||||
* DESCRIPTION: remove a record from a node
|
||||
*/
|
||||
int n_delete(node *np, byte *record, int *flag)
|
||||
{
|
||||
byte *rec;
|
||||
|
||||
rec = HFS_NODEREC(*np, np->rnum);
|
||||
|
||||
HFS_SETKEYLEN(rec, 0);
|
||||
compact(np);
|
||||
|
||||
if (np->nd.ndNRecs == 0)
|
||||
{
|
||||
if (n_free(np) == -1)
|
||||
goto fail;
|
||||
|
||||
HFS_SETKEYLEN(record, 0);
|
||||
*flag = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* see if we can join with our left sibling */
|
||||
|
||||
if (np->nd.ndBLink > 0)
|
||||
{
|
||||
node left;
|
||||
|
||||
if (bt_getnode(&left, np->bt, np->nd.ndBLink) == -1)
|
||||
goto fail;
|
||||
|
||||
if (np->nd.ndNRecs + left.nd.ndNRecs <= HFS_MAX_NRECS &&
|
||||
NODEUSED(*np) + 2 * np->nd.ndNRecs <= NODEFREE(left))
|
||||
return join(&left, np, record, flag);
|
||||
}
|
||||
|
||||
if (np->rnum == 0)
|
||||
{
|
||||
/* special case: first record changed; update parent record key */
|
||||
|
||||
n_index(np, record, 0);
|
||||
*flag = 1;
|
||||
}
|
||||
|
||||
return bt_putnode(np);
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: node.h,v 1.7 1998/11/02 22:09:06 rob Exp $
|
||||
*/
|
||||
|
||||
void n_init(node *, btree *, int, int);
|
||||
|
||||
int n_new(node *);
|
||||
int n_free(node *);
|
||||
|
||||
int n_search(node *, const byte *);
|
||||
|
||||
void n_index(const node *, byte *, unsigned int *);
|
||||
|
||||
void n_insertx(node *, const byte *, unsigned int);
|
||||
int n_insert(node *, byte *, unsigned int *);
|
||||
|
||||
int n_delete(node *, byte *, int *);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: os.h,v 1.6 1998/09/15 19:21:05 rob Exp $
|
||||
*/
|
||||
|
||||
int os_open(void **, const char *, int);
|
||||
int os_close(void **);
|
||||
|
||||
int os_same(void **, const char *);
|
||||
|
||||
unsigned long os_seek(void **priv, long base, unsigned long offset);
|
||||
unsigned long os_read(void **, void *, unsigned long);
|
||||
unsigned long os_write(void **, const void *, unsigned long);
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: unix.c,v 1.8 1998/11/02 22:09:13 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_FCNTL_H
|
||||
# include <fcntl.h>
|
||||
# else
|
||||
int open(const char *, int, ...);
|
||||
int fcntl(int, int, ...);
|
||||
# endif
|
||||
|
||||
# ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
# else
|
||||
int close(int);
|
||||
off_t lseek(int, off_t, int);
|
||||
ssize_t read(int, void *, size_t);
|
||||
ssize_t write(int, const char *, size_t);
|
||||
int stat(const char *, struct stat *);
|
||||
int fstat(int, struct stat *);
|
||||
# endif
|
||||
|
||||
# include <errno.h>
|
||||
# include <sys/stat.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "os.h"
|
||||
|
||||
/*
|
||||
* NAME: os->open()
|
||||
* DESCRIPTION: open and lock a new descriptor from the given path and mode
|
||||
*/
|
||||
int os_open(void **priv, const char *path, int mode)
|
||||
{
|
||||
int fd;
|
||||
struct flock lock;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case HFS_MODE_RDONLY:
|
||||
mode = O_RDONLY;
|
||||
break;
|
||||
|
||||
case HFS_MODE_RDWR:
|
||||
default:
|
||||
mode = O_RDWR;
|
||||
break;
|
||||
}
|
||||
|
||||
fd = open(path, mode);
|
||||
if (fd == -1)
|
||||
ERROR(errno, "error opening medium");
|
||||
|
||||
/* lock descriptor against concurrent access */
|
||||
|
||||
lock.l_type = (mode == O_RDONLY) ? F_RDLCK : F_WRLCK;
|
||||
lock.l_start = 0;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_len = 0;
|
||||
|
||||
if (fcntl(fd, F_SETLK, &lock) == -1 &&
|
||||
(errno == EACCES || errno == EAGAIN))
|
||||
ERROR(EAGAIN, "unable to obtain lock for medium");
|
||||
|
||||
*priv = (void *) fd;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
if (fd != -1)
|
||||
close(fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: os->close()
|
||||
* DESCRIPTION: close an open descriptor
|
||||
*/
|
||||
int os_close(void **priv)
|
||||
{
|
||||
int fd = (int) *priv;
|
||||
|
||||
*priv = (void *) -1;
|
||||
|
||||
if (close(fd) == -1)
|
||||
ERROR(errno, "error closing medium");
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: os->same()
|
||||
* DESCRIPTION: return 1 if path is same as the open descriptor
|
||||
*/
|
||||
int os_same(void **priv, const char *path)
|
||||
{
|
||||
int fd = (int) *priv;
|
||||
struct stat fdev, dev;
|
||||
|
||||
if (fstat(fd, &fdev) == -1 ||
|
||||
stat(path, &dev) == -1)
|
||||
ERROR(errno, "can't get path information");
|
||||
|
||||
return fdev.st_dev == dev.st_dev &&
|
||||
fdev.st_ino == dev.st_ino;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: os->seek()
|
||||
* DESCRIPTION: set a descriptor's seek pointer (offset in blocks)
|
||||
*/
|
||||
unsigned long os_seek(void **priv, long base, unsigned long offset)
|
||||
{
|
||||
int fd = (int) *priv;
|
||||
off_t result;
|
||||
|
||||
/* offset == -1 special; seek to last block of device */
|
||||
|
||||
if (offset == (unsigned long) -1)
|
||||
result = lseek(fd, 0, SEEK_END);
|
||||
else
|
||||
result = lseek(fd, (offset << HFS_BLOCKSZ_BITS) + base, SEEK_SET);
|
||||
|
||||
if (result == -1)
|
||||
ERROR(errno, "error seeking medium");
|
||||
|
||||
result -= base;
|
||||
return (unsigned long) (result >> HFS_BLOCKSZ_BITS);
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: os->read()
|
||||
* DESCRIPTION: read blocks from an open descriptor
|
||||
*/
|
||||
unsigned long os_read(void **priv, void *buf, unsigned long len)
|
||||
{
|
||||
int fd = (int) *priv;
|
||||
ssize_t result;
|
||||
|
||||
result = read(fd, buf, len << HFS_BLOCKSZ_BITS);
|
||||
|
||||
if (result == -1)
|
||||
ERROR(errno, "error reading from medium");
|
||||
|
||||
return (unsigned long) result >> HFS_BLOCKSZ_BITS;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: os->write()
|
||||
* DESCRIPTION: write blocks to an open descriptor
|
||||
*/
|
||||
unsigned long os_write(void **priv, const void *buf, unsigned long len)
|
||||
{
|
||||
int fd = (int) *priv;
|
||||
ssize_t result;
|
||||
|
||||
result = write(fd, buf, len << HFS_BLOCKSZ_BITS);
|
||||
|
||||
if (result == -1)
|
||||
ERROR(errno, "error writing to medium");
|
||||
|
||||
return (unsigned long) result >> HFS_BLOCKSZ_BITS;
|
||||
|
||||
fail:
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: record.c,v 1.9 1998/11/02 22:09:07 rob Exp $
|
||||
*/
|
||||
|
||||
# ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
# endif
|
||||
|
||||
# include <string.h>
|
||||
|
||||
# include "libhfs.h"
|
||||
# include "record.h"
|
||||
# include "data.h"
|
||||
|
||||
/*
|
||||
* NAME: record->packcatkey()
|
||||
* DESCRIPTION: pack a catalog record key
|
||||
*/
|
||||
void r_packcatkey(const CatKeyRec *key, byte *pkey, unsigned int *len)
|
||||
{
|
||||
const byte *start = pkey;
|
||||
|
||||
d_storesb(&pkey, key->ckrKeyLen);
|
||||
d_storesb(&pkey, key->ckrResrv1);
|
||||
d_storeul(&pkey, key->ckrParID);
|
||||
|
||||
d_storestr(&pkey, key->ckrCName, sizeof(key->ckrCName));
|
||||
|
||||
if (len)
|
||||
*len = HFS_RECKEYSKIP(start);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->unpackcatkey()
|
||||
* DESCRIPTION: unpack a catalog record key
|
||||
*/
|
||||
void r_unpackcatkey(const byte *pkey, CatKeyRec *key)
|
||||
{
|
||||
d_fetchsb(&pkey, &key->ckrKeyLen);
|
||||
d_fetchsb(&pkey, &key->ckrResrv1);
|
||||
d_fetchul(&pkey, &key->ckrParID);
|
||||
|
||||
d_fetchstr(&pkey, key->ckrCName, sizeof(key->ckrCName));
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->packextkey()
|
||||
* DESCRIPTION: pack an extents record key
|
||||
*/
|
||||
void r_packextkey(const ExtKeyRec *key, byte *pkey, unsigned int *len)
|
||||
{
|
||||
const byte *start = pkey;
|
||||
|
||||
d_storesb(&pkey, key->xkrKeyLen);
|
||||
d_storesb(&pkey, key->xkrFkType);
|
||||
d_storeul(&pkey, key->xkrFNum);
|
||||
d_storeuw(&pkey, key->xkrFABN);
|
||||
|
||||
if (len)
|
||||
*len = HFS_RECKEYSKIP(start);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->unpackextkey()
|
||||
* DESCRIPTION: unpack an extents record key
|
||||
*/
|
||||
void r_unpackextkey(const byte *pkey, ExtKeyRec *key)
|
||||
{
|
||||
d_fetchsb(&pkey, &key->xkrKeyLen);
|
||||
d_fetchsb(&pkey, &key->xkrFkType);
|
||||
d_fetchul(&pkey, &key->xkrFNum);
|
||||
d_fetchuw(&pkey, &key->xkrFABN);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->comparecatkeys()
|
||||
* DESCRIPTION: compare two (packed) catalog record keys
|
||||
*/
|
||||
int r_comparecatkeys(const CatKeyRec *key1, const CatKeyRec *key2)
|
||||
{
|
||||
int diff;
|
||||
|
||||
diff = key1->ckrParID - key2->ckrParID;
|
||||
if (diff)
|
||||
return diff;
|
||||
|
||||
return d_relstring(key1->ckrCName, key2->ckrCName);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->compareextkeys()
|
||||
* DESCRIPTION: compare two (packed) extents record keys
|
||||
*/
|
||||
int r_compareextkeys(const ExtKeyRec *key1, const ExtKeyRec *key2)
|
||||
{
|
||||
int diff;
|
||||
|
||||
diff = key1->xkrFNum - key2->xkrFNum;
|
||||
if (diff)
|
||||
return diff;
|
||||
|
||||
diff = (unsigned char) key1->xkrFkType -
|
||||
(unsigned char) key2->xkrFkType;
|
||||
if (diff)
|
||||
return diff;
|
||||
|
||||
return key1->xkrFABN - key2->xkrFABN;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->packcatdata()
|
||||
* DESCRIPTION: pack catalog record data
|
||||
*/
|
||||
void r_packcatdata(const CatDataRec *data, byte *pdata, unsigned int *len)
|
||||
{
|
||||
const byte *start = pdata;
|
||||
int i;
|
||||
|
||||
d_storesb(&pdata, data->cdrType);
|
||||
d_storesb(&pdata, data->cdrResrv2);
|
||||
|
||||
switch (data->cdrType)
|
||||
{
|
||||
case cdrDirRec:
|
||||
d_storesw(&pdata, data->u.dir.dirFlags);
|
||||
d_storeuw(&pdata, data->u.dir.dirVal);
|
||||
d_storeul(&pdata, data->u.dir.dirDirID);
|
||||
d_storesl(&pdata, data->u.dir.dirCrDat);
|
||||
d_storesl(&pdata, data->u.dir.dirMdDat);
|
||||
d_storesl(&pdata, data->u.dir.dirBkDat);
|
||||
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.top);
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.left);
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.bottom);
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.right);
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frFlags);
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.v);
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.h);
|
||||
d_storesw(&pdata, data->u.dir.dirUsrInfo.frView);
|
||||
|
||||
d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.v);
|
||||
d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.h);
|
||||
d_storesl(&pdata, data->u.dir.dirFndrInfo.frOpenChain);
|
||||
d_storesw(&pdata, data->u.dir.dirFndrInfo.frUnused);
|
||||
d_storesw(&pdata, data->u.dir.dirFndrInfo.frComment);
|
||||
d_storesl(&pdata, data->u.dir.dirFndrInfo.frPutAway);
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
d_storesl(&pdata, data->u.dir.dirResrv[i]);
|
||||
|
||||
break;
|
||||
|
||||
case cdrFilRec:
|
||||
d_storesb(&pdata, data->u.fil.filFlags);
|
||||
d_storesb(&pdata, data->u.fil.filTyp);
|
||||
|
||||
d_storesl(&pdata, data->u.fil.filUsrWds.fdType);
|
||||
d_storesl(&pdata, data->u.fil.filUsrWds.fdCreator);
|
||||
d_storesw(&pdata, data->u.fil.filUsrWds.fdFlags);
|
||||
d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.v);
|
||||
d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.h);
|
||||
d_storesw(&pdata, data->u.fil.filUsrWds.fdFldr);
|
||||
|
||||
d_storeul(&pdata, data->u.fil.filFlNum);
|
||||
|
||||
d_storeuw(&pdata, data->u.fil.filStBlk);
|
||||
d_storeul(&pdata, data->u.fil.filLgLen);
|
||||
d_storeul(&pdata, data->u.fil.filPyLen);
|
||||
|
||||
d_storeuw(&pdata, data->u.fil.filRStBlk);
|
||||
d_storeul(&pdata, data->u.fil.filRLgLen);
|
||||
d_storeul(&pdata, data->u.fil.filRPyLen);
|
||||
|
||||
d_storesl(&pdata, data->u.fil.filCrDat);
|
||||
d_storesl(&pdata, data->u.fil.filMdDat);
|
||||
d_storesl(&pdata, data->u.fil.filBkDat);
|
||||
|
||||
d_storesw(&pdata, data->u.fil.filFndrInfo.fdIconID);
|
||||
for (i = 0; i < 4; ++i)
|
||||
d_storesw(&pdata, data->u.fil.filFndrInfo.fdUnused[i]);
|
||||
d_storesw(&pdata, data->u.fil.filFndrInfo.fdComment);
|
||||
d_storesl(&pdata, data->u.fil.filFndrInfo.fdPutAway);
|
||||
|
||||
d_storeuw(&pdata, data->u.fil.filClpSize);
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrStABN);
|
||||
d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrStABN);
|
||||
d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
d_storesl(&pdata, data->u.fil.filResrv);
|
||||
|
||||
break;
|
||||
|
||||
case cdrThdRec:
|
||||
for (i = 0; i < 2; ++i)
|
||||
d_storesl(&pdata, data->u.dthd.thdResrv[i]);
|
||||
|
||||
d_storeul(&pdata, data->u.dthd.thdParID);
|
||||
|
||||
d_storestr(&pdata, data->u.dthd.thdCName,
|
||||
sizeof(data->u.dthd.thdCName));
|
||||
|
||||
break;
|
||||
|
||||
case cdrFThdRec:
|
||||
for (i = 0; i < 2; ++i)
|
||||
d_storesl(&pdata, data->u.fthd.fthdResrv[i]);
|
||||
|
||||
d_storeul(&pdata, data->u.fthd.fthdParID);
|
||||
|
||||
d_storestr(&pdata, data->u.fthd.fthdCName,
|
||||
sizeof(data->u.fthd.fthdCName));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
|
||||
if (len)
|
||||
*len += pdata - start;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->unpackcatdata()
|
||||
* DESCRIPTION: unpack catalog record data
|
||||
*/
|
||||
void r_unpackcatdata(const byte *pdata, CatDataRec *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
d_fetchsb(&pdata, &data->cdrType);
|
||||
d_fetchsb(&pdata, &data->cdrResrv2);
|
||||
|
||||
switch (data->cdrType)
|
||||
{
|
||||
case cdrDirRec:
|
||||
d_fetchsw(&pdata, &data->u.dir.dirFlags);
|
||||
d_fetchuw(&pdata, &data->u.dir.dirVal);
|
||||
d_fetchul(&pdata, &data->u.dir.dirDirID);
|
||||
d_fetchsl(&pdata, &data->u.dir.dirCrDat);
|
||||
d_fetchsl(&pdata, &data->u.dir.dirMdDat);
|
||||
d_fetchsl(&pdata, &data->u.dir.dirBkDat);
|
||||
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.top);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.left);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.bottom);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.right);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frFlags);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.v);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.h);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frView);
|
||||
|
||||
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.v);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.h);
|
||||
d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frOpenChain);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frUnused);
|
||||
d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frComment);
|
||||
d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frPutAway);
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
d_fetchsl(&pdata, &data->u.dir.dirResrv[i]);
|
||||
|
||||
break;
|
||||
|
||||
case cdrFilRec:
|
||||
d_fetchsb(&pdata, &data->u.fil.filFlags);
|
||||
d_fetchsb(&pdata, &data->u.fil.filTyp);
|
||||
|
||||
d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdType);
|
||||
d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdCreator);
|
||||
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFlags);
|
||||
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.v);
|
||||
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.h);
|
||||
d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFldr);
|
||||
|
||||
d_fetchul(&pdata, &data->u.fil.filFlNum);
|
||||
|
||||
d_fetchuw(&pdata, &data->u.fil.filStBlk);
|
||||
d_fetchul(&pdata, &data->u.fil.filLgLen);
|
||||
d_fetchul(&pdata, &data->u.fil.filPyLen);
|
||||
|
||||
d_fetchuw(&pdata, &data->u.fil.filRStBlk);
|
||||
d_fetchul(&pdata, &data->u.fil.filRLgLen);
|
||||
d_fetchul(&pdata, &data->u.fil.filRPyLen);
|
||||
|
||||
d_fetchsl(&pdata, &data->u.fil.filCrDat);
|
||||
d_fetchsl(&pdata, &data->u.fil.filMdDat);
|
||||
d_fetchsl(&pdata, &data->u.fil.filBkDat);
|
||||
|
||||
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdIconID);
|
||||
for (i = 0; i < 4; ++i)
|
||||
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdUnused[i]);
|
||||
d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdComment);
|
||||
d_fetchsl(&pdata, &data->u.fil.filFndrInfo.fdPutAway);
|
||||
|
||||
d_fetchuw(&pdata, &data->u.fil.filClpSize);
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrStABN);
|
||||
d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrStABN);
|
||||
d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
d_fetchsl(&pdata, &data->u.fil.filResrv);
|
||||
|
||||
break;
|
||||
|
||||
case cdrThdRec:
|
||||
for (i = 0; i < 2; ++i)
|
||||
d_fetchsl(&pdata, &data->u.dthd.thdResrv[i]);
|
||||
|
||||
d_fetchul(&pdata, &data->u.dthd.thdParID);
|
||||
|
||||
d_fetchstr(&pdata, data->u.dthd.thdCName,
|
||||
sizeof(data->u.dthd.thdCName));
|
||||
|
||||
break;
|
||||
|
||||
case cdrFThdRec:
|
||||
for (i = 0; i < 2; ++i)
|
||||
d_fetchsl(&pdata, &data->u.fthd.fthdResrv[i]);
|
||||
|
||||
d_fetchul(&pdata, &data->u.fthd.fthdParID);
|
||||
|
||||
d_fetchstr(&pdata, data->u.fthd.fthdCName,
|
||||
sizeof(data->u.fthd.fthdCName));
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->packextdata()
|
||||
* DESCRIPTION: pack extent record data
|
||||
*/
|
||||
void r_packextdata(const ExtDataRec *data, byte *pdata, unsigned int *len)
|
||||
{
|
||||
const byte *start = pdata;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_storeuw(&pdata, (*data)[i].xdrStABN);
|
||||
d_storeuw(&pdata, (*data)[i].xdrNumABlks);
|
||||
}
|
||||
|
||||
if (len)
|
||||
*len += pdata - start;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->unpackextdata()
|
||||
* DESCRIPTION: unpack extent record data
|
||||
*/
|
||||
void r_unpackextdata(const byte *pdata, ExtDataRec *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
{
|
||||
d_fetchuw(&pdata, &(*data)[i].xdrStABN);
|
||||
d_fetchuw(&pdata, &(*data)[i].xdrNumABlks);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->makecatkey()
|
||||
* DESCRIPTION: construct a catalog record key
|
||||
*/
|
||||
void r_makecatkey(CatKeyRec *key, unsigned long parid, const char *name)
|
||||
{
|
||||
int len;
|
||||
|
||||
len = strlen(name) + 1;
|
||||
|
||||
key->ckrKeyLen = 0x05 + len + (len & 1);
|
||||
key->ckrResrv1 = 0;
|
||||
key->ckrParID = parid;
|
||||
|
||||
strcpy(key->ckrCName, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->makeextkey()
|
||||
* DESCRIPTION: construct an extents record key
|
||||
*/
|
||||
void r_makeextkey(ExtKeyRec *key,
|
||||
int fork, unsigned long fnum, unsigned int fabn)
|
||||
{
|
||||
key->xkrKeyLen = 0x07;
|
||||
key->xkrFkType = fork;
|
||||
key->xkrFNum = fnum;
|
||||
key->xkrFABN = fabn;
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->packcatrec()
|
||||
* DESCRIPTION: create a packed catalog record
|
||||
*/
|
||||
void r_packcatrec(const CatKeyRec *key, const CatDataRec *data,
|
||||
byte *precord, unsigned int *len)
|
||||
{
|
||||
r_packcatkey(key, precord, len);
|
||||
r_packcatdata(data, HFS_RECDATA(precord), len);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->packextrec()
|
||||
* DESCRIPTION: create a packed extents record
|
||||
*/
|
||||
void r_packextrec(const ExtKeyRec *key, const ExtDataRec *data,
|
||||
byte *precord, unsigned int *len)
|
||||
{
|
||||
r_packextkey(key, precord, len);
|
||||
r_packextdata(data, HFS_RECDATA(precord), len);
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->packdirent()
|
||||
* DESCRIPTION: make changes to a catalog record
|
||||
*/
|
||||
void r_packdirent(CatDataRec *data, const hfsdirent *ent)
|
||||
{
|
||||
switch (data->cdrType)
|
||||
{
|
||||
case cdrDirRec:
|
||||
data->u.dir.dirCrDat = d_mtime(ent->crdate);
|
||||
data->u.dir.dirMdDat = d_mtime(ent->mddate);
|
||||
data->u.dir.dirBkDat = d_mtime(ent->bkdate);
|
||||
|
||||
data->u.dir.dirUsrInfo.frFlags = ent->fdflags;
|
||||
data->u.dir.dirFndrInfo.frComment = ent->fdcomment;
|
||||
data->u.dir.dirUsrInfo.frLocation.v = ent->fdlocation.v;
|
||||
data->u.dir.dirUsrInfo.frLocation.h = ent->fdlocation.h;
|
||||
|
||||
data->u.dir.dirUsrInfo.frRect.top = ent->u.dir.rect.top;
|
||||
data->u.dir.dirUsrInfo.frRect.left = ent->u.dir.rect.left;
|
||||
data->u.dir.dirUsrInfo.frRect.bottom = ent->u.dir.rect.bottom;
|
||||
data->u.dir.dirUsrInfo.frRect.right = ent->u.dir.rect.right;
|
||||
|
||||
break;
|
||||
|
||||
case cdrFilRec:
|
||||
if (ent->flags & HFS_ISLOCKED)
|
||||
data->u.fil.filFlags |= (1 << 0);
|
||||
else
|
||||
data->u.fil.filFlags &= ~(1 << 0);
|
||||
|
||||
data->u.fil.filCrDat = d_mtime(ent->crdate);
|
||||
data->u.fil.filMdDat = d_mtime(ent->mddate);
|
||||
data->u.fil.filBkDat = d_mtime(ent->bkdate);
|
||||
|
||||
data->u.fil.filUsrWds.fdFlags = ent->fdflags;
|
||||
data->u.fil.filFndrInfo.fdComment = ent->fdcomment;
|
||||
|
||||
data->u.fil.filUsrWds.fdLocation.v = ent->fdlocation.v;
|
||||
data->u.fil.filUsrWds.fdLocation.h = ent->fdlocation.h;
|
||||
|
||||
data->u.fil.filUsrWds.fdType =
|
||||
d_getsl((const unsigned char *) ent->u.file.type);
|
||||
data->u.fil.filUsrWds.fdCreator =
|
||||
d_getsl((const unsigned char *) ent->u.file.creator);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* NAME: record->unpackdirent()
|
||||
* DESCRIPTION: unpack catalog information into hfsdirent structure
|
||||
*/
|
||||
void r_unpackdirent(unsigned long parid, const char *name,
|
||||
const CatDataRec *data, hfsdirent *ent)
|
||||
{
|
||||
strcpy(ent->name, name);
|
||||
ent->parid = parid;
|
||||
|
||||
switch (data->cdrType)
|
||||
{
|
||||
case cdrDirRec:
|
||||
ent->flags = HFS_ISDIR;
|
||||
ent->cnid = data->u.dir.dirDirID;
|
||||
|
||||
ent->crdate = d_ltime(data->u.dir.dirCrDat);
|
||||
ent->mddate = d_ltime(data->u.dir.dirMdDat);
|
||||
ent->bkdate = d_ltime(data->u.dir.dirBkDat);
|
||||
|
||||
ent->fdflags = data->u.dir.dirUsrInfo.frFlags;
|
||||
ent->fdcomment = data->u.dir.dirFndrInfo.frComment;
|
||||
ent->fdlocation.v = data->u.dir.dirUsrInfo.frLocation.v;
|
||||
ent->fdlocation.h = data->u.dir.dirUsrInfo.frLocation.h;
|
||||
|
||||
ent->u.dir.valence = data->u.dir.dirVal;
|
||||
|
||||
ent->u.dir.rect.top = data->u.dir.dirUsrInfo.frRect.top;
|
||||
ent->u.dir.rect.left = data->u.dir.dirUsrInfo.frRect.left;
|
||||
ent->u.dir.rect.bottom = data->u.dir.dirUsrInfo.frRect.bottom;
|
||||
ent->u.dir.rect.right = data->u.dir.dirUsrInfo.frRect.right;
|
||||
|
||||
break;
|
||||
|
||||
case cdrFilRec:
|
||||
ent->flags = (data->u.fil.filFlags & (1 << 0)) ? HFS_ISLOCKED : 0;
|
||||
ent->cnid = data->u.fil.filFlNum;
|
||||
|
||||
ent->crdate = d_ltime(data->u.fil.filCrDat);
|
||||
ent->mddate = d_ltime(data->u.fil.filMdDat);
|
||||
ent->bkdate = d_ltime(data->u.fil.filBkDat);
|
||||
|
||||
ent->fdflags = data->u.fil.filUsrWds.fdFlags;
|
||||
ent->fdcomment = data->u.fil.filFndrInfo.fdComment;
|
||||
ent->fdlocation.v = data->u.fil.filUsrWds.fdLocation.v;
|
||||
ent->fdlocation.h = data->u.fil.filUsrWds.fdLocation.h;
|
||||
|
||||
ent->u.file.dsize = data->u.fil.filLgLen;
|
||||
ent->u.file.rsize = data->u.fil.filRLgLen;
|
||||
|
||||
d_putsl((unsigned char *) ent->u.file.type,
|
||||
data->u.fil.filUsrWds.fdType);
|
||||
d_putsl((unsigned char *) ent->u.file.creator,
|
||||
data->u.fil.filUsrWds.fdCreator);
|
||||
|
||||
ent->u.file.type[4] = ent->u.file.creator[4] = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: record.h,v 1.7 1998/11/02 22:09:08 rob Exp $
|
||||
*/
|
||||
|
||||
void r_packcatkey(const CatKeyRec *, byte *, unsigned int *);
|
||||
void r_unpackcatkey(const byte *, CatKeyRec *);
|
||||
|
||||
void r_packextkey(const ExtKeyRec *, byte *, unsigned int *);
|
||||
void r_unpackextkey(const byte *, ExtKeyRec *);
|
||||
|
||||
int r_comparecatkeys(const CatKeyRec *, const CatKeyRec *);
|
||||
int r_compareextkeys(const ExtKeyRec *, const ExtKeyRec *);
|
||||
|
||||
void r_packcatdata(const CatDataRec *, byte *, unsigned int *);
|
||||
void r_unpackcatdata(const byte *, CatDataRec *);
|
||||
|
||||
void r_packextdata(const ExtDataRec *, byte *, unsigned int *);
|
||||
void r_unpackextdata(const byte *, ExtDataRec *);
|
||||
|
||||
void r_makecatkey(CatKeyRec *, unsigned long, const char *);
|
||||
void r_makeextkey(ExtKeyRec *, int, unsigned long, unsigned int);
|
||||
|
||||
void r_packcatrec(const CatKeyRec *, const CatDataRec *,
|
||||
byte *, unsigned int *);
|
||||
void r_packextrec(const ExtKeyRec *, const ExtDataRec *,
|
||||
byte *, unsigned int *);
|
||||
|
||||
void r_packdirent(CatDataRec *, const hfsdirent *);
|
||||
void r_unpackdirent(unsigned long, const char *,
|
||||
const CatDataRec *, hfsdirent *);
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $
|
||||
*/
|
||||
|
||||
# include "version.h"
|
||||
|
||||
const char libhfs_rcsid[] =
|
||||
"$Id: version.c,v 1.11 1998/11/02 22:09:09 rob Exp $";
|
||||
|
||||
const char libhfs_version[] = "libhfs version 3.2.6";
|
||||
const char libhfs_copyright[] = "Copyright (C) 1996-1998 Robert Leslie";
|
||||
const char libhfs_author[] = "Robert Leslie <rob@mars.org>";
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: version.h,v 1.6 1998/09/18 22:56:38 rob Exp $
|
||||
*/
|
||||
|
||||
extern const char libhfs_rcsid[];
|
||||
|
||||
extern const char libhfs_version[];
|
||||
extern const char libhfs_copyright[];
|
||||
extern const char libhfs_author[];
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* libhfs - library for reading and writing Macintosh HFS volumes
|
||||
* Copyright (C) 1996-1998 Robert Leslie
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Id: volume.h,v 1.7 1998/11/02 22:09:12 rob Exp $
|
||||
*/
|
||||
|
||||
void v_init(hfsvol *, int);
|
||||
|
||||
int v_open(hfsvol *, const char *, int);
|
||||
int v_flush(hfsvol *);
|
||||
int v_close(hfsvol *);
|
||||
|
||||
int v_same(hfsvol *, const char *);
|
||||
int v_geometry(hfsvol *, int);
|
||||
|
||||
int v_readmdb(hfsvol *);
|
||||
int v_writemdb(hfsvol *);
|
||||
|
||||
int v_readvbm(hfsvol *);
|
||||
int v_writevbm(hfsvol *);
|
||||
|
||||
int v_mount(hfsvol *);
|
||||
int v_dirty(hfsvol *);
|
||||
|
||||
int v_catsearch(hfsvol *, unsigned long, const char *,
|
||||
CatDataRec *, char *, node *);
|
||||
int v_extsearch(hfsfile *, unsigned int, ExtDataRec *, node *);
|
||||
|
||||
int v_getthread(hfsvol *, unsigned long, CatDataRec *, node *, int);
|
||||
|
||||
# define v_getdthread(vol, id, thread, np) \
|
||||
v_getthread(vol, id, thread, np, cdrThdRec)
|
||||
# define v_getfthread(vol, id, thread, np) \
|
||||
v_getthread(vol, id, thread, np, cdrFThdRec)
|
||||
|
||||
int v_putcatrec(const CatDataRec *, node *);
|
||||
int v_putextrec(const ExtDataRec *, node *);
|
||||
|
||||
int v_allocblocks(hfsvol *, ExtDescriptor *);
|
||||
int v_freeblocks(hfsvol *, const ExtDescriptor *);
|
||||
|
||||
int v_resolve(hfsvol **, const char *, CatDataRec *, long *, char *, node *);
|
||||
|
||||
int v_adjvalence(hfsvol *, unsigned long, int, int);
|
||||
int v_mkdir(hfsvol *, unsigned long, const char *);
|
||||
|
||||
int v_scavenge(hfsvol *);
|
Loading…
Reference in New Issue