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
31 #include "common/utils.h"
41 #define SECT_PER_CLUST (CLUST / SECT)
42 #define SIZE_TO_NSECT(s) ((s) == 0 ? 1 : ((s) + SECT - 1) / SECT)
43 #define SIZE_TO_NCLUST(s) ((s) == 0 ? 1 : ((s) + CLUST - 1) / CLUST)
45 #define CLUST_FREE 0x00000000
46 #define CLUST_RESERVED 0x00000001
47 #define CLUST_BAD 0x0FFFFFF7
48 #define CLUST_ROOT_END 0X0FFFFFF8
49 #define CLUST_EOF 0x0FFFFFFF
51 #define MAX_DIR_ENTRY_CNT 16
52 #define FILE_SYS_TYPE_OFF 82
53 #define BYTES_PER_SEC_OFF 11
54 #define SEC_PER_CLUS_OFF 13
55 #define RES_SEC_CNT_OFF 14
56 #define FAT_CNT_OFF 16
57 #define TOT_SEC_CNT_OFF 32
58 #define SEC_PER_FAT 36
59 #define ROOT_DIR_STRT_CLUS_OFF 44
60 #define FS_INFOSECTOR_OFF 48
61 #define BACKUP_BOOT_SEC_OFF 50
62 #define NXT_FREE_CLUS_OFF 492
63 #define FILE_SYS_TYPE_LENGTH 8
64 #define SHRT_FILE_NAME_LEN 11
65 #define STRT_CLUS_LOW_OFF 26
66 #define STRT_CLUS_HIGH_OFF 20
67 #define FILE_SIZE_OFF 28
69 #define FILE_STAT_LEN 21
70 #define CHECK_SUM_OFF 13
71 #define FILE_NAME_SHRT_LEN 8
72 #define FILE_NAME_EXTN_LEN 3
73 #define LONG_FILE_NAME_LEN 255
74 #define LOW_CLUSWORD_MASK 0x0000FFFF
75 #define HIGH_CLUSWORD_MASK 0xFFFF0000
76 #define LONG_FNAME_MASK 0x0F
77 #define LAST_ORD_FIELD_SEQ 0x40
78 #define LFN_END_MARK 0xFFFF
79 #define LFN_TERM_MARK 0x0000
80 #define LFN_FIRST_OFF 0x01
81 #define LFN_SIXTH_OFF 0x0E
82 #define LFN_TWELVETH_OFF 0x1C
83 #define LFN_FIRST_SET_CNT 5
84 #define LFN_SEC_SET_CNT 6
85 #define LFN_THIRD_SET_CNT 2
86 #define LFN_FIRST_SET_LEN 10
87 #define LFN_SEC_SET_LEN 12
88 #define LFN_THIRD_SET_LEN 4
89 #define LFN_EMPTY_LEN 2
90 #define LFN_LEN_PER_ENTRY 13
91 #define FNAME_EXTN_SEP_OFF 6
92 #define FNAME_SEQ_NUM_OFF 7
93 #define BYTES_PER_CLUSTER_ENTRY 4
94 #define DIR_ENTRY_LEN 32
96 #define VOL_LABEL_LEN 11
99 #define OEM_NAME_LEN 8
100 #define JUMP_INS_LEN 3
101 #define MAX_FAT_CNT 2
103 #define FILE_READ 0x01
104 #define FILE_WRITE 0X02
105 #define FILE_CREATE_NEW 0x04
106 #define FILE_CREATE_ALWAYS 0x08
107 #define FILE_APPEND 0x10
108 #define FREE_DIR_ENTRY 0x00
109 #define DEL_DIR_ENTRY 0xE5
110 #define DOT_DIR_ENTRY 0x2E
111 #define ASCII_DIFF 32
112 #define FILE_SEEK_SET 0
113 #define FILE_SEEK_CUR 1
114 #define FILE_SEEK_END 2
115 #define DELIMITER '/'
116 #define EXTN_DELIMITER '.'
118 #define FULL_SHRT_NAME_LEN 13
120 #pragma pack(push, 1)
124 uint8_t status
; // 0x80 for bootable, 0x00 for not bootable, anything else for invalid
125 uint8_t start_head
; // The head of the start
126 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.
127 uint8_t start_cylinder
; // (C & 0xFF) where C is the cylinder of the start
131 uint8_t end_cylinder
;
132 uint32_t StartLBA
; // linear address of first sector in partition. Multiply by sector size (usually 512) for real offset
133 uint32_t EndLBA
; // linear address of last sector in partition. Multiply by sector size (usually 512) for real offset
139 uint32_t DiskSig
; //This is optional
140 uint16_t Reserved
; //Usually 0x0000
141 mbr_part_t PartTable
[4];
142 uint8_t BootSignature
[2]; //0x55 0xAA for bootable
147 uint8_t jump
[JUMP_INS_LEN
];
148 uint8_t OEM_name
[OEM_NAME_LEN
];
149 uint16_t bytes_per_sec
;
150 uint8_t sec_per_clus
;
151 uint16_t reserved_sec_cnt
;
153 uint16_t root_dir_max_cnt
;
154 uint16_t tot_sectors
;
156 uint16_t sec_per_fat_fat16
;
157 uint16_t sec_per_track
;
158 uint16_t number_of_heads
;
159 uint32_t hidden_sec_cnt
;
160 uint32_t tol_sector_cnt
;
161 uint32_t sectors_per_fat
;
163 uint8_t fs_version
[FS_VER_LEN
];
164 uint32_t root_dir_strt_cluster
;
165 uint16_t fs_info_sector
;
166 uint16_t backup_boot_sector
;
167 uint8_t reserved
[RESERV_LEN
];
168 uint8_t drive_number
;
171 uint8_t volume_id
[VOL_ID_LEN
];
172 uint8_t volume_label
[VOL_LABEL_LEN
];
173 uint8_t file_system_type
[FILE_SYS_TYPE_LENGTH
];
178 uint32_t signature1
; /* 0x41615252L */
179 uint32_t reserved1
[120]; /* Nothing as far as I can tell */
180 uint32_t signature2
; /* 0x61417272L */
181 uint32_t free_clusters
; /* Free cluster count. -1 if unknown */
182 uint32_t next_cluster
; /* Most recently allocated cluster */
183 uint32_t reserved2
[3];
189 uint8_t name
[FILE_NAME_SHRT_LEN
];
190 uint8_t extn
[FILE_NAME_EXTN_LEN
];
193 uint8_t crt_time_tenth
;
196 uint16_t lst_access_date
;
197 uint16_t strt_clus_hword
;
198 uint16_t lst_mod_time
;
199 uint16_t lst_mod_date
;
200 uint16_t strt_clus_lword
;
207 uint8_t fname0_4
[LFN_FIRST_SET_LEN
];
211 uint8_t fname6_11
[LFN_SEC_SET_LEN
];
212 uint8_t empty
[LFN_EMPTY_LEN
];
213 uint8_t fname12_13
[LFN_THIRD_SET_LEN
];
218 bool emfat_init_entries(emfat_entry_t
*entries
)
224 if (e
->level
!= 0 || !e
->dir
|| e
->name
== NULL
) return false;
229 e
->priv
.num_subentry
= 0;
232 for (i
= 1; entries
[i
].name
!= NULL
; i
++) {
233 entries
[i
].priv
.top
= NULL
;
234 entries
[i
].priv
.next
= NULL
;
235 entries
[i
].priv
.sub
= NULL
;
236 entries
[i
].priv
.num_subentry
= 0;
237 if (entries
[i
].level
== n
- 1) {
238 if (n
== 0) return false;
243 if (entries
[i
].level
== n
+ 1) {
244 if (!e
->dir
) return false;
245 e
->priv
.sub
= &entries
[i
];
246 entries
[i
].priv
.top
= e
;
252 if (entries
[i
].level
== n
) {
253 if (n
== 0) return false;
254 e
->priv
.top
->priv
.num_subentry
++;
255 entries
[i
].priv
.top
= e
->priv
.top
;
256 e
->priv
.next
= &entries
[i
];
267 static void lba_to_chs(int lba
, uint8_t *cl
, uint8_t *ch
, uint8_t *dh
)
269 int cylinder
, head
, sector
;
272 int cylinders
= 1024;
273 sector
= lba
% sectors
+ 1;
274 head
= (lba
/ sectors
) % heads
;
275 cylinder
= lba
/ (sectors
* heads
);
276 if (cylinder
>= cylinders
) {
277 *cl
= *ch
= *dh
= 0xff;
280 *cl
= sector
| ((cylinder
& 0x300) >> 2);
281 *ch
= cylinder
& 0xFF;
285 bool emfat_init(emfat_t
*emfat
, const char *label
, emfat_entry_t
*entries
)
287 uint32_t sect_per_fat
;
289 uint32_t reserved_clust
= 0;
293 if (emfat
== NULL
|| label
== NULL
|| entries
== NULL
) {
297 if (!emfat_init_entries(entries
)) {
302 for (i
= 0; entries
[i
].name
!= NULL
; i
++) {
307 e
->priv
.first_clust
= clust
;
308 e
->priv
.last_clust
= clust
+ SIZE_TO_NCLUST(e
->priv
.num_subentry
* sizeof(dir_entry
)) - 1;
309 e
->priv
.last_reserved
= e
->priv
.last_clust
;
311 e
->priv
.first_clust
= clust
;
312 e
->priv
.last_clust
= e
->priv
.first_clust
+ SIZE_TO_NCLUST(entries
[i
].curr_size
) - 1;
313 e
->priv
.last_reserved
= e
->priv
.first_clust
+ SIZE_TO_NCLUST(entries
[i
].max_size
) - 1;
315 reserved_clust
+= e
->priv
.last_reserved
- e
->priv
.last_clust
;
316 clust
= e
->priv
.last_reserved
+ 1;
320 emfat
->vol_label
= label
;
321 emfat
->priv
.num_entries
= i
;
322 emfat
->priv
.boot_lba
= 62;
323 emfat
->priv
.fsinfo_lba
= emfat
->priv
.boot_lba
+ 1;
324 emfat
->priv
.fat1_lba
= emfat
->priv
.fsinfo_lba
+ 1;
325 emfat
->priv
.num_clust
= clust
;
326 emfat
->priv
.free_clust
= reserved_clust
;
327 sect_per_fat
= SIZE_TO_NSECT((uint64_t)emfat
->priv
.num_clust
* 4);
328 emfat
->priv
.fat2_lba
= emfat
->priv
.fat1_lba
+ sect_per_fat
;
329 emfat
->priv
.root_lba
= emfat
->priv
.fat2_lba
+ sect_per_fat
;
330 emfat
->priv
.entries
= entries
;
331 emfat
->priv
.last_entry
= entries
;
332 emfat
->disk_sectors
= clust
* SECT_PER_CLUST
+ emfat
->priv
.root_lba
;
333 emfat
->vol_size
= (uint64_t)emfat
->disk_sectors
* SECT
;
334 /* calc cyl number */
335 // i = ((emfat->disk_sectors + 63*255 - 1) / (63*255));
336 // emfat->disk_sectors = i * 63*255;
340 void read_mbr_sector(const emfat_t
*emfat
, uint8_t *sect
)
343 memset(sect
, 0, SECT
);
347 mbr
->PartTable
[0].status
= 0x80;
348 mbr
->PartTable
[0].PartType
= 0x0C;
349 mbr
->PartTable
[0].StartLBA
= emfat
->priv
.boot_lba
;
350 mbr
->PartTable
[0].EndLBA
= emfat
->disk_sectors
;
351 lba_to_chs(mbr
->PartTable
[0].StartLBA
, &mbr
->PartTable
[0].start_sector
, &mbr
->PartTable
[0].start_cylinder
, &mbr
->PartTable
[0].start_head
);
352 lba_to_chs(emfat
->disk_sectors
- 1, &mbr
->PartTable
[0].end_sector
, &mbr
->PartTable
[0].end_cylinder
, &mbr
->PartTable
[0].end_head
);
353 mbr
->BootSignature
[0] = 0x55;
354 mbr
->BootSignature
[1] = 0xAA;
357 void read_boot_sector(const emfat_t
*emfat
, uint8_t *sect
)
360 memset(sect
, 0, SECT
);
361 bs
= (boot_sector
*)sect
;
365 memcpy(bs
->OEM_name
, "MSDOS5.0", 8);
366 bs
->bytes_per_sec
= SECT
;
367 bs
->sec_per_clus
= 8; /* 4 kb per cluster */
368 bs
->reserved_sec_cnt
= 2; /* boot sector & fsinfo sector */
369 bs
->fat_cnt
= 2; /* two tables */
370 bs
->root_dir_max_cnt
= 0;
372 bs
->media_desc
= 0xF8;
373 bs
->sec_per_fat_fat16
= 0;
374 bs
->sec_per_track
= 63;
375 bs
->number_of_heads
= 0xFF;
376 bs
->hidden_sec_cnt
= 62;
377 bs
->tol_sector_cnt
= emfat
->disk_sectors
- emfat
->priv
.boot_lba
;
378 bs
->sectors_per_fat
= emfat
->priv
.fat2_lba
- emfat
->priv
.fat1_lba
;
380 bs
->fs_version
[0] = 0;
381 bs
->fs_version
[1] = 0;
382 bs
->root_dir_strt_cluster
= 2;
383 bs
->fs_info_sector
= 1;
384 bs
->backup_boot_sector
= 0; /* not used */
385 bs
->drive_number
= 128;
387 bs
->volume_id
[0] = 148;
388 bs
->volume_id
[1] = 14;
389 bs
->volume_id
[2] = 13;
390 bs
->volume_id
[3] = 8;
391 memcpy(bs
->volume_label
, "NO NAME ", VOL_LABEL_LEN
);
392 memcpy(bs
->file_system_type
, "FAT32 ", FILE_SYS_TYPE_LENGTH
);
393 sect
[SECT
- 2] = 0x55;
394 sect
[SECT
- 1] = 0xAA;
397 #define IS_CLUST_OF(clust, entry) ((clust) >= (entry)->priv.first_clust && (clust) <= (entry)->priv.last_reserved)
399 emfat_entry_t
*find_entry(const emfat_t
*emfat
, uint32_t clust
, emfat_entry_t
*nearest
)
401 if (nearest
== NULL
) {
402 nearest
= emfat
->priv
.entries
;
405 if (nearest
->priv
.first_clust
> clust
) {
406 while (nearest
>= emfat
->priv
.entries
) { // backward finding
407 if (IS_CLUST_OF(clust
, nearest
))
412 while (nearest
->name
!= NULL
) { // forward finding
413 if (IS_CLUST_OF(clust
, nearest
))
421 void read_fsinfo_sector(const emfat_t
*emfat
, uint8_t *sect
)
425 fsinfo_t
*info
= (fsinfo_t
*)sect
;
426 info
->signature1
= 0x41615252L
;
427 info
->signature2
= 0x61417272L
;
428 //info->free_clusters = 0;
429 info
->free_clusters
= emfat
->priv
.free_clust
;
430 //info->next_cluster = emfat->priv.num_clust + 2;
431 info
->next_cluster
= 0xffffffff;
432 memset(info
->reserved1
, 0, sizeof(info
->reserved1
));
433 memset(info
->reserved2
, 0, sizeof(info
->reserved2
));
434 info
->signature3
= 0xAA550000;
437 void read_fat_sector(emfat_t
*emfat
, uint8_t *sect
, uint32_t index
)
444 values
= (uint32_t *)sect
;
449 *values
++ = CLUST_ROOT_END
;
450 *values
++ = 0xFFFFFFFF;
455 le
= emfat
->priv
.last_entry
;
457 if (!IS_CLUST_OF(curr
, le
)) {
458 le
= find_entry(emfat
, curr
, le
);
460 le
= emfat
->priv
.last_entry
;
461 *values
= CLUST_RESERVED
;
469 if (curr
== le
->priv
.last_clust
) {
475 if (curr
== le
->priv
.last_clust
) {
477 } else if (curr
> le
->priv
.last_clust
) {
478 *values
= CLUST_FREE
;
487 emfat
->priv
.last_entry
= le
;
490 void fill_entry(dir_entry
*entry
, const char *name
, uint8_t attr
, uint32_t clust
, const uint32_t cma
[3], uint32_t size
)
495 memset(entry
, 0, sizeof(dir_entry
));
498 entry
->crt_date
= cma
[0] >> 16;
499 entry
->crt_time
= cma
[0] & 0xFFFF;
500 entry
->lst_mod_date
= cma
[1] >> 16;
501 entry
->lst_mod_time
= cma
[1] & 0xFFFF;
502 entry
->lst_access_date
= cma
[2] >> 16;
508 if ((attr
& ATTR_DIR
) == 0) {
509 for (i
= l
- 1; i
>= 0; i
--) {
519 l1
= l
> FILE_NAME_SHRT_LEN
? FILE_NAME_SHRT_LEN
: l
;
523 l1
= l1
> FILE_NAME_SHRT_LEN
? FILE_NAME_SHRT_LEN
: l1
;
524 l2
= l
- dot_pos
- 1;
525 l2
= l2
> FILE_NAME_EXTN_LEN
? FILE_NAME_EXTN_LEN
: l2
;
528 memset(entry
->name
, ' ', FILE_NAME_SHRT_LEN
);
529 memcpy(entry
->name
, name
, l1
);
530 memset(entry
->extn
, ' ', FILE_NAME_EXTN_LEN
);
531 memcpy(entry
->extn
, name
+ dot_pos
+ 1, l2
);
533 for (i
= 0; i
< FILE_NAME_SHRT_LEN
; i
++) {
534 if (entry
->name
[i
] >= 'a' && entry
->name
[i
] <= 'z') {
535 entry
->name
[i
] -= 0x20;
539 for (i
= 0; i
< FILE_NAME_EXTN_LEN
; i
++) {
540 if (entry
->extn
[i
] >= 'a' && entry
->extn
[i
] <= 'z') {
541 entry
->extn
[i
] -= 0x20;
546 entry
->reserved
= 24;
547 entry
->strt_clus_hword
= clust
>> 16;
548 entry
->strt_clus_lword
= clust
;
554 void fill_dir_sector(emfat_t
*emfat
, uint8_t *data
, emfat_entry_t
*entry
, uint32_t rel_sect
)
559 memset(data
, 0, SECT
);
560 de
= (dir_entry
*)data
;
563 if (rel_sect
== 0) { // 1. first sector of directory
564 if (entry
->priv
.top
== NULL
) {
565 fill_entry(de
++, emfat
->vol_label
, ATTR_VOL_LABEL
, 0, 0, 0);
566 avail
-= sizeof(dir_entry
);
568 fill_entry(de
++, ".", ATTR_DIR
| ATTR_READ
, entry
->priv
.first_clust
, 0, 0);
569 if (entry
->priv
.top
->priv
.top
== NULL
) {
570 fill_entry(de
++, "..", ATTR_DIR
| ATTR_READ
, 0, 0, 0);
572 fill_entry(de
++, "..", ATTR_DIR
| ATTR_READ
, entry
->priv
.top
->priv
.first_clust
, 0, 0);
574 avail
-= sizeof(dir_entry
) * 2;
576 entry
= entry
->priv
.sub
;
577 } else { // 2. not a first sector
579 n
= rel_sect
* (SECT
/ sizeof(dir_entry
));
580 n
-= entry
->priv
.top
== NULL
? 1 : 2;
581 entry
= entry
->priv
.sub
;
583 while (n
> 0 && entry
!= NULL
) {
584 entry
= entry
->priv
.next
;
589 while (entry
!= NULL
&& avail
>= sizeof(dir_entry
)) {
591 fill_entry(de
++, entry
->name
, ATTR_DIR
| ATTR_READ
, entry
->priv
.first_clust
, entry
->cma_time
, 0);
593 //fill_entry(de++, entry->name, ATTR_ARCHIVE | ATTR_READ, entry->priv.first_clust, entry->cma_time, entry->curr_size);
594 fill_entry(de
++, entry
->name
, ATTR_ARCHIVE
| ATTR_READ
| entry
->attr
, entry
->priv
.first_clust
, entry
->cma_time
, entry
->curr_size
);
596 entry
= entry
->priv
.next
;
597 avail
-= sizeof(dir_entry
);
601 void read_data_sector(emfat_t
*emfat
, uint8_t *data
, uint32_t rel_sect
)
605 cluster
= rel_sect
/ 8 + 2;
606 rel_sect
= rel_sect
% 8;
608 le
= emfat
->priv
.last_entry
;
609 if (!IS_CLUST_OF(cluster
, le
)) {
610 le
= find_entry(emfat
, cluster
, le
);
613 for (i
= 0; i
< SECT
/ 4; i
++)
614 ((uint32_t *)data
)[i
] = 0xEFBEADDE;
617 emfat
->priv
.last_entry
= le
;
621 fill_dir_sector(emfat
, data
, le
, rel_sect
);
625 if (le
->readcb
== NULL
) {
626 memset(data
, 0, SECT
);
628 uint32_t offset
= cluster
- le
->priv
.first_clust
;
629 offset
= offset
* CLUST
+ rel_sect
* SECT
;
630 le
->readcb(data
, SECT
, offset
+ le
->offset
, le
);
636 void emfat_read(emfat_t
*emfat
, uint8_t *data
, uint32_t sector
, int num_sectors
)
638 while (num_sectors
> 0) {
639 if (sector
>= emfat
->priv
.root_lba
) {
640 read_data_sector(emfat
, data
, sector
- emfat
->priv
.root_lba
);
641 } else if (sector
== 0) {
642 read_mbr_sector(emfat
, data
);
643 } else if (sector
== emfat
->priv
.fsinfo_lba
) {
644 read_fsinfo_sector(emfat
, data
);
645 } else if (sector
== emfat
->priv
.boot_lba
) {
646 read_boot_sector(emfat
, data
);
647 } else if (sector
>= emfat
->priv
.fat1_lba
&& sector
< emfat
->priv
.fat2_lba
) {
648 read_fat_sector(emfat
, data
, sector
- emfat
->priv
.fat1_lba
);
649 } else if (sector
>= emfat
->priv
.fat2_lba
&& sector
< emfat
->priv
.root_lba
) {
650 read_fat_sector(emfat
, data
, sector
- emfat
->priv
.fat2_lba
);
652 memset(data
, 0, SECT
);
660 void write_data_sector(emfat_t
*emfat
, const uint8_t *data
, uint32_t rel_sect
)
664 cluster
= rel_sect
/ 8 + 2;
665 rel_sect
= rel_sect
% 8;
667 le
= emfat
->priv
.last_entry
;
669 if (!IS_CLUST_OF(cluster
, le
)) {
670 le
= find_entry(emfat
, cluster
, le
);
671 if (le
== NULL
) return;
672 emfat
->priv
.last_entry
= le
;
676 // TODO: handle changing a filesize
680 if (le
->writecb
!= NULL
) {
681 le
->writecb(data
, SECT
, rel_sect
* SECT
+ le
->offset
, le
);
686 #define STARTOFTIME 1970
687 #define SECDAY 86400L
688 #define SECYR (SECDAY * 365)
689 #define leapyear(year) ((year) % 4 == 0)
690 #define days_in_year(a) (leapyear(a) ? 366 : 365)
691 #define days_in_month(a) (month_days[(a) - 1])
693 static int month_days
[12] = {
694 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
697 uint32_t emfat_cma_time_from_unix(uint32_t tim
)
700 register long tmp
, day
;
707 /* Hours, minutes, seconds are easy */
710 hms
[1] = (tmp
% 3600) / 60;
711 hms
[2] = (tmp
% 3600) % 60;
713 /* Number of years in days */
714 for (i
= STARTOFTIME
; day
>= days_in_year(i
); i
++)
715 day
-= days_in_year(i
);
718 /* Number of months in days left */
719 if (leapyear(ymd
[0])) {
720 days_in_month(FEBRUARY
) = 29;
722 for (i
= 1; day
>= days_in_month(i
); i
++) {
723 day
-= days_in_month(i
);
725 days_in_month(FEBRUARY
) = 28;
728 /* Days are what is left over (+1) from all that. */
731 return EMFAT_ENCODE_CMA_TIME(ymd
[2], ymd
[1], ymd
[0], hms
[0], hms
[1], hms
[2]);