Skip to content

Commit ef895ef

Browse files
committed
FEAT: bincode: added reading/writing for MSDOS date time formats (used for example in ZIP files)
The new dialect words: MSDOS-DATE, MSDOS-TIME and MSDOS-DATETIME
1 parent cdfe7a8 commit ef895ef

File tree

3 files changed

+171
-1
lines changed

3 files changed

+171
-1
lines changed

src/boot/words.r

+5-1
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,8 @@ window-cols
222222
window-rows
223223

224224
devices-in
225-
devices-out
225+
devices-out
226+
227+
msdos-datetime
228+
msdos-date
229+
msdos-time

src/core/u-bincode.c

+139
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,37 @@ float float16to32(float16_s f16) {
107107
return f32.v;
108108
}
109109

110+
#pragma pack(4)
111+
typedef union {
112+
u16 num;
113+
struct {
114+
u32 day : 5;
115+
u32 month : 4;
116+
u32 year : 7;
117+
} date;
118+
} ms_date;
119+
120+
typedef union {
121+
u16 num;
122+
struct {
123+
u32 second : 5;
124+
u32 minute : 6;
125+
u32 hour : 5;
126+
} time;
127+
} ms_time;
128+
129+
typedef union {
130+
u32 num;
131+
struct {
132+
u32 second : 5;
133+
u32 minute : 6;
134+
u32 hour : 5;
135+
u32 day : 5;
136+
u32 month : 4;
137+
u32 year : 7;
138+
} val;
139+
} ms_datetime;
140+
#pragma pack()
110141

111142
#define ASSERT_SI_RANGE(v, n) if (VAL_INT64(v) < (- (i64)n) || VAL_INT64(v) > (i64)n) Trap1(RE_OUT_OF_RANGE, v);
112143
#define ASSERT_UI_RANGE(v, n) if (VAL_INT32(v) > n) Trap1(RE_OUT_OF_RANGE, v);
@@ -204,8 +235,13 @@ static REBCNT EncodedU32_Size(u32 value) {
204235
REBCNT n, count, index, tail, tail_new;
205236
i32 i, len;
206237
u64 u;
238+
i64 si;
207239
u32 ulong;
208240

241+
ms_datetime* msdt = NULL;
242+
ms_date* msd = NULL;
243+
ms_time* mst = NULL;
244+
209245
REBVAL *value, *next;
210246
REBVAL *data;
211247
REBSER *blk = NULL; //used to store results of the read action
@@ -472,6 +508,24 @@ static REBCNT EncodedU32_Size(u32 value) {
472508
value--; //there is no argument so no next
473509
count += 4;
474510
break;
511+
512+
case SYM_MSDOS_DATE:
513+
if (!IS_DATE(next)) goto error; // only date allowed
514+
//continue..
515+
case SYM_MSDOS_TIME:
516+
if (IS_DATE(next) || IS_TIME(next)) {
517+
count += 2;
518+
continue;
519+
}
520+
goto error;
521+
522+
case SYM_MSDOS_DATETIME:
523+
if (IS_DATE(next) || IS_TIME(next)) {
524+
count += 4;
525+
continue;
526+
}
527+
goto error;
528+
475529
case SYM_RANDOM_BYTES:
476530
if (IS_INTEGER(next)) {
477531
count += VAL_INT32(next);
@@ -751,6 +805,52 @@ static REBCNT EncodedU32_Size(u32 value) {
751805
cp[0] = bp[3]; cp[1] = bp[2]; cp[2] = bp[1]; cp[3] = bp[0];
752806
#endif
753807
break;
808+
809+
case SYM_MSDOS_DATE:
810+
msd = (ms_date*)cp;
811+
msd->date.year = VAL_YEAR(next) - 1980;
812+
msd->date.month = VAL_MONTH(next);
813+
msd->date.day = VAL_DAY(next);
814+
n = 2;
815+
break;
816+
817+
case SYM_MSDOS_TIME:
818+
if (VAL_TIME(next) == NO_TIME) {
819+
cp[0] = 0;
820+
cp[1] = 0;
821+
} else {
822+
mst = (ms_time*)cp;
823+
si = VAL_TIME(next) / SEC_SEC;
824+
mst->time.hour = (u32)(si / 3600);
825+
si -= 3600 * (i64)mst->time.hour;
826+
mst->time.minute = (u32)(si / 60);
827+
si -= 60 * (i64)mst->time.minute;
828+
mst->time.second = (u32)si / 2; // this format has only 2 sec resolution!
829+
}
830+
n = 2;
831+
break;
832+
833+
case SYM_MSDOS_DATETIME:
834+
msdt = (ms_datetime*)cp;
835+
msdt->val.year = VAL_YEAR(next) - 1980;
836+
msdt->val.month = VAL_MONTH(next);
837+
msdt->val.day = VAL_DAY(next);
838+
if (VAL_TIME(next) == NO_TIME) {
839+
msdt->val.hour = 0;
840+
msdt->val.minute = 0;
841+
msdt->val.second = 0;
842+
}
843+
else {
844+
si = VAL_TIME(next) / SEC_SEC;
845+
msdt->val.hour = (u32)(si / 3600);
846+
si -= 3600 * (i64)msdt->val.hour;
847+
msdt->val.minute = (u32)(si / 60);
848+
si -= 60 * (i64)msdt->val.minute;
849+
msdt->val.second = (u32)si / 2; // this format has only 2 sec resolution!
850+
}
851+
n = 4;
852+
break;
853+
754854
case SYM_RANDOM_BYTES:
755855
if (IS_INTEGER(next)) {
756856
n = (REBCNT)VAL_INT32(next);
@@ -1269,6 +1369,45 @@ static REBCNT EncodedU32_Size(u32 value) {
12691369
VAL_SET(temp, REB_DECIMAL);
12701370
VAL_DECIMAL(temp) = ((float)i / 65536.0f);
12711371
break;
1372+
case SYM_MSDOS_DATETIME:
1373+
n = 4;
1374+
ASSERT_READ_SIZE(value, cp, ep, n);
1375+
msdt->num = ((u32)cp[0] << 0) |
1376+
((u32)cp[1] << 8) |
1377+
((u32)cp[2] << 16) |
1378+
((u32)cp[3] << 24) ;
1379+
VAL_SET (temp, REB_DATE);
1380+
VAL_YEAR (temp) = msdt->val.year + 1980;
1381+
VAL_MONTH(temp) = msdt->val.month;
1382+
VAL_DAY (temp) = msdt->val.day;
1383+
VAL_ZONE (temp) = 0;
1384+
VAL_TIME (temp) = TIME_SEC(msdt->val.second * 2
1385+
+ msdt->val.minute * 60
1386+
+ msdt->val.hour * 3600);
1387+
break;
1388+
case SYM_MSDOS_DATE:
1389+
n = 2;
1390+
ASSERT_READ_SIZE(value, cp, ep, n);
1391+
msd->num = ((u16)cp[0] << 0) |
1392+
((u16)cp[1] << 8) ;
1393+
CLEARS(temp);
1394+
VAL_SET (temp, REB_DATE);
1395+
VAL_YEAR (temp) = msd->date.year + 1980;
1396+
VAL_MONTH(temp) = msd->date.month;
1397+
VAL_DAY (temp) = msd->date.day;
1398+
VAL_TIME (temp) = NO_TIME;
1399+
break;
1400+
case SYM_MSDOS_TIME:
1401+
n = 2;
1402+
ASSERT_READ_SIZE(value, cp, ep, n);
1403+
mst->num = ((u16)cp[0] << 0) |
1404+
((u16)cp[1] << 8) ;
1405+
CLEARS(temp);
1406+
VAL_SET (temp, REB_TIME);
1407+
VAL_TIME (temp) = TIME_SEC(mst->time.second * 2 // this format has only 2 sec resolution!
1408+
+ mst->time.minute * 60
1409+
+ mst->time.hour * 3600);
1410+
break;
12721411

12731412
default:
12741413
Trap1(RE_INVALID_SPEC, value);

src/tests/units/bincode-test.r3

+27
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,33 @@ is-protected-error?: func[code][
323323
--assert 1.0 = binary/read #{0000803F} 'FLOAT
324324
--assert 1.0 = binary/read #{000000000000F03F} 'DOUBLE
325325

326+
--test-- "BinCode - MSDOS-DATETIME"
327+
;- old and not much precise format for storing date and time from MS-DOS times
328+
;- still used in some file formats, like ZIP
329+
330+
--assert 4-Apr-2018/18:53:56 = binary/read #{BC96844C} 'msdos-datetime
331+
--assert [18:53:56 4-Apr-2018] = binary/read #{BC96844C} [msdos-time msdos-date]
332+
333+
b: binary 64
334+
binary/write b [
335+
msdos-time 11:32:20
336+
msdos-time 21:23:55 ;<- will be stored as 21:23:54 (2sec resolution only)
337+
msdos-time 14-Mar-2019/15:29:52 ;<- only time is used
338+
msdos-time 14-Mar-2019 ;<- zero time is used
339+
340+
msdos-date 14-Mar-2019/15:29:52 ;<- only date is used
341+
msdos-datetime 14-Mar-2019/15:29:52
342+
msdos-datetime 14-Mar-2019/15:33:18+1:00
343+
]
344+
--assert 11:32:20 = binary/read b 'msdos-time
345+
--assert 21:23:54 = binary/read b 'msdos-time
346+
--assert 15:29:52 = binary/read b 'msdos-time
347+
--assert 0:0:0 = binary/read b 'msdos-time
348+
--assert 14-Mar-2019 = binary/read b 'msdos-date
349+
--assert 14-Mar-2019/15:29:52 = binary/read b 'msdos-datetime
350+
--assert 14-Mar-2019/14:33:18 = binary/read b 'msdos-datetime
351+
352+
--assert error? try [binary/write b [msdos-date 15:33:18]] ;<- date required
326353

327354
===end-group===
328355

0 commit comments

Comments
 (0)