1#!/usr/bin/env python
2import struct, sys
3
4
5"""
6Each timestamp field is an eight-byte value, first storing the time the
7file was loaded into the editor in DOS date/time format (see
8http://msdn.microsoft.com/en-us/library/ms724247(VS.85).aspx for details
9of this format), and then the length of time the file was loaded in the
10editor, in units of 1/18.2 second. (apparently this is fairly standard
11in DOS apps - I don't know)
12A new timestamp is written whenever the file is reloaded, but it is
13overwritten if the file is re-saved multiple times in the same editing
14session.
15
16Thanks to Saga Musix for finally figuring this crazy format out.
17"""
18
19for filename in sys.argv[1:]:
20        f = open(filename, 'rb')
21        if f.read(4) != 'IMPM'.encode('ascii'):
22                print("%s: not an IT file" % filename)
23                continue
24        f.seek(0x20)
25        ordnum, insnum, smpnum, patnum = struct.unpack('<4H', f.read(8))
26        f.seek(0x2e)
27        special, = struct.unpack('<H', f.read(2))
28        if not (special & 2):
29                print("%s: history flag not set (old IT version?)" % filename)
30                continue
31        para = []
32        if special & 1: # message exists
33                f.seek(0x36)
34                msglen, msgoff = struct.unpack('<HL', f.read(6))
35                if msglen:
36                        para.append(msgoff)
37        f.seek(0xc0 + ordnum)
38        # we'll assume history is invalid if it ends after the first parapointer
39        para_count = insnum + smpnum + patnum
40        para.extend(struct.unpack('<%sL' % para_count, f.read(4 * para_count)))
41        hist, = struct.unpack('<H', f.read(2))
42        hist_start = f.tell()
43        hist_len = hist_start + 8 * hist
44        try:
45                para_min = min(filter(None, para))
46        except ValueError:
47                para_min = hist_len
48        # history entry count follows parapointers
49        if not hist:
50                print("%s: history missing (probably not saved with IT)" % filename)
51                continue
52        if para_min < hist_len:
53                print("%s: history data overlaps parapointers (malformed?)" % filename)
54                continue
55        histdata = f.read(8 * hist)
56        if len(histdata) != 8 * hist:
57                print("%s: history malformed (probably not saved by IT)" % filename)
58                continue
59        f.close()
60        print(filename)
61        totalticks = 0
62        def ticks2hms(ticks):
63                secs = ticks / 18.2
64                h, m, s = int(secs / 3600), int(secs / 60) % 60, int(secs) % 60
65                return ''.join([('%dh' % h if h else ''), ('%dm' % m if h or m else ''), ('%ds' % s)])
66        for n in range(hist):
67                fatdate, fattime, ticks = struct.unpack('<HHL', histdata[8 * n : 8 * n + 8])
68                day = fatdate & 31
69                month = (fatdate >> 5) & 15
70                year = (fatdate >> 9) + 1980
71                second = (fattime & 31) * 2
72                minute = (fattime >> 5) & 63
73                hour = fattime >> 11
74                print('\t%04d-%02d-%02d %02d:%02d:%02d   %s' % (year, month, day, hour, minute, second, ticks2hms(ticks)))
75                totalticks += ticks
76        print("\t%13d ticks = %s" % (totalticks, ticks2hms(totalticks)))
77
78