-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmain.c
249 lines (244 loc) · 8.74 KB
/
main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/*
* main.c
*
* Created by Rainer Brockerhoff on 29/4/08.
* Copyright 2008-2010 Rainer Brockerhoff. All rights reserved.
*
* This command-line utility adds one license at a time to unflattened disk images.
* It's meant to be used inside build scripts.
*
* Usage: AddLicense /path/to/TheUnflattened.dmg Language /path/to/TheLicense.rtf
*
* The language parameter can be either short (en) or long (English) form.
* The strings for each language are inside the license.plist file.
* There must be one number and 9 strings for every language; the number is the output string
* encoding (kTextEncodingMacXYZ in TextCommon.h). The string group is actually indexed
* by the ISO short language name. All strings are converted to the encoding
* specified; usually this should be MacRoman.
*
* This should run on 10.5 and up.
*
*/
#include <Carbon/Carbon.h>
#include <mach-o/getsect.h>
// Autogenerated by Xcode before compiling
#include "license.plist.h"
// When referencing resource layouts, always make sure of 68K aligment
#pragma options align=mac68k
typedef struct LPic {
UInt16 defLang;
UInt16 count;
struct {
UInt16 lang;
UInt16 resid;
UInt16 twobyte;
} item[1];
} LPic;
typedef struct STRn {
UInt16 count;
UInt8 pstr;
} STRn;
#pragma options align=reset
int main(int argc, char *argv[]) {
int result = 0;
int i,j;
// First we get the strings dictionary...
CFDataRef licpdata = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (UInt8*)license_plist, license_plist_len, kCFAllocatorNull);
CFPropertyListRef licdict = CFPropertyListCreateWithData(kCFAllocatorDefault, licpdata, kCFPropertyListImmutable, NULL, NULL);
if (!licdict||(CFGetTypeID(licdict)!=CFDictionaryGetTypeID())) {
printf("Unreadable license dictionary\n");
return 1;
}
// If not 4 arguments (first argument is the executable path, remember), print help text.
if (argc!=4) {
printf("Add one license at a time to a (unflattened) disk image.\n\n"
"Usage: AddLicense /path/to/TheUnflattened.dmg Language /path/to/TheLicense.rtf\n"
"\n\tLanguages supported:");
j = CFDictionaryGetCount(licdict);
CFStringRef* keys = calloc(j,sizeof(CFStringRef));
CFDictionaryGetKeysAndValues(licdict, (const void**)keys, NULL);
char* buffer = malloc(256);
for (i=0;i<j;i++) {
if (CFStringGetCString(keys[i], buffer, 255, kCFStringEncodingUTF8)) {
printf(" %s",buffer);
}
}
printf("\n\t\tYou can also use long equivalents like English, French etc.\n"
"\t\tThe first language added will be the default language (usually English).\n\n"
"Here's an actual usage example from a build script:\n"
"\thdiutil unflatten \"$SOURCE_ROOT/My.dmg\"\n"
"\t\"$BUILT_PRODUCTS_DIR/AddLicense\" \"$SOURCE_ROOT/My.dmg\" English \"$SOURCE_ROOT/EnglishLicense.rtf\"\n"
"\t\"$BUILT_PRODUCTS_DIR/AddLicense\" \"$SOURCE_ROOT/My.dmg\" French \"$SOURCE_ROOT/FrenchLicense.rtf\"\n"
"\thdiutil flatten \"$SOURCE_ROOT/My.dmg\"\n\n");
return 0;
}
// Now get and check the language code.
CFStringRef langcode = CFStringCreateWithCString(kCFAllocatorDefault, argv[2], kCFStringEncodingUTF8);
if (!langcode) {
printf("Unreadable language code\n");
return 1;
}
// Make sure we have the canonical ISO language code here.
CFStringRef iso = CFLocaleCreateCanonicalLanguageIdentifierFromString(kCFAllocatorDefault, langcode);
if (!iso) {
printf("'%s' is not a language\n",argv[2]);
return 1;
}
// We also need a Pascal string representation of the ISO code. Since the buffer is zeroed,
// isoch+1 will always be a valid C string, too.
char* isoch = calloc(65, 1);
if (!CFStringGetCString(iso, isoch+1, 63, kCFStringEncodingMacRoman)) {
printf("ISO code too long or not encodeable\n"); // This should of course never happen
return 1;
}
isoch[0] = strlen(isoch+1);
// We also need the language and region codes.
LangCode lang = 0;
RegionCode region = 0;
OSStatus err = LocaleStringToLangAndRegionCodes(isoch+1, &lang, ®ion);
if (err!=noErr) {
printf("No language/region number for '%s'\n",isoch+1);
return 1;
}
// Now we get the language encoding and strings from our dictionary
CFArrayRef strings = CFDictionaryGetValue(licdict, iso);
if (!strings||(CFGetTypeID(strings)!=CFArrayGetTypeID())) {
printf("'%s (%s)' is not in the dictionary\n",argv[2],isoch+1);
return 1;
}
if (CFArrayGetCount(strings)!=10) {
printf("'%s' dictionary strings error (should be 10 items)\n",isoch+1);
return 1;
}
UInt32 encoding = kTextEncodingMacRoman;
CFNumberRef enc = CFArrayGetValueAtIndex(strings, 0);
if (CFGetTypeID(enc)!=CFNumberGetTypeID()) {
printf("'%s' dictionary strings error (first item must be a number)\n",isoch+1);
return 1;
}
// Set up the STR* resource here. No worries about RAM, so we do a sufficiently large size,
// and whittle it down later.
Handle strsh = NewHandleClear(32768);
STRn* strs = (STRn*)(*strsh);
// We could hard-code j = strs->count = 10 here, of course...
j = strs->count = CFArrayGetCount(strings)-1; // OSSwapHostToBigInt16() not needed, the system will flip it
UInt8* p = &strs->pstr;
for (i=1;i<=j;i++) {
CFStringRef s = CFArrayGetValueAtIndex(strings, i);
if (CFGetTypeID(s)!=CFStringGetTypeID()) {
printf("'%s' dictionary error (#%d is not a string)\n",isoch+1,i);
return 1;
}
if (!CFStringGetPascalString(s, p, 256, encoding)) {
printf("'%s' dictionary error (can't convert #%d to a Pascal string)\n",isoch+1,i);
return 1;
}
p += p[0]+1;
}
SetHandleSize(strsh, p-(UInt8*)strs);
// Set up the RTF resource.
Handle rtfb = NULL;
int fd = open(argv[3], O_RDONLY);
if (fd>=0) {
size_t rtfs = lseek(fd, 0, SEEK_END);
if (rtfs>1024*1024) { // 1MB ought to be enough for everybody!
printf("'%s' is over 1MB long\n",argv[3]);
close(fd);
return 1;
}
rtfb = NewHandle(rtfs);
lseek(fd, 0, SEEK_SET);
size_t actual = read(fd, *rtfb, rtfs);
close(fd);
if (actual!=rtfs) {
printf("Error reading '%s'\n",argv[3]);
return 1;
}
}
// Open the disk image (or create it if not there, useful for debugging only)
FSRef ref;
HFSUniStr255 xfork = {-1};
FSGetResourceForkName(&xfork);
ResFileRefNum rref = kResFileNotOpened;
if (FSPathMakeRef((UInt8*)argv[1],&ref,nil)!=noErr) {
// If we didn't get the FSRef the file doesn't exist, have to create it
int ofd = open(argv[1],O_RDWR|O_CREAT,00700);
if (ofd>=0) {
close(ofd);
} else {
printf("Couldn't create output file '%s'\n",argv[1]);
return 1;
}
// Now try again to get the FSRef
if (FSPathMakeRef((UInt8*)argv[1],&ref,nil)!=noErr) {
printf("Failed to get FSRef for output file '%s'\n",argv[1]);
return 1;
} else {
printf("Created empty output file '%s'\n",argv[1]);
}
}
// We try to create the resource fork, this will fail silently if it's already there
FSCreateResourceFork(&ref, xfork.length, xfork.unicode, 0);
// Try to open the resource fork
if ((FSOpenResourceFile(&ref,xfork.length,xfork.unicode,fsRdWrPerm,&rref)!=noErr)||(rref==kResFileNotOpened)) {
printf("Failed to open resource fork of '%s'\n",argv[1]);
return 1;
}
// Now we set up the LPic resource, or expand it if it already exists
Handle lpich = Get1Resource('LPic', 5000);
LPic* lpic = NULL;
SInt16 resid = 5000;
if (lpich) {
lpic = (LPic*)(*lpich);
j = OSSwapBigToHostInt16(lpic->count); // No flipper for LPic resources
// Seatch existing LPic
for (i=0;i<j;i++) {
SInt16 rid = OSSwapBigToHostInt16(lpic->item[i].resid)+5000;
if (lpic->item[i].lang==OSSwapBigToHostInt16(region)) {
resid = rid;
goto doit; // Substitute existing license
}
if (resid<=rid) {
resid = rid+1;
}
}
// Insert new license
SetHandleSize(lpich, 2*sizeof(UInt16)+(j+1)*3*sizeof(UInt16));
lpic = (LPic*)(*lpich);
lpic->count = OSSwapHostToBigInt16(j+1);
} else {
// Create new LPic and add the license
lpich = NewHandleClear(sizeof(LPic));
AddResource(lpich, 'LPic', 5000, "\p");
lpic = (LPic*)(*lpich);
lpic->count = OSSwapHostToBigInt16(1);
j = 0;
}
// Store the indexes and write the LPic
lpic->item[j].lang = OSSwapHostToBigInt16(region);
lpic->item[j].resid = OSSwapHostToBigInt16(resid-5000);
lpic->item[j].twobyte = OSSwapHostToBigInt16(encoding>0?1:0);
ChangedResource(lpich);
WriteResource(lpich);
printf("Wrote 'LPic' with %d licenses\n",(int)OSSwapBigToHostInt16(lpic->count));
doit:;
// Write the RTF
Handle old = Get1Resource('RTF ', resid);
if (old) {
RemoveResource(old);
}
AddResource(rtfb, 'RTF ', resid, (UInt8*)isoch);
WriteResource(rtfb);
printf("Wrote 'RTF '#%d for '%s(%s)'\n",(int)resid,argv[2],isoch+1);
// Write the STR#
old = Get1Resource('STR#', resid);
if (old) {
RemoveResource(old);
}
AddResource(strsh, 'STR#', resid, (UInt8*)isoch);
WriteResource(strsh);
printf("Wrote 'STR#'#%d for '%s(%s)', LangCode = %d, RegionCode = %d\n",(int)resid,argv[2],isoch+1,lang,region);
UpdateResFile(rref);
CloseResFile(rref);
return result;
}