这几天在整 DMG 安装包打开时添加软件协议声明(SLA, Software License Agreement),这个东西算是个比较古老的东西了。在缺乏文档和相关信息的情况下,往 DMG 文件添加 SLA 还是很折腾人的。
简单地说大致步骤如下:
- 下载 Apple 的 SLA for UDIFs 文件,提取 SLAResources 为 rsrc 格式。
- 使用 ResEdit 或者 ResKnife 对其进行编辑。
- 在 Terminal 里面用 Rez 命令把编辑好的文件集成到 DMG 文件里面。
但是这些鬼东西每一步都坑爹。
- 下载
首先 SLA_for_UDIFs_1.0.dmg 文件在 developer.apple.com 已经搜索不到,我是在 www.tribler.org 上搜到的,附件可以下载。这里附上 Dropbox 下载地址:https://dl.dropboxusercontent.com/u/6750144/SLAs_for_UDIFs_1.0.dmg
- 提取 rsrc 文件
OK,下载完这个 DMG 文件后挂载之,在终端运行命令行:
DeRez SLAResrouces > sla.r
把 DMG 里面的 SLAResources 文件解出来,得到一个 sla.r 文件。如果遇到 DeRez 命令不可用,那你估计没安装 CommandLine Tools,去 Xcode 里面下一个,或者去 Developer.apple.com 也有可以下载的。
提取完的 .r 文件可以直接用文本编辑器打开:
data 'TMPL' (128, "LPic") { $"1344 6566 6175 6C74 204C 616E 6775 6167" /* .Default Languag */ $"6520 4944 4457 5244 0543 6F75 6E74 4F43" /* e IDDWRD.CountOC */ $"4E54 042A 2A2A 2A4C 5354 430B 7379 7320" /* NT.****LSTC.sys */ $"6C61 6E67 2049 4444 5752 441E 6C6F 6361" /* lang IDDWRD.loca */ $"6C20 7265 7320 4944 2028 6F66 6673 6574" /* l res ID (offset */ $"2066 726F 6D20 3530 3030 4457 5244 1032" /* from 5000DWRD.2 */ $"2D62 7974 6520 6C61 6E67 7561 6765 3F44" /* -byte language?D */ $"5752 4404 2A2A 2A2A 4C53 5445" /* WRD.****LSTE */ }; data 'LPic' (5000) { $"0052 0002 0034 000A 0000 0000 0002 0000" /* ...........4.Â.. */ }; data 'STR#' (5000, "English buttons") { $"0006 0D45 6E67 6C69 7368 2074 6573 7431" /* ...English test1 */ $"0541 6772 6565 0844 6973 6167 7265 6505" /* .Agree.Disagree. */ $"5072 696E 7407 5361 7665 2E2E 2E7A 4966" /* Print.Save...zIf */ $"2079 6F75 2061 6772 6565 2077 6974 6820" /* you agree with */ $"7468 6520 7465 726D 7320 6F66 2074 6869" /* the terms of thi */ $"7320 6C69 6365 6E73 652C 2063 6C69 636B" /* s license, click */ $"2022 4167 7265 6522 2074 6F20 6163 6365" /* "Agree" to acce */ $"7373 2074 6865 2073 6F66 7477 6172 652E" /* ss the software. */ $"2020 4966 2079 6F75 2064 6F20 6E6F 7420" /* If you do not */ $"6167 7265 652C 2070 7265 7373 2022 4469" /* agree, press "Di */ $"7361 6772 6565 2E22" /* sagree." */ };
发现其实就是一个配置文件, DMG 文件被打开的时候, Mac OS X 会自动去读取这个配置文件,然后自动生成一个 SLA 窗口,但是这个文件全是 hex 值咱看不懂,官方给的那个 SLA_for_UDIFs 那个 dmg 里的文档又写得不明不白。里面提及可以用 ResEdit 来打开,但是,坑爹啊,这玩意 Mac 10.7 以后估计就用不了了,靠。
于是乎找另一个替代软件 ResKnife。
- 用 ResKnife 对 SLAResources 文件进行编辑
搜一下出来一堆下载链接,不要管,直接进 ResKnife 的 Github 页面:https://github.com/slobo/ResKnife
直接 clone 一个到本地,然后 XCode Run 一遍,再用它打开那个官方下载的 SLAResources 文件,大概如下:
这里面 LPic TMPL 是给 Res 编辑器用的,用来解析 LPic 这个类型里面的内容。 直接看 LPic Type, ID 5000 的这条数据。
Default Lan 是默认选中的语言,Count 是多少种语言可选,下面的就是可选的是什么语言了。这里 sys lang 是定义于 CoreService/CarbonCore/Script.h 里面的枚举值。对应的语言是多少得自己去查。
而 local res 就是这个 sla.r 文件里面一样的那些 ID, 比如简体中文是 5010,local res 就是 10。剩下那个不用管。修改这条数据我们就改了那个 SLA 窗口里面选择多语言的下拉菜单。
- 接下来是最头疼的地方,添加协议内容。
我们可以看到 English SLA 有两种 Type,一种 TEXT,一种styl,其实是两种不同的数据类型,都是用来填协议内容的文本的,但是格式不同。这时如果我们直接填进英文版本的协议,wow! It works!
但是坑爹的来了,如果在 TEXT 和 styl 里面填入中文等会出来就一定是乱码。一开始我以为是 encoding 的问题,但是换了无数种 encode 方式还是没用,坑爹的。而那个官方说明文件里面只是说 styl 数据跟 encoding 有关,但又不说明跟什么有关。 ResKnife 打开 Text 类型的数据还算能看到文本内容,打开 styl 类型的数据就全是 ... 了我擦。最后 google 了半天,总算有相关的文章讲到这个问题了。
- 利用 NSPasteboard 把文本转成 TEXT 和 styl 格式
2010 年 Dan Wood 这篇文章 http://gigliwood.com/weblog/cocoa/Converting_Rich_Tex.html 谈到怎么自动化把 SLA 集成到 DMG 文件。
到 TEXT/styl 数据这里,作者很牛叉地利用 ObjC 代码,读进文本再贴进 Pastboard,然后再写入文件,但是文章中用的把 NSData 输出的方法的那个项目已经没了,于是我找到了另一个项目,FreeDMG on Github ,里面的 rtf2r.m 文件里面有同样的方法,而且可以 dump 进文件里面。
大致上我是这样做的:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your applicationNSString *sourcePath = [[NSBundle mainBundle] pathForResource:@"sla" ofType:@"rtf"]; NSAttributedString *str = [[NSAttributedString alloc] initWithPath:sourcePath documentAttributes:nil]; NSData *data = [str RTFFromRange:NSMakeRange(0, [str length]) documentAttributes:nil]; NSPasteboard *pb = [NSPasteboard generalPasteboard]; [pb declareTypes:[NSArray arrayWithObject:NSRTFPboardType] owner:nil]; [pb setData:data forType:NSRTFPboardType]; NSData *textData = [pb dataForType:@"CorePasteboardFlavorType 0x54455854"]; // TEXT NSData *styleData = [pb dataForType:@"CorePasteboardFlavorType 0x7374796C"]; // styl int len = [styleData length]; char *bytes = malloc(len); [styleData getBytes:bytes length:len]; OSStatus status = CoreEndianFlipData ( kCoreEndianResourceManagerDomain, //OSType dataDomain, 'styl', //OSType dataType, 0, //SInt16 id, bytes, //void *data, len, //ByteCount dataLen, #ifdef __BIG_ENDIAN__ true #else false //Boolean currentlyNative #endif ); NSData *newStyleData = [[NSData alloc] initWithBytesNoCopy:bytes length:len freeWhenDone:YES]; NSString * outPath = @"/Users/justinyan/Downloads/test.txt"; dump_rsrc_file("TEXT", textData, outPath); dump_rsrc_file("styl", newStyleData, outPath); }
读进一个 RTF 文件,然后把内容转成 TEXT/styl 数据并保存起来。这段代码其实有一个很重要的点,就是
CoreEndianFlipData
这个函数,把大端小端交换了一下。如果没有交换大小端,生成的数据是无法 Rez 到 DMG 文件里面的,文章中作者也纳闷为毛 Intel CPU 的 Mac 就不行,于是他在他的旧机器 G5 (真土豪啊)上面跑了一遍发现出来的 styl 数据如下:
0060 0000 0000 000F 000C 0400 0100 000C
而这个数据是正确的,可以 Rez 进去的,分别表示 0x0060 style runs, 0x00000000 first offset, 0x000F line height, 0x000C font ascent, 0x0400 font family, 0x0100 char style, 0x000c pixel size.
但是 Intel 机器跑出来是这样:
6000 0000 0000 0f00 0C00 0004 0001 0C00
于是他猜到可能是大端小端的问题于是交换了一下(要不是有台旧 Mac,我盯着这坨 hex 一年都猜不到),解决了!
完了我再手动去把这段数据给粘贴到用 ResKnife 编辑好,生成的 .r 文件。
DeRez SLAResources.rsrc > sla.r
这样用 DeRez 命令就可以生成 .r 文件了。把对应的中文的数据贴进 .r 文件之后,再用 Rez 命令集成到 DMG 文件里面,终于大功告成~!
Rez -a sla.r -o your_file.dmg
Refrences
https://dl.dropboxusercontent.com/u/6750144/SLAs_for_UDIFs_1.0.dmg
https://github.com/slobo/ResKnife