Esempio n. 1
0
static void parse_cochran_header(const char *filename,
		const unsigned char *decode, unsigned mod,
		const unsigned char *in, unsigned size)
{
	char *buf = malloc(size);

	/* Do the "null decode" using a one-byte decode array of '\0' */
	partial_decode(0    , 0x0b14, "", 0, 1, in, size, buf);

	/*
	 * The header scrambling is different form the dive
	 * scrambling. Oh yay!
	 */
	partial_decode(0x010e, 0x0b14, decode, 0, mod, in, size, buf);
	partial_decode(0x0b14, 0x1b14, decode, 0, mod, in, size, buf);
	partial_decode(0x1b14, 0x2b14, decode, 0, mod, in, size, buf);
	partial_decode(0x2b14, 0x3b14, decode, 0, mod, in, size, buf);
	partial_decode(0x3b14, 0x5414, decode, 0, mod, in, size, buf);
	partial_decode(0x5414,   size, decode, 0, mod, in, size, buf);

	printf("\n%s, header\n\n", filename);
	cochran_debug_write(filename, buf, size);

	free(buf);
}
Esempio n. 2
0
static void cochran_parse_header(const unsigned char *decode, unsigned mod,
				 const unsigned char *in, unsigned size)
{
	unsigned char *buf = malloc(size);

	/* Do the "null decode" using a one-byte decode array of '\0' */
	/* Copies in plaintext, will be overwritten later */
	partial_decode(0, 0x0102, (const unsigned char *)"", 0, 1, in, size, buf);

	/*
	 * The header scrambling is different form the dive
	 * scrambling. Oh yay!
	 */
	partial_decode(0x0102, 0x010e, decode, 0, mod, in, size, buf);
	partial_decode(0x010e, 0x0b14, decode, 0, mod, in, size, buf);
	partial_decode(0x0b14, 0x1b14, decode, 0, mod, in, size, buf);
	partial_decode(0x1b14, 0x2b14, decode, 0, mod, in, size, buf);
	partial_decode(0x2b14, 0x3b14, decode, 0, mod, in, size, buf);
	partial_decode(0x3b14, 0x5414, decode, 0, mod, in, size, buf);
	partial_decode(0x5414,  size, decode, 0, mod, in, size, buf);

	// Detect log type
	switch (buf[0x133]) {
	case '2':	// Cochran Commander, version II log format
		config.logbook_size = 256;
		if (buf[0x132] == 0x10) {
			config.type = TYPE_GEMINI;
			config.sample_size = 2;	// Gemini with tank PSI samples
		} else  {
			config.type = TYPE_COMMANDER;
			config.sample_size = 2;	// Commander
		}
		break;
	case '3':	// Cochran EMC, version III log format
		config.type = TYPE_EMC;
		config.logbook_size = 512;
		config.sample_size = 3;
		break;
	default:
		printf ("Unknown log format v%c\n", buf[0x137]);
		free(buf);
		exit(1);
		break;
	}

#ifdef COCHRAN_DEBUG
	puts("Header\n======\n\n");
	cochran_debug_write(buf, size);
#endif

	free(buf);
}
Esempio n. 3
0
/*
 * The decode buffer size can be figured out by simply trying our the
 * decode: we expect that the scrambled contents are largely random, and
 * thus tend to have half the bits set. Summing over the bytes is going
 * to give an average of 0x80 per byte.
 *
 * The decoded array is mostly full of zeroes, so the sum is lower.
 *
 * Works for me.
 */
static int figure_out_modulus(const unsigned char *decode, const unsigned char *dive, unsigned int size)
{
	int mod, best = -1;
	unsigned int min = ~0u;

	if (size < 0x1000)
		return best;

	for (mod = 50; mod < 300; mod++) {
		unsigned int sum;

		sum = partial_decode(0, 0x0fff, decode, 1, mod, dive, size, NULL);
		if (sum < min) {
			min = sum;
			best = mod;
		}
	}
	return best;
}
Esempio n. 4
0
static void parse_cochran_dive(const char *filename, int dive,
		const unsigned char *decode, unsigned mod,
		const unsigned char *in, unsigned size)
{
	char *buf = malloc(size);
#ifdef DON
	unsigned int offset = 0x4a14;
#else
	unsigned int offset = 0x4b14;
#endif

	/*
	 * The scrambling has odd boundaries. I think the boundaries
	 * match some data structure size, but I don't know. They were
	 * discovered the same way we dynamically discover the decode
	 * size: automatically looking for least random output.
	 *
	 * The boundaries are also this confused "off-by-one" thing,
	 * the same way the file size is off by one. It's as if the
	 * cochran software forgot to write one byte at the beginning.
	 */
	partial_decode(0     , 0x0fff, decode, 1, mod, in, size, buf);
	partial_decode(0x0fff, 0x1fff, decode, 0, mod, in, size, buf);
	partial_decode(0x1fff, 0x2fff, decode, 0, mod, in, size, buf);
	partial_decode(0x2fff, 0x48ff, decode, 0, mod, in, size, buf);

	/*
	 * This is not all the descrambling you need - the above are just
	 * what appears to be the fixed-size blocks. The rest is also
	 * scrambled, but there seems to be size differences in the data,
	 * so this just descrambles part of it:
	 */
	partial_decode(0x48ff, offset, decode, 0, mod, in, size, buf);
	partial_decode(offset,   size, decode, 0, mod, in, size, buf);

	printf("\n%s, dive %d\n\n", filename, dive);
	cochran_debug_write(filename, buf, size);
	cochran_profile_write(buf + offset, size - offset);

	free(buf);
}
Esempio n. 5
0
static void cochran_parse_dive(const unsigned char *decode, unsigned mod,
			       const unsigned char *in, unsigned size,
			       struct dive_table *table)
{
	unsigned char *buf = malloc(size);
	struct dive *dive;
	struct divecomputer *dc;
	struct tm tm = {0};
	uint32_t csum[5];

	double max_depth, avg_depth, min_temp;
	unsigned int duration = 0, corrupt_dive = 0;

	/*
	 * The scrambling has odd boundaries. I think the boundaries
	 * match some data structure size, but I don't know. They were
	 * discovered the same way we dynamically discover the decode
	 * size: automatically looking for least random output.
	 *
	 * The boundaries are also this confused "off-by-one" thing,
	 * the same way the file size is off by one. It's as if the
	 * cochran software forgot to write one byte at the beginning.
	 */
	partial_decode(0, 0x0fff, decode, 1, mod, in, size, buf);
	partial_decode(0x0fff, 0x1fff, decode, 0, mod, in, size, buf);
	partial_decode(0x1fff, 0x2fff, decode, 0, mod, in, size, buf);
	partial_decode(0x2fff, 0x48ff, decode, 0, mod, in, size, buf);

	/*
	 * This is not all the descrambling you need - the above are just
	 * what appears to be the fixed-size blocks. The rest is also
	 * scrambled, but there seems to be size differences in the data,
	 * so this just descrambles part of it:
	 */

	if (size < 0x4914 + config.logbook_size) {
		// Analyst calls this a "Corrupt Beginning Summary"
		free(buf);
		return;
	}

	// Decode log entry (512 bytes + random prefix)
	partial_decode(0x48ff, 0x4914 + config.logbook_size, decode,
		0, mod, in, size, buf);

	unsigned int sample_size = size - 0x4914 - config.logbook_size;
	int g;
	unsigned int sample_pre_offset = 0, sample_end_offset = 0;

	// Decode sample data
	partial_decode(0x4914 + config.logbook_size, size, decode,
		0, mod, in, size, buf);

#ifdef COCHRAN_DEBUG
	// Display pre-logbook data
	puts("\nPre Logbook Data\n");
	cochran_debug_write(buf, 0x4914);

	// Display log book
	puts("\nLogbook Data\n");
	cochran_debug_write(buf + 0x4914,  config.logbook_size + 0x400);

	// Display sample data
	puts("\nSample Data\n");
#endif

	dive = alloc_dive();
	dc = &dive->dc;

	unsigned char *log = (buf + 0x4914);

	switch (config.type) {
	case TYPE_GEMINI:
	case TYPE_COMMANDER:
		if (config.type == TYPE_GEMINI) {
			dc->model = "Gemini";
			dc->deviceid = buf[0x18c] * 256 + buf[0x18d];	// serial no
			fill_default_cylinder(&dive->cylinder[0]);
			dive->cylinder[0].gasmix.o2.permille = (log[CMD_O2_PERCENT] / 256
				+ log[CMD_O2_PERCENT + 1]) * 10;
			dive->cylinder[0].gasmix.he.permille = 0;
		} else {
			dc->model = "Commander";
			dc->deviceid = array_uint32_le(buf + 0x31e);	// serial no
			for (g = 0; g < 2; g++) {
				fill_default_cylinder(&dive->cylinder[g]);
				dive->cylinder[g].gasmix.o2.permille = (log[CMD_O2_PERCENT + g * 2] / 256
					+ log[CMD_O2_PERCENT + g * 2 + 1]) * 10;
				dive->cylinder[g].gasmix.he.permille = 0;
			}
		}

		tm.tm_year = log[CMD_YEAR];
		tm.tm_mon = log[CMD_MON] - 1;
		tm.tm_mday = log[CMD_DAY];
		tm.tm_hour = log[CMD_HOUR];
		tm.tm_min = log[CMD_MIN];
		tm.tm_sec = log[CMD_SEC];
		tm.tm_isdst = -1;

		dive->when = dc->when = utc_mktime(&tm);
		dive->number = log[CMD_NUMBER] + log[CMD_NUMBER + 1] * 256 + 1;
		dc->duration.seconds = (log[CMD_BT] + log[CMD_BT + 1] * 256) * 60;
		dc->surfacetime.seconds = (log[CMD_SIT] + log[CMD_SIT + 1] * 256) * 60;
		dc->maxdepth.mm = lrint((log[CMD_MAX_DEPTH] +
			log[CMD_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000);
		dc->meandepth.mm = lrint((log[CMD_AVG_DEPTH] +
			log[CMD_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000);
		dc->watertemp.mkelvin = C_to_mkelvin((log[CMD_MIN_TEMP] / 32) - 1.8);
		dc->surface_pressure.mbar = lrint(ATM / BAR * pow(1 - 0.0000225577
			* (double) log[CMD_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
		dc->salinity = 10000 + 150 * log[CMD_WATER_CONDUCTIVITY];

		SHA1(log + CMD_NUMBER, 2, (unsigned char *)csum);
		dc->diveid = csum[0];

		if (log[CMD_MAX_DEPTH] == 0xff && log[CMD_MAX_DEPTH + 1] == 0xff)
			corrupt_dive = 1;

		sample_pre_offset = array_uint32_le(log + CMD_PREDIVE_OFFSET);
		sample_end_offset = array_uint32_le(log + CMD_END_OFFSET);

		break;
	case TYPE_EMC:
		dc->model = "EMC";
		dc->deviceid = array_uint32_le(buf + 0x31e);	// serial no
		for (g = 0; g < 4; g++) {
			fill_default_cylinder(&dive->cylinder[g]);
			dive->cylinder[g].gasmix.o2.permille =
				(log[EMC_O2_PERCENT + g * 2] / 256
				+ log[EMC_O2_PERCENT + g * 2 + 1]) * 10;
			dive->cylinder[g].gasmix.he.permille =
				(log[EMC_HE_PERCENT + g * 2] / 256
				+ log[EMC_HE_PERCENT + g * 2 + 1]) * 10;
		}

		tm.tm_year = log[EMC_YEAR];
		tm.tm_mon = log[EMC_MON] - 1;
		tm.tm_mday = log[EMC_DAY];
		tm.tm_hour = log[EMC_HOUR];
		tm.tm_min = log[EMC_MIN];
		tm.tm_sec = log[EMC_SEC];
		tm.tm_isdst = -1;

		dive->when = dc->when = utc_mktime(&tm);
		dive->number = log[EMC_NUMBER] + log[EMC_NUMBER + 1] * 256 + 1;
		dc->duration.seconds = (log[EMC_BT] + log[EMC_BT + 1] * 256) * 60;
		dc->surfacetime.seconds = (log[EMC_SIT] + log[EMC_SIT + 1] * 256) * 60;
		dc->maxdepth.mm = lrint((log[EMC_MAX_DEPTH] +
			log[EMC_MAX_DEPTH + 1] * 256) / 4 * FEET * 1000);
		dc->meandepth.mm = lrint((log[EMC_AVG_DEPTH] +
			log[EMC_AVG_DEPTH + 1] * 256) / 4 * FEET * 1000);
		dc->watertemp.mkelvin = C_to_mkelvin((log[EMC_MIN_TEMP] - 32) / 1.8);
		dc->surface_pressure.mbar = lrint(ATM / BAR * pow(1 - 0.0000225577
			* (double) log[EMC_ALTITUDE] * 250 * FEET, 5.25588) * 1000);
		dc->salinity = 10000 + 150 * (log[EMC_WATER_CONDUCTIVITY] & 0x3);

		SHA1(log + EMC_NUMBER, 2, (unsigned char *)csum);
		dc->diveid = csum[0];

		if (log[EMC_MAX_DEPTH] == 0xff && log[EMC_MAX_DEPTH + 1] == 0xff)
			corrupt_dive = 1;

		sample_pre_offset = array_uint32_le(log + EMC_PREDIVE_OFFSET);
		sample_end_offset = array_uint32_le(log + EMC_END_OFFSET);

		break;
	}

	// Use the log information to determine actual profile sample size
	// Otherwise we will get surface time at end of dive.
	if (sample_pre_offset < sample_end_offset && sample_end_offset != 0xffffffff)
		sample_size = sample_end_offset - sample_pre_offset;

	cochran_parse_samples(dive, buf + 0x4914, buf + 0x4914
		+ config.logbook_size, sample_size,
		&duration, &max_depth, &avg_depth, &min_temp);

	// Check for corrupt dive
	if (corrupt_dive) {
		dc->maxdepth.mm = lrint(max_depth * FEET * 1000);
		dc->meandepth.mm = lrint(avg_depth * FEET * 1000);
		dc->watertemp.mkelvin = C_to_mkelvin((min_temp - 32) / 1.8);
		dc->duration.seconds = duration;
	}

	record_dive_to_table(dive, table);
	mark_divelist_changed(true);

	free(buf);
}