|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
  
 
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #include "mac.h"  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
3
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 static int  | 
| 
4
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 get_macfileinfo(PerlIO *infile, char *file, HV *info)  | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 {  | 
| 
6
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   Buffer header;  | 
| 
7
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   char *bptr;  | 
| 
8
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   int32_t ret = 0;  | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   int32_t header_end;  | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   mac_streaminfo *si;  | 
| 
12
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   Newz(0, si, sizeof(mac_streaminfo), mac_streaminfo);  | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   /*  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     There are two possible variations here.  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     1.  There's an ID3V2 tag present at the beginning of the file  | 
| 
17
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     2.  There's an APE tag present at the beginning of the file  | 
| 
18
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
         (deprecated, but still possible)  | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     For each type of tag, check for existence and then skip it before  | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     looking for the MPC header  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   */  | 
| 
22
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if ((header_end = skip_id3v2(infile)) < 0) {  | 
| 
23
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't skip ID3v2]: %s\n", file);  | 
| 
24
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     Safefree(si);  | 
| 
25
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return -1;  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   // seek to first byte of MAC data  | 
| 
29
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (PerlIO_seek(infile, header_end, SEEK_SET) < 0) {  | 
| 
30
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't seek to offset %d]: %s\n", header_end, file);  | 
| 
31
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     Safefree(si);  | 
| 
32
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return -1;  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
35
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   // Offset + MAC. Does this need the space as well, to be +4 ?  | 
| 
36
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   si->audio_start_offset = PerlIO_tell(infile) + 3;  | 
| 
37
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   // Skip the APETAGEX if it exists.  | 
| 
39
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   buffer_init(&header, APE_HEADER_LEN);  | 
| 
40
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
41
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (!_check_buf(infile, &header, APE_HEADER_LEN, APE_HEADER_LEN)) {  | 
| 
42
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read tag header]: %s\n", file);  | 
| 
43
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     goto out;  | 
| 
44
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
46
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   bptr = buffer_ptr(&header);  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
48
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (memcmp(bptr, "APETAGEX", 8) == 0) {  | 
| 
49
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     // Skip the ape tag structure  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     // XXXX - need to test this code path.  | 
| 
51
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header);  | 
| 
52
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     PerlIO_seek(infile, buffer_get_int_le(&header), SEEK_CUR);  | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   } else {  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     // set the pointer back to original location  | 
| 
56
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     PerlIO_seek(infile, -APE_HEADER_LEN, SEEK_CUR);  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
59
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   buffer_clear(&header);  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
61
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (!_check_buf(infile, &header, 32, 32)) {  | 
| 
62
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read stream header]: %s\n", file);  | 
| 
63
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     goto out;  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
66
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   bptr = buffer_ptr(&header);  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
68
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (memcmp(bptr, "MAC ", 4) != 0) {  | 
| 
69
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't couldn't find stream header]: %s\n", file);  | 
| 
70
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     goto out;  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
73
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   buffer_consume(&header, 4);  | 
| 
74
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   si->version = buffer_get_short_le(&header);  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
76
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (si->version < 3980) {  | 
| 
77
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     uint16_t compression_id = buffer_get_short_le(&header);  | 
| 
78
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (compression_id % 1000) {  | 
| 
79
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       si->compression = "";  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
82
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       si->compression = mac_profile_names[ compression_id / 1000 ];  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
85
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (!_check_buf(infile, &header, MAC_397_HEADER_LEN, MAC_397_HEADER_LEN)) {  | 
| 
86
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read < 3.98 stream header]: %s\n", file);  | 
| 
87
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       goto out;  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
90
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_consume(&header, 2); // flags  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
92
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->channels = buffer_get_short_le(&header);  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
94
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->sample_rate = buffer_get_int_le(&header);  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
96
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_consume(&header, 4); // header size  | 
| 
97
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_consume(&header, 4); // terminating data bytes  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
99
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->total_frames      = buffer_get_int_le(&header);  | 
| 
100
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->final_frame       = buffer_get_int_le(&header);  | 
| 
101
 | 
  
0
  
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->blocks_per_frame  = si->version >= 3950 ? (73728 * 4) : 73728;  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   } else {  | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     unsigned char md5[16];  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     uint16_t profile;  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
107
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (!_check_buf(infile, &header, MAC_398_HEADER_LEN, MAC_398_HEADER_LEN)) {  | 
| 
108
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read > 3.98 stream header]: %s\n", file);  | 
| 
109
 | 
  
0
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       goto out;  | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
111
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
112
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_consume(&header, 2);  | 
| 
113
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     // unused.  | 
| 
115
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header); // desc bytes  | 
| 
116
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header); // header bytes  | 
| 
117
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header); // seek table bytes  | 
| 
118
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header); // header data bytes  | 
| 
119
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header); // ape frame data bytes  | 
| 
120
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header); // ape frame data bytes high  | 
| 
121
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_int_le(&header); // terminating data bytes  | 
| 
122
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get(&header, &md5, sizeof(md5));  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     // Header block  | 
| 
125
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     profile = buffer_get_short_le(&header);  | 
| 
126
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if (profile % 1000) {  | 
| 
127
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       si->compression = "";  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     else {  | 
| 
130
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
       si->compression = mac_profile_names[ profile / 1000 ];  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
132
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
133
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     buffer_get_short_le(&header); // flags  | 
| 
134
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
135
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->blocks_per_frame  = buffer_get_int_le(&header);  | 
| 
136
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->final_frame       = buffer_get_int_le(&header);  | 
| 
137
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->total_frames      = buffer_get_int_le(&header);  | 
| 
138
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->bits              = buffer_get_short_le(&header);  | 
| 
139
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->channels          = buffer_get_short_le(&header);  | 
| 
140
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     si->sample_rate       = buffer_get_int_le(&header);  | 
| 
141
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
142
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
143
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   si->file_size = _file_size(infile);  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
145
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   if (si->sample_rate) {  | 
| 
146
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     double total_samples = (double)(((si->blocks_per_frame * (si->total_frames - 1)) + si->final_frame));  | 
| 
147
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     uint32_t total_ms = (total_samples * 1000) / si->sample_rate;  | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
149
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "samplerate", newSViv(si->sample_rate));  | 
| 
150
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "channels", newSViv(si->channels));  | 
| 
151
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "song_length_ms", newSVuv(total_ms));  | 
| 
152
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "bitrate", newSVuv( _bitrate(si->file_size - si->audio_start_offset, total_ms) ));  | 
| 
153
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
154
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "file_size", newSVnv(si->file_size));  | 
| 
155
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "audio_offset", newSVuv(si->audio_start_offset));  | 
| 
156
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "audio_size", newSVuv(si->file_size - si->audio_start_offset));  | 
| 
157
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "compression", newSVpv(si->compression, 0));  | 
| 
158
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my_hv_store(info, "version", newSVpvf( "%0.2f", si->version * 1.0 / 1000 ) );  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   }  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 out:  | 
| 
162
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   buffer_free(&header);  | 
| 
163
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   Safefree(si);  | 
| 
164
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
165
 | 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
   return ret;  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  |