1/*
2 * Copyright 2012, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2003, Tyler Dauwalder, tyler@dauwalder.net.
4 * Distributed under the terms of the MIT License.
5 */
6
7/*! \file Utils.cpp - Miscellaneous Udf utility functions. */
8
9#include "UdfStructures.h"
10#include "Utils.h"
11
12extern "C" {
13	extern int32 timezone_offset;
14}
15
16/*! \brief Returns "true" if \a value is true, "false" otherwise. */
17const char*
18bool_to_string(bool value)
19{
20	return value ? "true" : "false";
21}
22
23
24/*! \brief Calculates the UDF crc checksum for the given byte stream.
25
26	Based on crc code from UDF-2.50 6.5, as permitted.
27
28	\param data Pointer to the byte stream.
29	\param length Length of the byte stream in bytes.
30
31	\return The crc checksum, or 0 if an error occurred.
32*/
33uint16
34calculate_crc(uint8 *data, uint16 length)
35{
36	uint16 crc = 0;
37	if (data) {
38		for ( ; length > 0; length--, data++)
39			crc = kCrcTable[(crc >> 8 ^ *data) & 0xff] ^ (crc << 8);
40	}
41	return crc;
42}
43
44
45/*! \brief Takes an overloaded ssize_t return value like those returned
46	by BFile::Read() and friends, as well as an expected number of bytes,
47	and returns B_OK if the byte counts match, or the appropriate error
48	code otherwise.
49*/
50status_t
51check_size_error(ssize_t bytesReturned, ssize_t bytesExpected)
52{
53	return bytesReturned == bytesExpected
54		? B_OK : (bytesReturned >= 0 ? B_IO_ERROR : status_t(bytesReturned));
55}
56
57
58/*! \brief Calculates the block shift amount for the given
59 	block size, which must be a positive power of 2.
60*/
61status_t
62get_block_shift(uint32 blockSize, uint32 &blockShift)
63{
64	if (blockSize == 0)
65		return B_BAD_VALUE;
66	uint32 bitCount = 0;
67	uint32 result = 0;
68	for (int i = 0; i < 32; i++) {
69		// Zero out all bits except bit i
70		uint32 block = blockSize & (uint32(1) << i);
71		if (block) {
72			if (++bitCount > 1)
73				return B_BAD_VALUE;
74			else
75				result = i;
76		}
77	}
78	blockShift = result;
79	return B_OK;
80}
81
82
83#define EPOCH_YEAR	1970
84#define MAX_YEAR	69
85#define SECSPERMIN	60
86#define MINSPERHOUR	60
87#define HOURSPERDAY	24
88#define SECSPERDAY	(SECSPERMIN * MINSPERHOUR * HOURSPERDAY)
89#define DAYSPERNYEAR	365
90
91status_t
92decode_time(timestamp &timestamp, struct timespec &timespec)
93{
94	DEBUG_INIT_ETC(NULL, ("timestamp: (tnt: 0x%x, type: %d, timezone: %d = 0x%x, year: %d, "
95	           "month: %d, day: %d, hour: %d, minute: %d, second: %d)", timestamp.type_and_timezone(),
96	           timestamp.type(), timestamp.timezone(),
97	            timestamp.timezone(),timestamp.year(),
98	           timestamp.month(), timestamp.day(), timestamp.hour(), timestamp.minute(), timestamp.second()));
99
100	if (timestamp.year() < EPOCH_YEAR || timestamp.year() >= EPOCH_YEAR + MAX_YEAR)
101		return B_BAD_VALUE;
102
103	time_t result = 0;
104	const int monthLengths[12]
105		= { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
106
107	int year = timestamp.year();
108	int month = timestamp.month();
109	int day = timestamp.day();
110	int hour = timestamp.hour();
111	int minute = timestamp.minute();
112	int second = timestamp.second();
113
114	// Range check the timezone offset, then round it down
115	// to the nearest hour, since no one I know treats timezones
116	// with a per-minute granularity, and none of the other OSes
117	// I've looked at appear to either.
118	int timezone_offset = 0;
119	if (timestamp.type() == 1)
120		timezone_offset = timestamp.timezone();
121	if (-SECSPERDAY > timezone_offset || timezone_offset > SECSPERDAY)
122		timezone_offset = 0;
123	timezone_offset -= timezone_offset % 60;
124
125	int previousLeapYears = (year - 1968) / 4;
126	bool isLeapYear = (year - 1968) % 4 == 0;
127	if (isLeapYear)
128		--previousLeapYears;
129
130	// Years to days
131	result = (year - EPOCH_YEAR) * DAYSPERNYEAR + previousLeapYears;
132	// Months to days
133	for (int i = 0; i < month-1; i++) {
134		result += monthLengths[i];
135	}
136	if (month > 2 && isLeapYear)
137		++result;
138	// Days to hours
139	result = (result + day - 1) * HOURSPERDAY;
140	// Hours to minutes
141	result = (result + hour) * MINSPERHOUR + timezone_offset;
142	// Minutes to seconds
143	result = (result + minute) * SECSPERMIN + second;
144
145	timespec.tv_sec = result;
146	timespec.tv_nsec = 1000 * (timestamp.microsecond()
147		+ timestamp.hundred_microsecond() * 100
148		+ timestamp.centisecond() * 10000);
149	return B_OK;
150}
151
152
153long_address
154to_long_address(ino_t id, uint32 length)
155{
156	TRACE(("udf_to_long_address: ino_t = %" B_PRIdINO ", length = %" B_PRIu32,
157		id, length));
158	long_address result;
159	result.set_block((id >> 16) & 0xffffffff);
160	result.set_partition(id & 0xffff);
161	result.set_length(length);
162	DUMP(result);
163	return result;
164}
165
166
167ino_t
168to_vnode_id(long_address address)
169{
170	DEBUG_INIT(NULL);
171	ino_t result = address.block();
172	result <<= 16;
173	result |= address.partition();
174	TRACE(("block:     %" B_PRIu32 ", 0x%" B_PRIx32 "\n", address.block(),
175		address.block()));
176	TRACE(("partition: %d, 0x%x\n", address.partition(), address.partition()));
177	TRACE(("length:    %" B_PRIu32 ", 0x%" B_PRIx32 "\n", address.length(),
178		address.length()));
179	TRACE(("ino_t:     %" B_PRIdINO "\n", result));
180	return result;
181}
182