3 * https://github.com/fetisov/emfat/blob/master/project/emfat.c
4 * version: 1.1 (2.04.2017)
8 * The MIT License (MIT)
10 * Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
12 * Permission is hereby granted, free of charge, to any person obtaining a copy
13 * of this software and associated documentation files (the "Software"), to deal
14 * in the Software without restriction, including without limitation the rights
15 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 * copies of the Software, and to permit persons to whom the Software is
17 * furnished to do so, subject to the following conditions:
19 * The above copyright notice and this permission notice shall be included in all
20 * copies or substantial portions of the Software.
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
33 #include "common/utils.h"
43 #define SECT_PER_CLUST (CLUST / SECT)
44 #define SIZE_TO_NSECT(s) ((s) == 0 ? 1 : ((s) + SECT - 1) / SECT)
45 #define SIZE_TO_NCLUST(s) ((s) == 0 ? 1 : ((s) + CLUST - 1) / CLUST)
47 #define CLUST_FREE 0x00000000
48 #define CLUST_RESERVED 0x00000001
49 #define CLUST_BAD 0x0FFFFFF7
50 #define CLUST_ROOT_END 0X0FFFFFF8
51 #define CLUST_EOF 0x0FFFFFFF
53 #define MAX_DIR_ENTRY_CNT 16
54 #define FILE_SYS_TYPE_OFF 82
55 #define BYTES_PER_SEC_OFF 11
56 #define SEC_PER_CLUS_OFF 13
57 #define RES_SEC_CNT_OFF 14
58 #define FAT_CNT_OFF 16
59 #define TOT_SEC_CNT_OFF 32
60 #define SEC_PER_FAT 36
61 #define ROOT_DIR_STRT_CLUS_OFF 44
62 #define FS_INFOSECTOR_OFF 48
63 #define BACKUP_BOOT_SEC_OFF 50
64 #define NXT_FREE_CLUS_OFF 492
65 #define FILE_SYS_TYPE_LENGTH 8
66 #define SHRT_FILE_NAME_LEN 11
67 #define STRT_CLUS_LOW_OFF 26
68 #define STRT_CLUS_HIGH_OFF 20
69 #define FILE_SIZE_OFF 28
71 #define FILE_STAT_LEN 21
72 #define CHECK_SUM_OFF 13
73 #define FILE_NAME_SHRT_LEN 8
74 #define FILE_NAME_EXTN_LEN 3
75 #define LONG_FILE_NAME_LEN 255
76 #define LOW_CLUSWORD_MASK 0x0000FFFF
77 #define HIGH_CLUSWORD_MASK 0xFFFF0000
78 #define LONG_FNAME_MASK 0x0F
79 #define LAST_ORD_FIELD_SEQ 0x40
80 #define LFN_END_MARK 0xFFFF
81 #define LFN_TERM_MARK 0x0000
82 #define LFN_FIRST_OFF 0x01
83 #define LFN_SIXTH_OFF 0x0E
84 #define LFN_TWELVETH_OFF 0x1C
85 #define LFN_FIRST_SET_CNT 5
86 #define LFN_SEC_SET_CNT 6
87 #define LFN_THIRD_SET_CNT 2
88 #define LFN_FIRST_SET_LEN 10
89 #define LFN_SEC_SET_LEN 12
90 #define LFN_THIRD_SET_LEN 4
91 #define LFN_EMPTY_LEN 2
92 #define LFN_LEN_PER_ENTRY 13
93 #define FNAME_EXTN_SEP_OFF 6
94 #define FNAME_SEQ_NUM_OFF 7
95 #define BYTES_PER_CLUSTER_ENTRY 4
96 #define DIR_ENTRY_LEN 32
98 #define VOL_LABEL_LEN 11
101 #define OEM_NAME_LEN 8
102 #define JUMP_INS_LEN 3
103 #define MAX_FAT_CNT 2
105 #define FILE_READ 0x01
106 #define FILE_WRITE 0X02
107 #define FILE_CREATE_NEW 0x04
108 #define FILE_CREATE_ALWAYS 0x08
109 #define FILE_APPEND 0x10
110 #define FREE_DIR_ENTRY 0x00
111 #define DEL_DIR_ENTRY 0xE5
112 #define DOT_DIR_ENTRY 0x2E
113 #define ASCII_DIFF 32
114 #define FILE_SEEK_SET 0
115 #define FILE_SEEK_CUR 1
116 #define FILE_SEEK_END 2
117 #define DELIMITER '/'
118 #define EXTN_DELIMITER '.'
120 #define FULL_SHRT_NAME_LEN 13
122 #pragma pack(push, 1)
126 uint8_t status
; // 0x80 for bootable, 0x00 for not bootable, anything else for invalid
127 uint8_t start_head
; // The head of the start
128 uint8_t start_sector
; // (S | ((C >> 2) & 0xC0)) where S is the sector of the start and C is the cylinder of the start. Note that S is counted from one.
129 uint8_t start_cylinder
; // (C & 0xFF) where C is the cylinder of the start
133 uint8_t end_cylinder
;
134 uint32_t StartLBA
; // linear address of first sector in partition. Multiply by sector size (usually 512) for real offset
135 uint32_t EndLBA
; // linear address of last sector in partition. Multiply by sector size (usually 512) for real offset
141 uint32_t DiskSig
; //This is optional
142 uint16_t Reserved
; //Usually 0x0000
143 mbr_part_t PartTable
[4];
144 uint8_t BootSignature
[2]; //0x55 0xAA for bootable
149 uint8_t jump
[JUMP_INS_LEN
];
150 uint8_t OEM_name
[OEM_NAME_LEN
];
151 uint16_t bytes_per_sec
;
152 uint8_t sec_per_clus
;
153 uint16_t reserved_sec_cnt
;
155 uint16_t root_dir_max_cnt
;
156 uint16_t tot_sectors
;
158 uint16_t sec_per_fat_fat16
;
159 uint16_t sec_per_track
;
160 uint16_t number_of_heads
;
161 uint32_t hidden_sec_cnt
;
162 uint32_t tol_sector_cnt
;
163 uint32_t sectors_per_fat
;
165 uint8_t fs_version
[FS_VER_LEN
];
166 uint32_t root_dir_strt_cluster
;
167 uint16_t fs_info_sector
;
168 uint16_t backup_boot_sector
;
169 uint8_t reserved
[RESERV_LEN
];
170 uint8_t drive_number
;
173 uint8_t volume_id
[VOL_ID_LEN
];
174 uint8_t volume_label
[VOL_LABEL_LEN
];
175 uint8_t file_system_type
[FILE_SYS_TYPE_LENGTH
];
180 uint32_t signature1
; /* 0x41615252L */
181 uint32_t reserved1
[120]; /* Nothing as far as I can tell */
182 uint32_t signature2
; /* 0x61417272L */
183 uint32_t free_clusters
; /* Free cluster count. -1 if unknown */
184 uint32_t next_cluster
; /* Most recently allocated cluster */
185 uint32_t reserved2
[3];
191 uint8_t name
[FILE_NAME_SHRT_LEN
];
192 uint8_t extn
[FILE_NAME_EXTN_LEN
];
195 uint8_t crt_time_tenth
;
198 uint16_t lst_access_date
;
199 uint16_t strt_clus_hword
;
200 uint16_t lst_mod_time
;
201 uint16_t lst_mod_date
;
202 uint16_t strt_clus_lword
;
209 uint8_t fname0_4
[LFN_FIRST_SET_LEN
];
213 uint8_t fname6_11
[LFN_SEC_SET_LEN
];
214 uint8_t empty
[LFN_EMPTY_LEN
];
215 uint8_t fname12_13
[LFN_THIRD_SET_LEN
];
220 bool emfat_init_entries(emfat_entry_t
*entries
)
226 if (e
->level
!= 0 || !e
->dir
|| e
->name
== NULL
) return false;
231 e
->priv
.num_subentry
= 0;
234 for (i
= 1; entries
[i
].name
!= NULL
; i
++) {
235 entries
[i
].priv
.top
= NULL
;
236 entries
[i
].priv
.next
= NULL
;
237 entries
[i
].priv
.sub
= NULL
;
238 entries
[i
].priv
.num_subentry
= 0;
239 if (entries
[i
].level
== n
- 1) {
240 if (n
== 0) return false;
245 if (entries
[i
].level
== n
+ 1) {
246 if (!e
->dir
) return false;
247 e
->priv
.sub
= &entries
[i
];
248 entries
[i
].priv
.top
= e
;
254 if (entries
[i
].level
== n
) {
255 if (n
== 0) return false;
256 e
->priv
.top
->priv
.num_subentry
++;
257 entries
[i
].priv
.top
= e
->priv
.top
;
258 e
->priv
.next
= &entries
[i
];
269 static void lba_to_chs(int lba
, uint8_t *cl
, uint8_t *ch
, uint8_t *dh
)
271 int cylinder
, head
, sector
;
274 int cylinders
= 1024;
275 sector
= lba
% sectors
+ 1;
276 head
= (lba
/ sectors
) % heads
;
277 cylinder
= lba
/ (sectors
* heads
);
278 if (cylinder
>= cylinders
) {
279 *cl
= *ch
= *dh
= 0xff;
282 *cl
= sector
| ((cylinder
& 0x300) >> 2);
283 *ch
= cylinder
& 0xFF;
287 bool emfat_init(emfat_t
*emfat
, const char *label
, emfat_entry_t
*entries
)
289 uint32_t sect_per_fat
;
291 uint32_t reserved_clust
= 0;
295 if (emfat
== NULL
|| label
== NULL
|| entries
== NULL
) {
299 if (!emfat_init_entries(entries
)) {
304 for (i
= 0; entries
[i
].name
!= NULL
; i
++) {
309 e
->priv
.first_clust
= clust
;
310 e
->priv
.last_clust
= clust
+ SIZE_TO_NCLUST(e
->priv
.num_subentry
* sizeof(dir_entry
)) - 1;
311 e
->priv
.last_reserved
= e
->priv
.last_clust
;
313 e
->priv
.first_clust
= clust
;
314 e
->priv
.last_clust
= e
->priv
.first_clust
+ SIZE_TO_NCLUST(entries
[i
].curr_size
) - 1;
315 e
->priv
.last_reserved
= e
->priv
.first_clust
+ SIZE_TO_NCLUST(entries
[i
].max_size
) - 1;
317 reserved_clust
+= e
->priv
.last_reserved
- e
->priv
.last_clust
;
318 clust
= e
->priv
.last_reserved
+ 1;
322 emfat
->vol_label
= label
;
323 emfat
->priv
.num_entries
= i
;
324 emfat
->priv
.boot_lba
= 62;
325 emfat
->priv
.fsinfo_lba
= emfat
->priv
.boot_lba
+ 1;
326 emfat
->priv
.fat1_lba
= emfat
->priv
.fsinfo_lba
+ 1;
327 emfat
->priv
.num_clust
= clust
;
328 emfat
->priv
.free_clust
= reserved_clust
;
329 sect_per_fat
= SIZE_TO_NSECT((uint64_t)emfat
->priv
.num_clust
* 4);
330 emfat
->priv
.fat2_lba
= emfat
->priv
.fat1_lba
+ sect_per_fat
;
331 emfat
->priv
.root_lba
= emfat
->priv
.fat2_lba
+ sect_per_fat
;
332 emfat
->priv
.entries
= entries
;
333 emfat
->priv
.last_entry
= entries
;
334 emfat
->disk_sectors
= clust
* SECT_PER_CLUST
+ emfat
->priv
.root_lba
;
335 emfat
->vol_size
= (uint64_t)emfat
->disk_sectors
* SECT
;
336 /* calc cyl number */
337 // i = ((emfat->disk_sectors + 63*255 - 1) / (63*255));
338 // emfat->disk_sectors = i * 63*255;
342 void read_mbr_sector(const emfat_t
*emfat
, uint8_t *sect
)
345 memset(sect
, 0, SECT
);
349 mbr
->PartTable
[0].status
= 0x80;
350 mbr
->PartTable
[0].PartType
= 0x0C;
351 mbr
->PartTable
[0].StartLBA
= emfat
->priv
.boot_lba
;
352 mbr
->PartTable
[0].EndLBA
= emfat
->disk_sectors
;
353 lba_to_chs(mbr
->PartTable
[0].StartLBA
, &mbr
->PartTable
[0].start_sector
, &mbr
->PartTable
[0].start_cylinder
, &mbr
->PartTable
[0].start_head
);
354 lba_to_chs(emfat
->disk_sectors
- 1, &mbr
->PartTable
[0].end_sector
, &mbr
->PartTable
[0].end_cylinder
, &mbr
->PartTable
[0].end_head
);
355 mbr
->BootSignature
[0] = 0x55;
356 mbr
->BootSignature
[1] = 0xAA;
359 void read_boot_sector(const emfat_t
*emfat
, uint8_t *sect
)
362 memset(sect
, 0, SECT
);
363 bs
= (boot_sector
*)sect
;
367 memcpy(bs
->OEM_name
, "MSDOS5.0", 8);
368 bs
->bytes_per_sec
= SECT
;
369 bs
->sec_per_clus
= 8; /* 4 kb per cluster */
370 bs
->reserved_sec_cnt
= 2; /* boot sector & fsinfo sector */
371 bs
->fat_cnt
= 2; /* two tables */
372 bs
->root_dir_max_cnt
= 0;
374 bs
->media_desc
= 0xF8;
375 bs
->sec_per_fat_fat16
= 0;
376 bs
->sec_per_track
= 63;
377 bs
->number_of_heads
= 0xFF;
378 bs
->hidden_sec_cnt
= 62;
379 bs
->tol_sector_cnt
= emfat
->disk_sectors
- emfat
->priv
.boot_lba
;
380 bs
->sectors_per_fat
= emfat
->priv
.fat2_lba
- emfat
->priv
.fat1_lba
;
382 bs
->fs_version
[0] = 0;
383 bs
->fs_version
[1] = 0;
384 bs
->root_dir_strt_cluster
= 2;
385 bs
->fs_info_sector
= 1;
386 bs
->backup_boot_sector
= 0; /* not used */
387 bs
->drive_number
= 128;
389 bs
->volume_id
[0] = 148;
390 bs
->volume_id
[1] = 14;
391 bs
->volume_id
[2] = 13;
392 bs
->volume_id
[3] = 8;
393 memcpy(bs
->volume_label
, "NO NAME ", VOL_LABEL_LEN
);
394 memcpy(bs
->file_system_type
, "FAT32 ", FILE_SYS_TYPE_LENGTH
);
395 sect
[SECT
- 2] = 0x55;
396 sect
[SECT
- 1] = 0xAA;
399 #define IS_CLUST_OF(clust, entry) ((clust) >= (entry)->priv.first_clust && (clust) <= (entry)->priv.last_reserved)
401 emfat_entry_t
*find_entry(const emfat_t
*emfat
, uint32_t clust
, emfat_entry_t
*nearest
)
403 if (nearest
== NULL
) {
404 nearest
= emfat
->priv
.entries
;
407 if (nearest
->priv
.first_clust
> clust
) {
408 while (nearest
>= emfat
->priv
.entries
) { // backward finding
409 if (IS_CLUST_OF(clust
, nearest
))
414 while (nearest
->name
!= NULL
) { // forward finding
415 if (IS_CLUST_OF(clust
, nearest
))
423 void read_fsinfo_sector(const emfat_t
*emfat
, uint8_t *sect
)
427 fsinfo_t
*info
= (fsinfo_t
*)sect
;
428 info
->signature1
= 0x41615252L
;
429 info
->signature2
= 0x61417272L
;
430 //info->free_clusters = 0;
431 info
->free_clusters
= emfat
->priv
.free_clust
;
432 //info->next_cluster = emfat->priv.num_clust + 2;
433 info
->next_cluster
= 0xffffffff;
434 memset(info
->reserved1
, 0, sizeof(info
->reserved1
));
435 memset(info
->reserved2
, 0, sizeof(info
->reserved2
));
436 info
->signature3
= 0xAA550000;
439 void read_fat_sector(emfat_t
*emfat
, uint8_t *sect
, uint32_t index
)
446 values
= (uint32_t *)sect
;
451 *values
++ = CLUST_ROOT_END
;
452 *values
++ = 0xFFFFFFFF;
457 le
= emfat
->priv
.last_entry
;
459 if (!IS_CLUST_OF(curr
, le
)) {
460 le
= find_entry(emfat
, curr
, le
);
462 le
= emfat
->priv
.last_entry
;
463 *values
= CLUST_RESERVED
;
471 if (curr
== le
->priv
.last_clust
) {
477 if (curr
== le
->priv
.last_clust
) {
479 } else if (curr
> le
->priv
.last_clust
) {
480 *values
= CLUST_FREE
;
489 emfat
->priv
.last_entry
= le
;
492 void fill_entry(dir_entry
*entry
, const char *name
, uint8_t attr
, uint32_t clust
, const uint32_t cma
[3], uint32_t size
)
497 memset(entry
, 0, sizeof(dir_entry
));
500 entry
->crt_date
= cma
[0] >> 16;
501 entry
->crt_time
= cma
[0] & 0xFFFF;
502 entry
->lst_mod_date
= cma
[1] >> 16;
503 entry
->lst_mod_time
= cma
[1] & 0xFFFF;
504 entry
->lst_access_date
= cma
[2] >> 16;
510 if ((attr
& ATTR_DIR
) == 0) {
511 for (i
= l
- 1; i
>= 0; i
--) {
521 l1
= l
> FILE_NAME_SHRT_LEN
? FILE_NAME_SHRT_LEN
: l
;
525 l1
= l1
> FILE_NAME_SHRT_LEN
? FILE_NAME_SHRT_LEN
: l1
;
526 l2
= l
- dot_pos
- 1;
527 l2
= l2
> FILE_NAME_EXTN_LEN
? FILE_NAME_EXTN_LEN
: l2
;
530 memset(entry
->name
, ' ', FILE_NAME_SHRT_LEN
);
531 memcpy(entry
->name
, name
, l1
);
532 memset(entry
->extn
, ' ', FILE_NAME_EXTN_LEN
);
533 memcpy(entry
->extn
, name
+ dot_pos
+ 1, l2
);
535 for (i
= 0; i
< FILE_NAME_SHRT_LEN
; i
++) {
536 if (entry
->name
[i
] >= 'a' && entry
->name
[i
] <= 'z') {
537 entry
->name
[i
] -= 0x20;
541 for (i
= 0; i
< FILE_NAME_EXTN_LEN
; i
++) {
542 if (entry
->extn
[i
] >= 'a' && entry
->extn
[i
] <= 'z') {
543 entry
->extn
[i
] -= 0x20;
548 entry
->reserved
= 24;
549 entry
->strt_clus_hword
= clust
>> 16;
550 entry
->strt_clus_lword
= clust
;
556 void fill_dir_sector(emfat_t
*emfat
, uint8_t *data
, emfat_entry_t
*entry
, uint32_t rel_sect
)
561 memset(data
, 0, SECT
);
562 de
= (dir_entry
*)data
;
565 if (rel_sect
== 0) { // 1. first sector of directory
566 if (entry
->priv
.top
== NULL
) {
567 fill_entry(de
++, emfat
->vol_label
, ATTR_VOL_LABEL
, 0, 0, 0);
568 avail
-= sizeof(dir_entry
);
570 fill_entry(de
++, ".", ATTR_DIR
| ATTR_READ
, entry
->priv
.first_clust
, 0, 0);
571 if (entry
->priv
.top
->priv
.top
== NULL
) {
572 fill_entry(de
++, "..", ATTR_DIR
| ATTR_READ
, 0, 0, 0);
574 fill_entry(de
++, "..", ATTR_DIR
| ATTR_READ
, entry
->priv
.top
->priv
.first_clust
, 0, 0);
576 avail
-= sizeof(dir_entry
) * 2;
578 entry
= entry
->priv
.sub
;
579 } else { // 2. not a first sector
581 n
= rel_sect
* (SECT
/ sizeof(dir_entry
));
582 n
-= entry
->priv
.top
== NULL
? 1 : 2;
583 entry
= entry
->priv
.sub
;
585 while (n
> 0 && entry
!= NULL
) {
586 entry
= entry
->priv
.next
;
591 while (entry
!= NULL
&& avail
>= sizeof(dir_entry
)) {
593 fill_entry(de
++, entry
->name
, ATTR_DIR
| ATTR_READ
, entry
->priv
.first_clust
, entry
->cma_time
, 0);
595 //fill_entry(de++, entry->name, ATTR_ARCHIVE | ATTR_READ, entry->priv.first_clust, entry->cma_time, entry->curr_size);
596 fill_entry(de
++, entry
->name
, ATTR_ARCHIVE
| ATTR_READ
| entry
->attr
, entry
->priv
.first_clust
, entry
->cma_time
, entry
->curr_size
);
598 entry
= entry
->priv
.next
;
599 avail
-= sizeof(dir_entry
);
603 void read_data_sector(emfat_t
*emfat
, uint8_t *data
, uint32_t rel_sect
)
607 cluster
= rel_sect
/ 8 + 2;
608 rel_sect
= rel_sect
% 8;
610 le
= emfat
->priv
.last_entry
;
611 if (!IS_CLUST_OF(cluster
, le
)) {
612 le
= find_entry(emfat
, cluster
, le
);
615 for (i
= 0; i
< SECT
/ 4; i
++)
616 ((uint32_t *)data
)[i
] = 0xEFBEADDE;
619 emfat
->priv
.last_entry
= le
;
623 fill_dir_sector(emfat
, data
, le
, rel_sect
);
627 if (le
->readcb
== NULL
) {
628 memset(data
, 0, SECT
);
630 uint32_t offset
= cluster
- le
->priv
.first_clust
;
631 offset
= offset
* CLUST
+ rel_sect
* SECT
;
632 le
->readcb(data
, SECT
, offset
+ le
->offset
, le
);
638 void emfat_read(emfat_t
*emfat
, uint8_t *data
, uint32_t sector
, int num_sectors
)
640 while (num_sectors
> 0) {
641 if (sector
>= emfat
->priv
.root_lba
) {
642 read_data_sector(emfat
, data
, sector
- emfat
->priv
.root_lba
);
643 } else if (sector
== 0) {
644 read_mbr_sector(emfat
, data
);
645 } else if (sector
== emfat
->priv
.fsinfo_lba
) {
646 read_fsinfo_sector(emfat
, data
);
647 } else if (sector
== emfat
->priv
.boot_lba
) {
648 read_boot_sector(emfat
, data
);
649 } else if (sector
>= emfat
->priv
.fat1_lba
&& sector
< emfat
->priv
.fat2_lba
) {
650 read_fat_sector(emfat
, data
, sector
- emfat
->priv
.fat1_lba
);
651 } else if (sector
>= emfat
->priv
.fat2_lba
&& sector
< emfat
->priv
.root_lba
) {
652 read_fat_sector(emfat
, data
, sector
- emfat
->priv
.fat2_lba
);
654 memset(data
, 0, SECT
);
662 void write_data_sector(emfat_t
*emfat
, const uint8_t *data
, uint32_t rel_sect
)
666 cluster
= rel_sect
/ 8 + 2;
667 rel_sect
= rel_sect
% 8;
669 le
= emfat
->priv
.last_entry
;
671 if (!IS_CLUST_OF(cluster
, le
)) {
672 le
= find_entry(emfat
, cluster
, le
);
673 if (le
== NULL
) return;
674 emfat
->priv
.last_entry
= le
;
678 // TODO: handle changing a filesize
682 if (le
->writecb
!= NULL
) {
683 le
->writecb(data
, SECT
, rel_sect
* SECT
+ le
->offset
, le
);
688 #define STARTOFTIME 1970
689 #define SECDAY 86400L
690 #define SECYR (SECDAY * 365)
691 #define leapyear(year) ((year) % 4 == 0)
692 #define days_in_year(a) (leapyear(a) ? 366 : 365)
693 #define days_in_month(a) (month_days[(a) - 1])
695 static int month_days
[12] = {
696 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
699 uint32_t emfat_cma_time_from_unix(uint32_t tim
)
702 register long tmp
, day
;
709 /* Hours, minutes, seconds are easy */
712 hms
[1] = (tmp
% 3600) / 60;
713 hms
[2] = (tmp
% 3600) % 60;
715 /* Number of years in days */
716 for (i
= STARTOFTIME
; day
>= days_in_year(i
); i
++)
717 day
-= days_in_year(i
);
720 /* Number of months in days left */
721 if (leapyear(ymd
[0])) {
722 days_in_month(FEBRUARY
) = 29;
724 for (i
= 1; day
>= days_in_month(i
); i
++) {
725 day
-= days_in_month(i
);
727 days_in_month(FEBRUARY
) = 28;
730 /* Days are what is left over (+1) from all that. */
733 return EMFAT_ENCODE_CMA_TIME(ymd
[2], ymd
[1], ymd
[0], hms
[0], hms
[1], hms
[2]);