1 /* wvWare
2  * Copyright (C) Caolan McNamara, Dom Lachowicz, and others
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17  * 02111-1307, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <time.h>
27 #include "wv.h"
28 
29 void
wvGetFILETIME(FILETIME * ft,wvStream * fd)30 wvGetFILETIME (FILETIME * ft, wvStream * fd)
31 {
32     ft->dwLowDateTime = read_32ubit (fd);
33     ft->dwHighDateTime = read_32ubit (fd);
34 }
35 
36 void
wvInitFILETIME(FILETIME * ft)37 wvInitFILETIME (FILETIME * ft)
38 {
39     ft->dwLowDateTime = 0;
40     ft->dwHighDateTime = 0;
41 }
42 
43 int
wvFileTimeToDosDateTime(const FILETIME * ft,U16 * fatdate,U16 * fattime)44 wvFileTimeToDosDateTime (const FILETIME * ft, U16 * fatdate, U16 * fattime)
45 {
46     time_t unixtime = wvDOSFS_FileTimeToUnixTime (ft, NULL);
47     struct tm *tm = localtime (&unixtime);
48     if (fattime)
49 	*fattime = (tm->tm_hour << 11) + (tm->tm_min << 5) + (tm->tm_sec / 2);
50     if (fatdate)
51 	*fatdate = ((tm->tm_year - 80) << 9) + ((tm->tm_mon + 1) << 5)
52 	    + tm->tm_mday;
53     return (1);
54 }
55 
56 
57 /***********************************************************************
58  *           DOSFS_FileTimeToUnixTime
59  *
60  * Convert a FILETIME format to Unix time.
61  * If not NULL, 'remainder' contains the fractional part of the filetime,
62  * in the range of [0..9999999] (even if time_t is negative).
63  */
64 time_t
wvDOSFS_FileTimeToUnixTime(const FILETIME * filetime,U32 * remainder)65 wvDOSFS_FileTimeToUnixTime (const FILETIME * filetime, U32 * remainder)
66 {
67     U32 a0;			/* 16 bit, low    bits */
68     U32 a1;			/* 16 bit, medium bits */
69     U32 a2;			/* 32 bit, high   bits */
70     U32 r;			/* remainder of division */
71     unsigned int carry;		/* carry bit for subtraction */
72     int negative;		/* whether a represents a negative value */
73 
74     /* Copy the time values to a2/a1/a0 */
75     a2 = filetime->dwHighDateTime;
76     a1 = ((U32) filetime->dwLowDateTime) >> 16;
77     a0 = ((U32) filetime->dwLowDateTime) & 0xffff;
78 
79     /* Subtract the time difference */
80     if (a0 >= 32768)
81 	a0 -= 32768, carry = 0;
82     else
83 	a0 += (1 << 16) - 32768, carry = 1;
84 
85     if (a1 >= 54590 + carry)
86 	a1 -= 54590 + carry, carry = 0;
87     else
88 	a1 += (1 << 16) - 54590 - carry, carry = 1;
89 
90     a2 -= 27111902 + carry;
91 
92     /* If a is negative, replace a by (-1-a) */
93     negative = (a2 >= ((U32) 1) << 31);
94     if (negative)
95       {
96 	  /* Set a to -a - 1 (a is a2/a1/a0) */
97 	  a0 = 0xffff - a0;
98 	  a1 = 0xffff - a1;
99 	  a2 = ~a2;
100       }
101 
102     /* Divide a by 10000000 (a = a2/a1/a0), put the rest into r.
103        Split the divisor into 10000 * 1000 which are both less than 0xffff. */
104     a1 += (a2 % 10000) << 16;
105     a2 /= 10000;
106     a0 += (a1 % 10000) << 16;
107     a1 /= 10000;
108     r = a0 % 10000;
109     a0 /= 10000;
110 
111     a1 += (a2 % 1000) << 16;
112     a2 /= 1000;
113     a0 += (a1 % 1000) << 16;
114     a1 /= 1000;
115     r += (a0 % 1000) * 10000;
116     a0 /= 1000;
117 
118     /* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
119     if (negative)
120       {
121 	  /* Set a to -a - 1 (a is a2/a1/a0) */
122 	  a0 = 0xffff - a0;
123 	  a1 = 0xffff - a1;
124 	  a2 = ~a2;
125 
126 	  r = 9999999 - r;
127       }
128 
129     if (remainder)
130 	*remainder = r;
131 /* Do not replace this by << 32, it gives a compiler warning and it does
132        not work. */
133     return ((((time_t) a2) << 16) << 16) + (a1 << 16) + a0;
134 
135 }
136