macserialrb 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,14 @@
1
+ ## @file
2
+ # Copyright (c) 2019, vit9696. All rights reserved.
3
+ # Copyright (c) 2020, PMheart. All rights reserved.
4
+ # SPDX-License-Identifier: BSD-3-Clause
5
+ ##
6
+ STANDALONE = 1
7
+ PROJECT = macserial
8
+ PRODUCT = $(PROJECT)$(SUFFIX)
9
+ OBJS = $(PROJECT).o
10
+ include ../../User/Makefile
11
+
12
+ ifeq ($(DIST),Darwin)
13
+ LDFLAGS += -Wl,-framework,IOKit -Wl,-framework,CoreFoundation
14
+ endif
@@ -0,0 +1,7 @@
1
+ ## macserial
2
+
3
+ macserial is a tool that obtains and decodes Mac serial number and board identifier to provide more information about the production of your hardware. Works as a decent companion to [Apple Check Coverage](https://checkcoverage.apple.com) and [Apple Specs](http://support-sp.apple.com/sp/index?page=cpuspec&cc=HTD5) portal. Check the [format description](https://github.com/acidanthera/OpenCorePkg/blob/master/Utilities/macserial/FORMAT.md) for more details.
4
+
5
+ Should be built with a compiler supporting C99. Prebuilt binaries are available for macOS 10.4 and higher.
6
+
7
+ Run with `-h` argument to see all available arguments.
@@ -0,0 +1,96 @@
1
+ #
2
+ # Extract platform data from CCC
3
+ # Copyright (c) 2018 vit9696
4
+ #
5
+
6
+ import ida_bytes
7
+ import struct
8
+
9
+ def readaddr(ea):
10
+ a = ida_bytes.get_bytes(ea, 8)
11
+ if a == BADADDR:
12
+ return None
13
+ return struct.unpack("L", a)[0]
14
+
15
+ def readstr(ea, l=256):
16
+ if ea == BADADDR:
17
+ return None
18
+
19
+ str = ""
20
+ while 1:
21
+ c = ida_bytes.get_bytes(ea, 1)
22
+ if c == BADADDR:
23
+ if str == "":
24
+ return None
25
+ else:
26
+ return str
27
+ elif c == "\0":
28
+ return str
29
+
30
+ str += c
31
+ ea += 1
32
+ if len(str) > l:
33
+ return str
34
+
35
+ base = get_name_ea(0, "_show")
36
+ count = 0
37
+ print 'typedef enum {'
38
+ while 1:
39
+ s = readstr(readaddr(base))
40
+ if s == None:
41
+ break
42
+ info = s.split('-', 1)
43
+ print ' %s, // %s' % (info[0].strip().replace(',', '_'), info[1].strip())
44
+ base += 8
45
+ count += 1;
46
+
47
+ name = get_name(base)
48
+ if name != None and name != "" and name != "_show":
49
+ break
50
+ print '} AppleModel;'
51
+ print '#define APPLE_MODEL_MAX %d' % count
52
+
53
+ base = get_name_ea(0, "_ApplePlatformData")
54
+ print 'static PLATFORMDATA ApplePlatformData[] = {'
55
+ while 1:
56
+ productName = readstr(readaddr(base+0))
57
+ firmwareVersion = readstr(readaddr(base+8)) # board version
58
+ boardID = readstr(readaddr(base+16))
59
+ productFamily = readstr(readaddr(base+24))
60
+ systemVersion = readstr(readaddr(base+32)) # bios version
61
+ serialNumber = readstr(readaddr(base+40))
62
+ chassisAsset = readstr(readaddr(base+48))
63
+ smcRevision = struct.unpack("BBBBBB", ida_bytes.get_bytes(base+56, 6))
64
+ unknownValue = readaddr(base+64)
65
+ smcBranch = readstr(readaddr(base+72))
66
+ smcPlatform = readstr(readaddr(base+80))
67
+ smcConfig = readaddr(base+88)
68
+
69
+ if productName == None or firmwareVersion == None or boardID == None or productFamily == None or systemVersion == None or serialNumber == None or chassisAsset == None or smcBranch == None or smcConfig == None:
70
+ break
71
+
72
+ print ' { "%s", "%s", "%s",' % (productName, firmwareVersion, boardID)
73
+ print ' "%s", "%s", "%s", "%s",' % (productFamily, systemVersion, serialNumber, chassisAsset)
74
+ print ' { 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X }, "%s", "%s", 0x%08X },' % (smcRevision[0], smcRevision[1], smcRevision[2], smcRevision[3], smcRevision[4], smcRevision[5], smcBranch, smcPlatform, smcConfig)
75
+
76
+ base += 0x60
77
+
78
+ name = get_name(base)
79
+ if name != None and name != "" and name != "_ApplePlatformData":
80
+ break
81
+ print '};'
82
+
83
+ base = get_name_ea(0, "_ModelCode")
84
+ print 'static const char *AppleModelCode[] = {'
85
+ while 1:
86
+ s = readstr(readaddr(base))
87
+ if s == None:
88
+ break
89
+
90
+ print ' "%s",' % s
91
+ base += 8
92
+
93
+ name = get_name(base)
94
+ if name != None and name != "" and name != "_ModelCode":
95
+ break
96
+ print '};'
@@ -0,0 +1,971 @@
1
+ //
2
+ // Decode mac serial number
3
+ //
4
+ // Copyright (c) 2018-2020 vit9696
5
+ // Copyright (c) 2020 Matis Schotte
6
+ //
7
+
8
+ #include <limits.h>
9
+ #include <stdbool.h>
10
+ #include <stdio.h>
11
+ #include <stdlib.h>
12
+ #include <stdint.h>
13
+ #include <string.h>
14
+ #include <time.h>
15
+
16
+ #ifdef __APPLE__
17
+ #include <CoreFoundation/CoreFoundation.h>
18
+ #include <IOKit/IOKitLib.h>
19
+ #endif
20
+
21
+ #include "macserial.h"
22
+ #include "modelinfo.h"
23
+
24
+ #ifdef __APPLE__
25
+ static CFTypeRef get_ioreg_entry(const char *path, CFStringRef name, CFTypeID type) {
26
+ CFTypeRef value = NULL;
27
+ io_registry_entry_t entry = IORegistryEntryFromPath(kIOMasterPortDefault, path);
28
+ if (entry) {
29
+ value = IORegistryEntryCreateCFProperty(entry, name, kCFAllocatorDefault, 0);
30
+ if (value) {
31
+ if (CFGetTypeID(value) != type) {
32
+ CFRelease(value);
33
+ value = NULL;
34
+ printf("%s in %s has wrong type!\n", CFStringGetCStringPtr(name, kCFStringEncodingMacRoman), path);
35
+ }
36
+ } else {
37
+ printf("Failed to find to %s in %s!\n", CFStringGetCStringPtr(name, kCFStringEncodingMacRoman), path);
38
+ }
39
+ IOObjectRelease(entry);
40
+ } else {
41
+ printf("Failed to connect to %s!\n", path);
42
+ }
43
+ return value;
44
+ }
45
+ #endif
46
+
47
+ // Apple uses various conversion tables (e.g. AppleBase34) for value encoding.
48
+ static int32_t alpha_to_value(char c, int32_t *conv, const char *blacklist) {
49
+ if (c < 'A' || c > 'Z')
50
+ return -1;
51
+
52
+ while (blacklist && *blacklist != '\0')
53
+ if (*blacklist++ == c)
54
+ return -1;
55
+
56
+ return conv[c - 'A'];
57
+ }
58
+
59
+ // This is modified base34 used by Apple with I and O excluded.
60
+ static int32_t base34_to_value(char c, int32_t mul) {
61
+ if (c >= '0' && c <= '9')
62
+ return (c - '0') * mul;
63
+ if (c >= 'A' && c <= 'Z') {
64
+ int32_t tmp = alpha_to_value(c, AppleTblBase34, AppleBase34Blacklist);
65
+ if (tmp >= 0)
66
+ return tmp * mul;
67
+ }
68
+ return -1;
69
+ }
70
+
71
+ static int32_t line_to_rmin(int32_t line) {
72
+ // info->line[0] is raw decoded copy, but it is not the real first produced unit.
73
+ // To get the real copy we need to find the minimal allowed raw decoded copy,
74
+ // which allows to obtain info->decodedLine.
75
+ int rmin = 0;
76
+ if (line > SERIAL_LINE_REPR_MAX)
77
+ rmin = (line - SERIAL_LINE_REPR_MAX + 67) / 68;
78
+ return rmin;
79
+ }
80
+
81
+ // This one is modded to implement CCC algo for better generation.
82
+ // Changed base36 to base34, since that's what Apple uses.
83
+ // The algo is trash but is left for historical reasons.
84
+ static bool get_ascii7(uint32_t value, char *dst, size_t sz) {
85
+ // This is CCC conversion.
86
+ if (value < 1000000)
87
+ return false;
88
+
89
+ while (value > 10000000)
90
+ value /= 10;
91
+
92
+ // log(2**64) / log(34) = 12.57 => max 13 char + '\0'
93
+ char buffer[14];
94
+ size_t offset = sizeof(buffer);
95
+
96
+ buffer[--offset] = '\0';
97
+ do {
98
+ buffer[--offset] = AppleBase34Reverse[value % 34];
99
+ } while (value /= 34);
100
+
101
+ strncpy(dst, &buffer[offset], sz-1);
102
+ dst[sz-1] = 0;
103
+
104
+ return true;
105
+ }
106
+
107
+ static bool verify_mlb_checksum(const char *mlb, size_t len) {
108
+ const char alphabet[] = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
109
+ size_t checksum = 0;
110
+ for (size_t i = 0; i < len; ++i) {
111
+ for (size_t j = 0; j <= sizeof (alphabet); ++j) {
112
+ if (j == sizeof (alphabet))
113
+ return false;
114
+ if (mlb[i] == alphabet[j]) {
115
+ checksum += (((i & 1) == (len & 1)) * 2 + 1) * j;
116
+ break;
117
+ }
118
+ }
119
+ }
120
+ return checksum % (sizeof(alphabet) - 1) == 0;
121
+ }
122
+
123
+ // Taken from https://en.wikipedia.org/wiki/Xorshift#Example_implementation
124
+ // I am not positive what is better to use here (especially on Windows).
125
+ // Fortunately we only need something only looking random.
126
+ static uint32_t pseudo_random(void) {
127
+ #ifdef __GNUC__
128
+ if (arc4random)
129
+ return arc4random();
130
+ #endif
131
+
132
+ static uint32_t state;
133
+
134
+ if (!state) {
135
+ fprintf(stderr, "Warning: arc4random is not available!\n");
136
+ state = (uint32_t)time(NULL);
137
+ }
138
+
139
+ uint32_t x = state;
140
+ x ^= x << 13;
141
+ x ^= x >> 17;
142
+ x ^= x << 5;
143
+ state = x;
144
+ return x;
145
+ }
146
+
147
+ // Taken from https://opensource.apple.com/source/Libc/Libc-1082.50.1/gen/FreeBSD/arc4random.c
148
+ // Mac OS X 10.6.8 and earlier do not have arc4random_uniform, so we implement one.
149
+ static uint32_t pseudo_random_between(uint32_t from, uint32_t to) {
150
+ uint32_t upper_bound = to + 1 - from;
151
+
152
+ #ifdef __GNUC__
153
+ // Prefer native implementation if available.
154
+ if (arc4random_uniform)
155
+ return from + arc4random_uniform(upper_bound);
156
+ #endif
157
+
158
+ uint32_t r, min;
159
+
160
+ if (upper_bound < 2)
161
+ return from;
162
+
163
+ #if (ULONG_MAX > 0xffffffffUL)
164
+ min = 0x100000000UL % upper_bound;
165
+ #else
166
+ if (upper_bound > 0x80000000)
167
+ min = 1 + ~upper_bound;
168
+ else
169
+ min = ((0xffffffff - (upper_bound * 2)) + 1) % upper_bound;
170
+ #endif
171
+
172
+ for (;;) {
173
+ r = pseudo_random();
174
+ if (r >= min)
175
+ break;
176
+ }
177
+
178
+ return from + r % upper_bound;
179
+ }
180
+
181
+ static int32_t get_current_model(void) {
182
+ #ifdef __APPLE__
183
+ CFDataRef model = get_ioreg_entry("IODeviceTree:/", CFSTR("model"), CFDataGetTypeID());
184
+ if (model) {
185
+ const char *cptr = (const char *)CFDataGetBytePtr(model);
186
+ size_t len = (size_t)CFDataGetLength(model);
187
+ int32_t i;
188
+ for (i = 0; i < APPLE_MODEL_MAX; i++) {
189
+ if (!strncmp(ApplePlatformData[i].productName, cptr, len))
190
+ break;
191
+ }
192
+ CFRelease(model);
193
+ if (i < APPLE_MODEL_MAX)
194
+ return i;
195
+ }
196
+ #endif
197
+ return -1;
198
+ }
199
+
200
+ static uint32_t get_production_year(AppleModel model, bool print) {
201
+ uint32_t *years = &AppleModelYear[model][0];
202
+ uint32_t num = 0;
203
+
204
+ for (num = 0; num < APPLE_MODEL_YEAR_MAX && years[num]; num++) {
205
+ if (print) {
206
+ if (num+1 != APPLE_MODEL_YEAR_MAX && years[num+1])
207
+ printf("%d, ", years[num]);
208
+ else
209
+ printf("%d\n", years[num]);
210
+ }
211
+ }
212
+
213
+ if (ApplePreferredModelYear[model] > 0)
214
+ return ApplePreferredModelYear[model];
215
+
216
+ return years[pseudo_random() % num];
217
+ }
218
+
219
+ static const char *get_model_code(AppleModel model, bool print) {
220
+ const char **codes = &AppleModelCode[model][0];
221
+
222
+ if (print) {
223
+ for (uint32_t i = 0; i < APPLE_MODEL_CODE_MAX && codes[i]; i++)
224
+ if (i+1 != APPLE_MODEL_CODE_MAX && codes[i+1])
225
+ printf("%s, ", codes[i]);
226
+ else
227
+ printf("%s\n", codes[i]);
228
+ }
229
+
230
+ // Always choose the first model for stability by default.
231
+ return codes[0];
232
+ }
233
+
234
+ static const char *get_board_code(AppleModel model, bool print) {
235
+ const char **codes = &AppleBoardCode[model][0];
236
+
237
+ if (print) {
238
+ for (uint32_t i = 0; i < APPLE_BOARD_CODE_MAX && codes[i]; i++)
239
+ if (i+1 != APPLE_BOARD_CODE_MAX && codes[i+1])
240
+ printf("%s, ", codes[i]);
241
+ else
242
+ printf("%s\n", codes[i]);
243
+ }
244
+
245
+ // Always choose the first model for stability by default.
246
+ return codes[0];
247
+ }
248
+
249
+ static bool get_serial_info(const char *serial, SERIALINFO *info, bool print) {
250
+ if (!info)
251
+ return false;
252
+
253
+ memset(info, 0, sizeof(SERIALINFO));
254
+
255
+ // Verify length.
256
+ size_t serial_len = strlen(serial);
257
+ if (serial_len != SERIAL_OLD_LEN && serial_len != SERIAL_NEW_LEN) {
258
+ printf("ERROR: Invalid serial length, must be %d or %d\n", SERIAL_NEW_LEN, SERIAL_OLD_LEN);
259
+ return false;
260
+ }
261
+
262
+ // Assume every serial valid by default.
263
+ info->valid = true;
264
+
265
+ // Verify alphabet (base34 with I and O exclued).
266
+ for (size_t i = 0; i < serial_len; i++) {
267
+ if (!((serial[i] >= 'A' && serial[i] <= 'Z' && serial[i] != 'O' && serial[i] != 'I') ||
268
+ (serial[i] >= '0' && serial[i] <= '9'))) {
269
+ printf("WARN: Invalid symbol '%c' in serial!\n", serial[i]);
270
+ info->valid = false;
271
+ }
272
+ }
273
+
274
+ size_t model_len = 0;
275
+
276
+ // Start with looking up the model.
277
+ info->modelIndex = -1;
278
+ for (uint32_t i = 0; i < ARRAY_SIZE(AppleModelCode); i++) {
279
+ for (uint32_t j = 0; j < APPLE_MODEL_CODE_MAX; j++) {
280
+ const char *code = AppleModelCode[i][j];
281
+ if (!code)
282
+ break;
283
+ model_len = strlen(code);
284
+ if (model_len == 0)
285
+ break;
286
+ assert(model_len == MODEL_CODE_OLD_LEN || model_len == MODEL_CODE_NEW_LEN);
287
+ if (((serial_len == SERIAL_OLD_LEN && model_len == MODEL_CODE_OLD_LEN)
288
+ || (serial_len == SERIAL_NEW_LEN && model_len == MODEL_CODE_NEW_LEN))
289
+ && !strncmp(serial + serial_len - model_len, code, model_len)) {
290
+ strncpy(info->model, code, sizeof(info->model));
291
+ info->model[sizeof(info->model)-1] = '\0';
292
+ info->modelIndex = (int32_t)i;
293
+ break;
294
+ }
295
+ }
296
+ }
297
+
298
+ // Also lookup apple model.
299
+ for (uint32_t i = 0; i < ARRAY_SIZE(AppleModelDesc); i++) {
300
+ const char *code = AppleModelDesc[i].code;
301
+ model_len = strlen(code);
302
+ assert(model_len == MODEL_CODE_OLD_LEN || model_len == MODEL_CODE_NEW_LEN);
303
+ if (((serial_len == SERIAL_OLD_LEN && model_len == MODEL_CODE_OLD_LEN)
304
+ || (serial_len == SERIAL_NEW_LEN && model_len == MODEL_CODE_NEW_LEN))
305
+ && !strncmp(serial + serial_len - model_len, code, model_len)) {
306
+ info->appleModel = AppleModelDesc[i].name;
307
+ break;
308
+ }
309
+ }
310
+
311
+ // Fallback to possibly valid values if model is unknown.
312
+ if (info->modelIndex == -1) {
313
+ if (serial_len == SERIAL_NEW_LEN)
314
+ model_len = MODEL_CODE_NEW_LEN;
315
+ else
316
+ model_len = MODEL_CODE_OLD_LEN;
317
+ strncpy(info->model, serial + serial_len - model_len, model_len);
318
+ info->model[model_len] = '\0';
319
+ }
320
+
321
+ // Lookup production location
322
+ info->legacyCountryIdx = -1;
323
+ info->modernCountryIdx = -1;
324
+
325
+ if (serial_len == SERIAL_NEW_LEN) {
326
+ strncpy(info->country, serial, COUNTRY_NEW_LEN);
327
+ info->country[COUNTRY_NEW_LEN] = '\0';
328
+ serial += COUNTRY_NEW_LEN;
329
+ for (size_t i = 0; i < ARRAY_SIZE(AppleLocations); i++) {
330
+ if (!strcmp(info->country, AppleLocations[i])) {
331
+ info->modernCountryIdx = (int32_t)i;
332
+ break;
333
+ }
334
+ }
335
+ } else {
336
+ strncpy(info->country, serial, COUNTRY_OLD_LEN);
337
+ info->country[COUNTRY_OLD_LEN] = '\0';
338
+ serial += COUNTRY_OLD_LEN;
339
+ for (size_t i = 0; i < ARRAY_SIZE(AppleLegacyLocations); i++) {
340
+ if (!strcmp(info->country, AppleLegacyLocations[i])) {
341
+ info->legacyCountryIdx = (int32_t)i;
342
+ break;
343
+ }
344
+ }
345
+ }
346
+
347
+ // Decode production year and week
348
+ if (serial_len == SERIAL_NEW_LEN) {
349
+ // These are not exactly year and week, lower year bit is used for week encoding.
350
+ info->year[0] = *serial++;
351
+ info->week[0] = *serial++;
352
+
353
+ // New encoding started in 2010.
354
+ info->decodedYear = alpha_to_value(info->year[0], AppleTblYear, AppleYearBlacklist);
355
+ // Since year can be encoded ambiguously, check the model code for 2010/2020 difference.
356
+ // Old check relies on first letter of model to be greater than or equal to H, which breaks compatibility with iMac20,2 (=0).
357
+ // Added logic checks provided model years `AppleModelYear` first year greater than or equal to 2020.
358
+ if ((info->modelIndex >= 0 && AppleModelYear[info->modelIndex][0] >= 2020) || (info->decodedYear == 0 && info->model[0] >= 'H')) {
359
+ info->decodedYear += 2020;
360
+ } else if (info->decodedYear >= 0) {
361
+ info->decodedYear += 2010;
362
+ } else {
363
+ printf("WARN: Invalid year symbol '%c'!\n", info->year[0]);
364
+ info->valid = false;
365
+ }
366
+
367
+ if (info->week[0] > '0' && info->week[0] <= '9')
368
+ info->decodedWeek = info->week[0] - '0';
369
+ else
370
+ info->decodedWeek = alpha_to_value(info->week[0], AppleTblWeek, AppleWeekBlacklist);
371
+ if (info->decodedWeek > 0) {
372
+ if (info->decodedYear > 0)
373
+ info->decodedWeek += alpha_to_value(info->year[0], AppleTblWeekAdd, NULL);
374
+ } else {
375
+ printf("WARN: Invalid week symbol '%c'!\n", info->week[0]);
376
+ info->valid = false;
377
+ }
378
+ } else {
379
+ info->year[0] = *serial++;
380
+ info->week[0] = *serial++;
381
+ info->week[1] = *serial++;
382
+
383
+ // This is proven by MacPro5,1 valid serials from 2011 and 2012.
384
+ if (info->year[0] >= '0' && info->year[0] <= '2') {
385
+ info->decodedYear = 2010 + info->year[0] - '0';
386
+ } else if (info->year[0] >= '3' && info->year[0] <= '9') {
387
+ info->decodedYear = 2000 + info->year[0] - '0';
388
+ } else {
389
+ info->decodedYear = -1;
390
+ printf("WARN: Invalid year symbol '%c'!\n", info->year[0]);
391
+ info->valid = false;
392
+ }
393
+
394
+ for (int32_t i = 0; i < 2; i++) {
395
+ if (info->week[i] >= '0' && info->week[i] <= '9') {
396
+ info->decodedWeek += (i == 0 ? 10 : 1) * (info->week[i] - '0');
397
+ } else {
398
+ info->decodedWeek = -1;
399
+ printf("WARN: Invalid week symbol '%c'!\n", info->week[i]);
400
+ info->valid = false;
401
+ break;
402
+ }
403
+ }
404
+ }
405
+
406
+ if (info->decodedWeek < SERIAL_WEEK_MIN || info->decodedWeek > SERIAL_WEEK_MAX) {
407
+ printf("WARN: Decoded week %d is out of valid range [%d, %d]!\n", info->decodedWeek, SERIAL_WEEK_MIN, SERIAL_WEEK_MAX);
408
+ info->decodedWeek = -1;
409
+ }
410
+
411
+ if (info->decodedYear > 0 && info->modelIndex >= 0) {
412
+ bool found = false;
413
+ for (size_t i = 0; !found && i < APPLE_MODEL_YEAR_MAX && AppleModelYear[info->modelIndex][i]; i++)
414
+ if ((int32_t)AppleModelYear[info->modelIndex][i] == info->decodedYear)
415
+ found = true;
416
+ if (!found) {
417
+ printf("WARN: Invalid year %d for model %s\n", info->decodedYear, ApplePlatformData[info->modelIndex].productName);
418
+ info->valid = false;
419
+ }
420
+ }
421
+
422
+ // Decode production line and copy
423
+ int32_t mul[] = {68, 34, 1};
424
+ for (uint32_t i = 0; i < ARRAY_SIZE(mul); i++) {
425
+ info->line[i] = *serial++;
426
+ int32_t tmp = base34_to_value(info->line[i], mul[i]);
427
+ if (tmp >= 0) {
428
+ info->decodedLine += tmp;
429
+ } else {
430
+ printf("WARN: Invalid line symbol '%c'!\n", info->line[i]);
431
+ info->valid = false;
432
+ break;
433
+ }
434
+ }
435
+
436
+ if (info->decodedLine >= 0)
437
+ info->decodedCopy = base34_to_value(info->line[0], 1) - line_to_rmin(info->decodedLine);
438
+
439
+ if (print) {
440
+ printf("%14s: %4s - ", "Country", info->country);
441
+ if (info->legacyCountryIdx >= 0)
442
+ printf("%s\n", AppleLegacyLocationNames[info->legacyCountryIdx]);
443
+ else if (info->modernCountryIdx >= 0)
444
+ printf("%s\n", AppleLocationNames[info->modernCountryIdx]);
445
+ else
446
+ puts("Unknown, please report!");
447
+ printf("%14s: %4s - %d\n", "Year", info->year, info->decodedYear);
448
+ printf("%14s: %4s - %d", "Week", info->week, info->decodedWeek);
449
+ if (info->decodedYear > 0 && info->decodedWeek > 0) {
450
+ struct tm startd = {
451
+ .tm_isdst = -1,
452
+ .tm_year = info->decodedYear - 1900,
453
+ .tm_mday = 1 + 7 * (info->decodedWeek-1),
454
+ .tm_mon = 0
455
+ };
456
+ if (mktime(&startd) >= 0) {
457
+ printf(" (%02d.%02d.%04d", startd.tm_mday, startd.tm_mon+1, startd.tm_year+1900);
458
+ if (info->decodedWeek == 53 && startd.tm_mday != 31) {
459
+ printf("-31.12.%04d", startd.tm_year+1900);
460
+ } else if (info->decodedWeek < 53) {
461
+ startd.tm_mday += 6;
462
+ if (mktime(&startd))
463
+ printf("-%02d.%02d.%04d", startd.tm_mday, startd.tm_mon+1, startd.tm_year+1900);
464
+ }
465
+ puts(")");
466
+ }
467
+ } else {
468
+ puts("");
469
+ }
470
+ printf("%14s: %4s - %d (copy %d)\n", "Line", info->line, info->decodedLine,
471
+ info->decodedCopy >= 0 ? info->decodedCopy + 1 : -1);
472
+ printf("%14s: %4s - %s\n", "Model", info->model, info->modelIndex >= 0 ?
473
+ ApplePlatformData[info->modelIndex].productName : "Unknown");
474
+ printf("%14s: %s\n", "SystemModel", info->appleModel != NULL ? info->appleModel : "Unknown, please report!");
475
+ printf("%14s: %s\n", "Valid", info->valid ? "Possibly" : "Unlikely");
476
+ }
477
+
478
+ return true;
479
+ }
480
+
481
+ static bool get_serial(SERIALINFO *info) {
482
+ if (info->modelIndex < 0 && info->model[0] == '\0') {
483
+ printf("ERROR: Unable to determine model!\n");
484
+ return false;
485
+ }
486
+
487
+ if (info->model[0] == '\0') {
488
+ strncpy(info->model, get_model_code((AppleModel)info->modelIndex, false), MODEL_CODE_NEW_LEN);
489
+ info->model[MODEL_CODE_NEW_LEN] = '\0';
490
+ }
491
+
492
+ size_t country_len = strlen(info->country);
493
+ if (country_len == 0) {
494
+ // Random country choice strongly decreases key verification probability.
495
+ country_len = strlen(info->model) == MODEL_CODE_NEW_LEN ? COUNTRY_NEW_LEN : COUNTRY_OLD_LEN;
496
+ if (info->modelIndex < 0) {
497
+ strncpy(info->country, country_len == COUNTRY_OLD_LEN ? AppleLegacyLocations[0] : AppleLocations[0], COUNTRY_NEW_LEN+1);
498
+ } else {
499
+ strncpy(info->country, &ApplePlatformData[info->modelIndex].serialNumber[0], country_len);
500
+ info->country[country_len] = '\0';
501
+ }
502
+ }
503
+
504
+ if (info->decodedYear < 0) {
505
+ if (info->modelIndex < 0)
506
+ info->decodedYear = country_len == COUNTRY_OLD_LEN ? SERIAL_YEAR_OLD_MAX : SERIAL_YEAR_NEW_MAX;
507
+ else
508
+ info->decodedYear = (int32_t)get_production_year((AppleModel)info->modelIndex, false);
509
+ }
510
+
511
+ // Last week is too rare to care
512
+ if (info->decodedWeek < 0)
513
+ info->decodedWeek = (int32_t)pseudo_random_between(SERIAL_WEEK_MIN, SERIAL_WEEK_MAX-1);
514
+
515
+ if (country_len == COUNTRY_OLD_LEN) {
516
+ if (info->decodedYear < SERIAL_YEAR_OLD_MIN || info->decodedYear > SERIAL_YEAR_OLD_MAX) {
517
+ printf("ERROR: Year %d is out of valid legacy range [%d, %d]!\n", info->decodedYear, SERIAL_YEAR_OLD_MIN, SERIAL_YEAR_OLD_MAX);
518
+ return false;
519
+ }
520
+
521
+ info->year[0] = '0' + (char)((info->decodedYear - 2000) % 10);
522
+ info->week[0] = '0' + (char)((info->decodedWeek) / 10);
523
+ info->week[1] = '0' + (info->decodedWeek) % 10;
524
+ } else {
525
+ if (info->decodedYear < SERIAL_YEAR_NEW_MIN || info->decodedYear > SERIAL_YEAR_NEW_MAX) {
526
+ printf("ERROR: Year %d is out of valid modern range [%d, %d]!\n", info->decodedYear, SERIAL_YEAR_NEW_MIN, SERIAL_YEAR_NEW_MAX);
527
+ return false;
528
+ }
529
+
530
+ size_t base_new_year = 2010;
531
+ if (info->decodedYear == SERIAL_YEAR_NEW_MAX) {
532
+ base_new_year = 2020;
533
+ }
534
+
535
+ info->year[0] = AppleYearReverse[(info->decodedYear - base_new_year) * 2 + (info->decodedWeek >= 27)];
536
+ info->week[0] = AppleWeekReverse[info->decodedWeek];
537
+ }
538
+
539
+ if (info->decodedLine < 0)
540
+ info->decodedLine = (int32_t)pseudo_random_between(SERIAL_LINE_MIN, SERIAL_LINE_MAX);
541
+
542
+ int32_t rmin = line_to_rmin(info->decodedLine);
543
+
544
+ // Verify and apply user supplied copy if any
545
+ if (info->decodedCopy >= 0) {
546
+ rmin += info->decodedCopy - 1;
547
+ if (rmin * 68 > info->decodedLine) {
548
+ printf("ERROR: Copy %d cannot represent line %d!\n", info->decodedCopy, info->decodedLine);
549
+ return false;
550
+ }
551
+ }
552
+
553
+ info->line[0] = AppleBase34Reverse[rmin];
554
+ info->line[1] = AppleBase34Reverse[(info->decodedLine - rmin * 68) / 34];
555
+ info->line[2] = AppleBase34Reverse[(info->decodedLine - rmin * 68) % 34];
556
+
557
+ return true;
558
+ }
559
+
560
+ static void get_mlb(SERIALINFO *info, char *dst, size_t sz) {
561
+ // This is a direct reverse from CCC, rework it later...
562
+ do {
563
+ uint32_t year = 0, week = 0;
564
+
565
+ bool legacy = strlen(info->country) == COUNTRY_OLD_LEN;
566
+
567
+ if (legacy) {
568
+ year = (uint32_t)(info->year[0] - '0');
569
+ week = (uint32_t)(info->week[0] - '0') * 10 + (uint32_t)(info->week[1] - '0');
570
+ } else {
571
+ char syear = info->year[0];
572
+ char sweek = info->week[0];
573
+
574
+ const char srcyear[] = "CDFGHJKLMNPQRSTVWXYZ";
575
+ const char dstyear[] = "00112233445566778899";
576
+ for (size_t i = 0; i < ARRAY_SIZE(srcyear)-1; i++) {
577
+ if (syear == srcyear[i]) {
578
+ year = (uint32_t)(dstyear[i] - '0');
579
+ break;
580
+ }
581
+ }
582
+
583
+ const char overrides[] = "DGJLNQSVXZ";
584
+ for (size_t i = 0; i < ARRAY_SIZE(overrides)-1; i++) {
585
+ if (syear == overrides[i]) {
586
+ week = 27;
587
+ break;
588
+ }
589
+ }
590
+
591
+ const char srcweek[] = "123456789CDFGHJKLMNPQRSTVWXYZ";
592
+ for (size_t i = 0; i < ARRAY_SIZE(srcweek)-1; i++) {
593
+ if (sweek == srcweek[i]) {
594
+ week += i + 1;
595
+ break;
596
+ }
597
+ }
598
+
599
+ // This is silently not handled, and it should not be needed for normal serials.
600
+ // Bugged MacBookPro6,2 and MacBookPro7,1 will gladly hit it.
601
+ if (week < SERIAL_WEEK_MIN) {
602
+ snprintf(dst, sz, "FAIL-ZERO-%c", sweek);
603
+ return;
604
+ }
605
+ }
606
+
607
+ week--;
608
+
609
+ if (week <= 9) {
610
+ if (week == 0) {
611
+ week = SERIAL_WEEK_MAX;
612
+ if (year == 0)
613
+ year = 9;
614
+ else
615
+ year--;
616
+ }
617
+ }
618
+
619
+ if (legacy) {
620
+ char code[4] = {0};
621
+ // The loop is not present in CCC, but it throws an exception here,
622
+ // and effectively generates nothing. The logic is crazy :/.
623
+ // Also, it was likely meant to be written as pseudo_random() % 0x8000.
624
+ while (!get_ascii7(pseudo_random_between(0, 0x7FFE) * 0x73BA1C, code, sizeof(code)));
625
+ const char *board = get_board_code(info->modelIndex, false);
626
+ char suffix = AppleBase34Reverse[pseudo_random() % 34];
627
+ snprintf(dst, sz, "%s%d%02d0%s%s%c", info->country, year, week, code, board, suffix);
628
+ } else {
629
+ const char *part1 = MLBBlock1[pseudo_random() % ARRAY_SIZE(MLBBlock1)];
630
+ const char *part2 = MLBBlock2[pseudo_random() % ARRAY_SIZE(MLBBlock2)];
631
+ const char *board = get_board_code(info->modelIndex, false);
632
+ const char *part3 = MLBBlock3[pseudo_random() % ARRAY_SIZE(MLBBlock3)];
633
+
634
+ snprintf(dst, sz, "%s%d%02d%s%s%s%s", info->country, year, week, part1, part2, board, part3);
635
+ }
636
+ } while (!verify_mlb_checksum(dst, strlen(dst)));
637
+ }
638
+
639
+ static void get_system_info(void) {
640
+ #ifdef __APPLE__
641
+ CFDataRef model = get_ioreg_entry("IODeviceTree:/", CFSTR("model"), CFDataGetTypeID());
642
+ CFDataRef board = get_ioreg_entry("IODeviceTree:/", CFSTR("board-id"), CFDataGetTypeID());
643
+ CFDataRef efiver = get_ioreg_entry("IODeviceTree:/rom", CFSTR("version"), CFDataGetTypeID());
644
+ CFStringRef serial = get_ioreg_entry("IODeviceTree:/", CFSTR("IOPlatformSerialNumber"), CFStringGetTypeID());
645
+ CFStringRef hwuuid = get_ioreg_entry("IODeviceTree:/", CFSTR("IOPlatformUUID"), CFStringGetTypeID());
646
+ CFDataRef smuuid = get_ioreg_entry("IODeviceTree:/efi/platform", CFSTR("system-id"), CFDataGetTypeID());
647
+ CFDataRef rom = get_ioreg_entry("IODeviceTree:/options", CFSTR("4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:ROM"), CFDataGetTypeID());
648
+ CFDataRef mlb = get_ioreg_entry("IODeviceTree:/options", CFSTR("4D1EDE05-38C7-4A6A-9CC6-4BCCA8B38C14:MLB"), CFDataGetTypeID());
649
+
650
+ CFDataRef pwr[5] = {0};
651
+ CFStringRef pwrname[5] = {
652
+ CFSTR("Gq3489ugfi"),
653
+ CFSTR("Fyp98tpgj"),
654
+ CFSTR("kbjfrfpoJU"),
655
+ CFSTR("oycqAZloTNDm"),
656
+ CFSTR("abKPld1EcMni"),
657
+ };
658
+
659
+ for (size_t i = 0; i < ARRAY_SIZE(pwr); i++)
660
+ pwr[i] = get_ioreg_entry("IOPower:/", pwrname[i], CFDataGetTypeID());
661
+
662
+ if (model) {
663
+ printf("%14s: %.*s\n", "Model", (int)CFDataGetLength(model), CFDataGetBytePtr(model));
664
+ CFRelease(model);
665
+ }
666
+
667
+ if (board) {
668
+ printf("%14s: %.*s\n", "Board ID", (int)CFDataGetLength(board), CFDataGetBytePtr(board));
669
+ CFRelease(board);
670
+ }
671
+
672
+ if (efiver) {
673
+ printf("%14s: %.*s\n", "FW Version", (int)CFDataGetLength(efiver), CFDataGetBytePtr(efiver));
674
+ CFRelease(efiver);
675
+ }
676
+
677
+ if (hwuuid) {
678
+ printf("%14s: %s\n", "Hardware UUID", CFStringGetCStringPtr(hwuuid, kCFStringEncodingMacRoman));
679
+ CFRelease(hwuuid);
680
+ }
681
+
682
+ puts("");
683
+
684
+ if (serial) {
685
+ const char *cstr = CFStringGetCStringPtr(serial, kCFStringEncodingMacRoman);
686
+ printf("%14s: %s\n", "Serial Number", cstr);
687
+ SERIALINFO info;
688
+ get_serial_info(cstr, &info, true);
689
+ CFRelease(serial);
690
+ puts("");
691
+ }
692
+
693
+ if (smuuid) {
694
+ if (CFDataGetLength(smuuid) == SZUUID) {
695
+ const uint8_t *p = CFDataGetBytePtr(smuuid);
696
+ printf("%14s: " PRIUUID "\n", "System ID", CASTUUID(p));
697
+ }
698
+ CFRelease(smuuid);
699
+ }
700
+
701
+ if (rom) {
702
+ if (CFDataGetLength(rom) == 6) {
703
+ const uint8_t *p = CFDataGetBytePtr(rom);
704
+ printf("%14s: %02X%02X%02X%02X%02X%02X\n", "ROM", p[0], p[1], p[2], p[3], p[4], p[5]);
705
+ }
706
+ CFRelease(rom);
707
+ }
708
+
709
+ if (mlb) {
710
+ printf("%14s: %.*s\n", "MLB", (int)CFDataGetLength(mlb), CFDataGetBytePtr(mlb));
711
+ if (!verify_mlb_checksum((const char *)CFDataGetBytePtr(mlb), CFDataGetLength(mlb)))
712
+ printf("WARN: Invalid MLB checksum!\n");
713
+ CFRelease(mlb);
714
+ }
715
+
716
+ puts("");
717
+
718
+ for (size_t i = 0; i < ARRAY_SIZE(pwr); i++) {
719
+ if (pwr[i]) {
720
+ printf("%14s: ", CFStringGetCStringPtr(pwrname[i], kCFStringEncodingMacRoman));
721
+ const uint8_t *p = CFDataGetBytePtr(pwr[i]);
722
+ CFIndex sz = CFDataGetLength(pwr[i]);
723
+ for (CFIndex j = 0; j < sz; j++)
724
+ printf("%02X", p[j]);
725
+ puts("");
726
+ CFRelease(pwr[i]);
727
+ }
728
+ }
729
+
730
+ puts("");
731
+ #endif
732
+
733
+ printf("Version %s. Use -h argument to see usage options.\n", PROGRAM_VERSION);
734
+ }
735
+
736
+ static int usage(const char *app) {
737
+ printf(
738
+ "%s arguments:\n"
739
+ " --help (-h) show this help\n"
740
+ " --version (-v) show program version\n"
741
+ " --deriv <serial> (-d) generate all derivative serials\n"
742
+ " --generate (-g) generate serial for current model\n"
743
+ " --generate-all (-a) generate serial for all models\n"
744
+ " --info <serial> (-i) decode serial information\n"
745
+ " --verify <mlb> verify MLB checksum\n"
746
+ " --list (-l) list known mac models\n"
747
+ " --list-products (-lp) list known product codes\n"
748
+ " --mlb <serial> generate MLB based on serial\n"
749
+ " --sys (-s) get system info\n\n"
750
+ "Tuning options:\n"
751
+ " --model <model> (-m) mac model used for generation\n"
752
+ " --num <num> (-n) number of generated pairs\n"
753
+ " --year <year> (-y) year used for generation\n"
754
+ " --week <week> (-w) week used for generation\n"
755
+ " --country <loc> (-c) country location used for generation\n"
756
+ " --copy <copy> (-o) production copy index\n"
757
+ " --line <line> (-e) production line\n"
758
+ " --platform <ppp> (-p) platform code used for generation\n\n", app);
759
+
760
+ return EXIT_FAILURE;
761
+ }
762
+
763
+ int main(int argc, char *argv[]) {
764
+ PROGRAMMODE mode = MODE_SYSTEM_INFO;
765
+ const char *passed_serial = NULL;
766
+ SERIALINFO info = {
767
+ .modelIndex = -1,
768
+ .decodedYear = -1,
769
+ .decodedWeek = -1,
770
+ .decodedCopy = -1,
771
+ .decodedLine = -1
772
+ };
773
+ int32_t limit = 10;
774
+
775
+ for (int i = 1; i < argc; i++) {
776
+ if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
777
+ usage(argv[0]);
778
+ return EXIT_SUCCESS;
779
+ } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
780
+ printf("ugrobator %s\n", PROGRAM_VERSION);
781
+ return EXIT_SUCCESS;
782
+ } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--generate")) {
783
+ mode = MODE_GENERATE_CURRENT;
784
+ } else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--generate-all")) {
785
+ mode = MODE_GENERATE_ALL;
786
+ } else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--info")) {
787
+ if (++i == argc) return usage(argv[0]);
788
+ mode = MODE_SERIAL_INFO;
789
+ passed_serial = argv[i];
790
+ } else if (!strcmp(argv[i], "--verify")) {
791
+ if (++i == argc) return usage(argv[0]);
792
+ mode = MODE_MLB_INFO;
793
+ passed_serial = argv[i];
794
+ } else if (!strcmp(argv[i], "-l") || !strcmp(argv[i], "--list")) {
795
+ mode = MODE_LIST_MODELS;
796
+ } else if (!strcmp(argv[i], "-lp") || !strcmp(argv[i], "--list-products")) {
797
+ mode = MODE_LIST_PRODUCTS;
798
+ } else if (!strcmp(argv[i], "-mlb") || !strcmp(argv[i], "--mlb")) {
799
+ // -mlb is supported due to legacy versions.
800
+ if (++i == argc) return usage(argv[0]);
801
+ mode = MODE_GENERATE_MLB;
802
+ passed_serial = argv[i];
803
+ } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--deriv")) {
804
+ if (++i == argc) return usage(argv[0]);
805
+ mode = MODE_GENERATE_DERIVATIVES;
806
+ passed_serial = argv[i];
807
+ } else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--sys")) {
808
+ mode = MODE_SYSTEM_INFO;
809
+ } else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--model")) {
810
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
811
+ if (++i == argc) return usage(argv[0]);
812
+ if (argv[i][0] >= '0' && argv[i][0] <= '9') {
813
+ info.modelIndex = atoi(argv[i]);
814
+ } else {
815
+ for (int32_t j = 0; j < APPLE_MODEL_MAX; j++) {
816
+ if (!strcmp(argv[i], ApplePlatformData[j].productName)) {
817
+ info.modelIndex = j;
818
+ break;
819
+ }
820
+ }
821
+ }
822
+ if (info.modelIndex < 0 || info.modelIndex > APPLE_MODEL_MAX) {
823
+ printf("Model id (%d) or name (%s) is out of valid range [0, %d]!\n", info.modelIndex, argv[i], APPLE_MODEL_MAX-1);
824
+ return EXIT_FAILURE;
825
+ }
826
+ } else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--num")) {
827
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
828
+ if (++i == argc) return usage(argv[0]);
829
+ limit = atoi(argv[i]);
830
+ if (limit <= 0) {
831
+ printf("Cannot generate %d pairs!\n", limit);
832
+ return EXIT_FAILURE;
833
+ }
834
+ } else if (!strcmp(argv[i], "-y") || !strcmp(argv[i], "--year")) {
835
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
836
+ if (++i == argc) return usage(argv[0]);
837
+ info.decodedYear = atoi(argv[i]);
838
+ if (info.decodedYear < SERIAL_YEAR_MIN || info.decodedYear > SERIAL_YEAR_MAX) {
839
+ printf("Year %d is out of valid range [%d, %d]!\n", info.decodedYear, SERIAL_YEAR_MIN, SERIAL_YEAR_MAX);
840
+ return EXIT_FAILURE;
841
+ }
842
+ } else if (!strcmp(argv[i], "-w") || !strcmp(argv[i], "--week")) {
843
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
844
+ if (++i == argc) return usage(argv[0]);
845
+ info.decodedWeek = atoi(argv[i]);
846
+ if (info.decodedWeek < SERIAL_WEEK_MIN || info.decodedWeek > SERIAL_WEEK_MAX) {
847
+ printf("Week %d is out of valid range [%d, %d]!\n", info.decodedWeek, SERIAL_WEEK_MIN, SERIAL_WEEK_MAX);
848
+ return EXIT_FAILURE;
849
+ }
850
+ } else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--country")) {
851
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
852
+ if (++i == argc) return usage(argv[0]);
853
+ size_t len = strlen(argv[i]);
854
+ if (len != COUNTRY_OLD_LEN && len != COUNTRY_NEW_LEN) {
855
+ printf("Country location %s is neither %d nor %d symbols long!\n", argv[i], COUNTRY_OLD_LEN, COUNTRY_NEW_LEN);
856
+ return EXIT_FAILURE;
857
+ }
858
+ strncpy(info.country, argv[i], COUNTRY_NEW_LEN+1);
859
+ } else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--platform")) {
860
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
861
+ if (++i == argc) return usage(argv[0]);
862
+ size_t len = strlen(argv[i]);
863
+ if (len != MODEL_CODE_OLD_LEN && len != MODEL_CODE_NEW_LEN) {
864
+ printf("Platform code %s is neither %d nor %d symbols long!\n", argv[i], MODEL_CODE_OLD_LEN, MODEL_CODE_NEW_LEN);
865
+ return EXIT_FAILURE;
866
+ }
867
+ strncpy(info.model, argv[i], MODEL_CODE_NEW_LEN+1);
868
+ } else if (!strcmp(argv[i], "-o") || !strcmp(argv[i], "--copy")) {
869
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
870
+ if (++i == argc) return usage(argv[0]);
871
+ info.decodedCopy = atoi(argv[i]);
872
+ if (info.decodedCopy < SERIAL_COPY_MIN || info.decodedCopy > SERIAL_COPY_MAX) {
873
+ printf("Copy %d is out of valid range [%d, %d]!\n", info.decodedCopy, SERIAL_COPY_MIN, SERIAL_COPY_MAX);
874
+ return EXIT_FAILURE;
875
+ }
876
+ } else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--line")) {
877
+ if (mode == MODE_SYSTEM_INFO) mode = MODE_GENERATE_CURRENT;
878
+ if (++i == argc) return usage(argv[0]);
879
+ info.decodedLine = atoi(argv[i]);
880
+ if (info.decodedLine < SERIAL_LINE_MIN || info.decodedLine > SERIAL_LINE_MAX) {
881
+ printf("Line %d is out of valid range [%d, %d]!\n", info.decodedLine, SERIAL_LINE_MIN, SERIAL_LINE_MAX);
882
+ return EXIT_FAILURE;
883
+ }
884
+ }
885
+ }
886
+
887
+ if (mode == MODE_SYSTEM_INFO) {
888
+ get_system_info();
889
+ } else if (mode == MODE_SERIAL_INFO) {
890
+ get_serial_info(passed_serial, &info, true);
891
+ } else if (mode == MODE_MLB_INFO) {
892
+ size_t len = strlen(passed_serial);
893
+ if (len == 13 || len == 17) {
894
+ printf("Valid MLB length: %s\n", len == 13 ? "legacy" : "modern");
895
+ } else {
896
+ printf("WARN: Invalid MLB length: %u\n", (unsigned) len);
897
+ }
898
+ if (verify_mlb_checksum(passed_serial, strlen(passed_serial))) {
899
+ printf("Valid MLB checksum.\n");
900
+ } else {
901
+ printf("WARN: Invalid MLB checksum!\n");
902
+ }
903
+ } else if (mode == MODE_LIST_MODELS) {
904
+ printf("Available models:\n");
905
+ for (int32_t j = 0; j < APPLE_MODEL_MAX; j++) {
906
+ printf("%14s: %s\n", "Model", ApplePlatformData[j].productName);
907
+ printf("%14s: ", "Prod years");
908
+ get_production_year((AppleModel)j, true);
909
+ printf("%14s: %s\n", "Base Serial", ApplePlatformData[j].serialNumber);
910
+ printf("%14s: ", "Model codes");
911
+ get_model_code((AppleModel)j, true);
912
+ printf("%14s: ", "Board codes");
913
+ get_board_code((AppleModel)j, true);
914
+ puts("");
915
+ }
916
+ printf("Available legacy location codes:\n");
917
+ for (size_t j = 0; j < ARRAY_SIZE(AppleLegacyLocations); j++)
918
+ printf(" - %s, %s\n", AppleLegacyLocations[j], AppleLegacyLocationNames[j]);
919
+ printf("\nAvailable new location codes:\n");
920
+ for (size_t j = 0; j < ARRAY_SIZE(AppleLocations); j++)
921
+ printf(" - %s, %s\n", AppleLocations[j], AppleLocationNames[j]);
922
+ puts("");
923
+ } else if (mode == MODE_LIST_PRODUCTS) {
924
+ for (size_t j = 0; j < ARRAY_SIZE(AppleModelDesc); j++)
925
+ printf("%4s - %s\n", AppleModelDesc[j].code, AppleModelDesc[j].name);
926
+ } else if (mode == MODE_GENERATE_MLB) {
927
+ if (get_serial_info(passed_serial, &info, false)) {
928
+ char mlb[MLB_MAX_SIZE];
929
+ get_mlb(&info, mlb, MLB_MAX_SIZE);
930
+ printf("%s\n", mlb);
931
+ }
932
+ } else if (mode == MODE_GENERATE_CURRENT) {
933
+ if (info.modelIndex < 0)
934
+ info.modelIndex = get_current_model();
935
+ for (int32_t i = 0; i < limit; i++) {
936
+ SERIALINFO tmp = info;
937
+ if (get_serial(&tmp)) {
938
+ char mlb[MLB_MAX_SIZE];
939
+ get_mlb(&tmp, mlb, MLB_MAX_SIZE);
940
+ printf("%s%s%s%s%s | %s\n", tmp.country, tmp.year, tmp.week, tmp.line, tmp.model, mlb);
941
+ }
942
+ }
943
+ } else if (mode == MODE_GENERATE_ALL) {
944
+ for (int32_t i = 0; i < APPLE_MODEL_MAX; i++) {
945
+ info.modelIndex = i;
946
+ for (int32_t j = 0; j < limit; j++) {
947
+ SERIALINFO tmp = info;
948
+ if (get_serial(&tmp)) {
949
+ char mlb[MLB_MAX_SIZE];
950
+ get_mlb(&tmp, mlb, MLB_MAX_SIZE);
951
+ printf("%14s | %s%s%s%s%s | %s\n", ApplePlatformData[info.modelIndex].productName,
952
+ tmp.country, tmp.year, tmp.week, tmp.line, tmp.model, mlb);
953
+ }
954
+ }
955
+ }
956
+ } else if (mode == MODE_GENERATE_DERIVATIVES) {
957
+ if (get_serial_info(passed_serial, &info, false)) {
958
+ int rmin = line_to_rmin(info.decodedLine);
959
+ for (int32_t k = 0; k < 34; k++) {
960
+ int32_t start = k * 68;
961
+ if (info.decodedLine > start && info.decodedLine - start <= SERIAL_LINE_REPR_MAX) {
962
+ int32_t rem = info.decodedLine - start;
963
+ printf("%s%s%s%c%c%c%s - copy %d\n", info.country, info.year, info.week, AppleBase34Reverse[k],
964
+ AppleBase34Reverse[rem / 34], AppleBase34Reverse[rem % 34], info.model, k - rmin + 1);
965
+ }
966
+ }
967
+ } else {
968
+ return EXIT_FAILURE;
969
+ }
970
+ }
971
+ }