//----------------------------------------------------------------------
//  This software is part of the OpenBeOS distribution and is covered 
//  by the OpenBeOS license.
//
//  Copyright (c) 2003 Tyler Dauwalder, tyler@dauwalder.net
//----------------------------------------------------------------------

/*! \file DiskStructures.cpp

	UDF on-disk data structure definitions
*/

#include "DiskStructures.h"

#include <string.h>

#include "CS0String.h"

using namespace Udf;

//----------------------------------------------------------------------
// Constants
//----------------------------------------------------------------------

//const charspec kCS0Charspec = { _character_set_type: 0,
//                                _character_set_info: "OSTA Compressed Unicode"
//                                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
//                                                    "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
//                              };
                              
// Volume structure descriptor ids 
const char* Udf::kVSDID_BEA 		= "BEA01";
const char* Udf::kVSDID_TEA 		= "TEA01";
const char* Udf::kVSDID_BOOT 		= "BOOT2";
const char* Udf::kVSDID_ISO 		= "CD001";
const char* Udf::kVSDID_ECMA167_2 	= "NSR02";
const char* Udf::kVSDID_ECMA167_3 	= "NSR03";
const char* Udf::kVSDID_ECMA168		= "CDW02";

//----------------------------------------------------------------------
// udf_volume_structure_descriptor_header
//----------------------------------------------------------------------

/*! \brief Returns true if the given \a id matches the header's id.
*/
bool
udf_volume_structure_descriptor_header::id_matches(const char *id)
{
	return strncmp(this->id, id, 5) == 0;
}


//----------------------------------------------------------------------
// udf_charspec
//----------------------------------------------------------------------

void
udf_charspec::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_DUMP, "udf_charspec");
	PRINT(("character_set_type: %d\n", character_set_type()));
	PRINT(("character_set_info: `%s'\n", character_set_info()));
}


//----------------------------------------------------------------------
// udf_timestamp
//----------------------------------------------------------------------

void
udf_timestamp::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_DUMP, "udf_timestamp");
	PRINT(("type:                %d\n", type()));
	PRINT(("timezone:            %d\n", timezone()));
	PRINT(("year:                %d\n", year()));
	PRINT(("month:               %d\n", month()));
	PRINT(("day:                 %d\n", day()));
	PRINT(("hour:                %d\n", hour()));
	PRINT(("minute:              %d\n", minute()));
	PRINT(("second:              %d\n", second()));
	PRINT(("centisecond:         %d\n", centisecond()));
	PRINT(("hundred_microsecond: %d\n", hundred_microsecond()));
	PRINT(("microsecond:         %d\n", microsecond()));
}

//----------------------------------------------------------------------
// udf_entity_id
//----------------------------------------------------------------------

void
udf_entity_id::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_DUMP, "udf_entity_id");
	PRINT(("flags:             %d\n", flags()));
	PRINT(("identifier:        `%s'\n", identifier()));
	PRINT(("identifier_suffix: `%s'\n", identifier_suffix()));
}


//----------------------------------------------------------------------
// udf_extent_address
//----------------------------------------------------------------------

void
udf_extent_address::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_DUMP, "udf_extent_address");
	PRINT(("length:   %ld\n", length()));
	PRINT(("location: %ld\n", location()));
}

void
udf_logical_block_address::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_DUMP, "udf_logical_block_address");
	PRINT(("block:     %ld\n", block()));
	PRINT(("partition: %d\n", partition()));
}

void
udf_long_address::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_DUMP, "udf_long_address");
	PRINT(("length:   %ld\n", length()));
	PRINT(("block:    %ld\n", block()));
	PRINT(("partiton: %d\n", partition()));
	PRINT(("implementation_use:\n"));
	DUMP(implementation_use());
}

//----------------------------------------------------------------------
// udf_tag
//----------------------------------------------------------------------

void
udf_tag::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_descriptor_tag");
	PRINT(("id:            %d\n", id()));
	PRINT(("version:       %d\n", version()));
	PRINT(("checksum:      %d\n", checksum()));
	PRINT(("serial_number: %d\n", serial_number()));
	PRINT(("crc:           %d\n", crc()));
	PRINT(("crc_length:    %d\n", crc_length()));
	PRINT(("location:      %ld\n", location()));
}


/*! \brief Calculates the tag's CRC, verifies the tag's checksum, and
	verifies the tag's location on the medium.
	
	\todo Calc the CRC.
*/
status_t 
udf_tag::init_check(uint32 diskBlock)
{
	DEBUG_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_HIGH_VOLUME, "udf_descriptor_tag");
	PRINT(("diskLocation == %ld\n", diskBlock));
	PRINT(("location() == %ld\n", location()));
	status_t err = (diskBlock == location()) ? B_OK : B_NO_INIT;
	// checksum
	if (!err) {
		uint32 sum = 0;
		for (int i = 0; i <= 3; i++)
			sum += ((uint8*)this)[i];
		for (int i = 5; i <= 15; i++)
			sum += ((uint8*)this)[i];
		err = sum % 256 == checksum() ? B_OK : B_NO_INIT;
	}
	
	RETURN(err);
	
}


//----------------------------------------------------------------------
// udf_primary_descriptor
//----------------------------------------------------------------------

void
udf_primary_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_primary_descriptor");
	
	CS0String string;
	
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                       %ld\n", vds_number()));
	PRINT(("primary_volume_descriptor_number: %ld\n", primary_volume_descriptor_number()));
	string = volume_identifier();
	PRINT(("volume_identifier:                `%s'\n", string.String()));
	PRINT(("volume_sequence_number:           %d\n", volume_sequence_number()));
	PRINT(("max_volume_sequence_number:       %d\n", max_volume_sequence_number()));
	PRINT(("interchange_level:                %d\n", interchange_level()));
	PRINT(("max_interchange_level:            %d\n", max_interchange_level()));
	PRINT(("character_set_list:               %ld\n", character_set_list()));
	PRINT(("max_character_set_list:           %ld\n", max_character_set_list()));
	string = volume_set_identifier();
	PRINT(("volume_set_identifier:            `%s'\n", string.String()));
	PRINT(("descriptor_character_set:\n"));
	DUMP(descriptor_character_set());
	PRINT(("explanatory_character_set:\n"));
	DUMP(explanatory_character_set());
	PRINT(("volume_abstract:\n"));
	DUMP(volume_abstract());
	PRINT(("volume_copyright_notice:\n"));
	DUMP(volume_copyright_notice());
	PRINT(("application_id:\n"));
	DUMP(application_id());
	PRINT(("recording_date_and_time:\n"));
	DUMP(recording_date_and_time());
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use:\n"));
	DUMP(implementation_use());
}


//----------------------------------------------------------------------
// udf_anchor_volume_descriptor_pointer
//----------------------------------------------------------------------

void
udf_anchor_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_anchor_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("main_vds:\n"));
	DUMP(main_vds());
	PRINT(("reserve_vds:\n"));
	DUMP(reserve_vds());
}


//----------------------------------------------------------------------
// udf_implementation_use_descriptor
//----------------------------------------------------------------------

void
udf_implementation_use_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_implementation_use_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number: %ld\n", vds_number()));
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use: XXX\n"));
	DUMP(implementation_use());
}


//----------------------------------------------------------------------
// udf_partition_descriptor
//----------------------------------------------------------------------

void
udf_partition_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_partition_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                %ld\n", vds_number()));
	PRINT(("partition_flags:           %d\n", partition_flags()));
	PRINT(("partition_flags.allocated: %s\n", allocated() ? "true" : "false"));
	PRINT(("partition_number:          %d\n", partition_number()));
	PRINT(("partition_contents:\n"));
	DUMP(partition_contents());
	PRINT(("partition_contents_use:    XXX\n"));
	DUMP(partition_contents_use());
	PRINT(("access_type:               %ld\n", access_type()));
	PRINT(("start:                     %ld\n", start()));
	PRINT(("length:                    %ld\n", length()));
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use:        XXX\n"));
	DUMP(implementation_use());
}

//----------------------------------------------------------------------
// udf_logical_descriptor
//----------------------------------------------------------------------

void
udf_logical_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_logical_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                %ld\n", vds_number()));
	PRINT(("character_set:\n"));
	DUMP(character_set());
	CS0String string(logical_volume_identifier());
	PRINT(("logical_volume_identifier: `%s'\n", string.String()));
	PRINT(("logical_block_size:        %ld\n", logical_block_size()));
	PRINT(("domain_id:\n"));
	DUMP(domain_id());
	PRINT(("logical_volume_contents_use:\n"));
	DUMP(logical_volume_contents_use());
	PRINT(("file_set_address:\n"));
	DUMP(file_set_address());
	PRINT(("map_table_length:          %ld\n", map_table_length()));
	PRINT(("partition_map_count:       %ld\n", partition_map_count()));
	PRINT(("implementation_id:\n"));
	DUMP(implementation_id());
	PRINT(("implementation_use:\n"));
	DUMP(implementation_use());
	PRINT(("integrity_sequence_extent:\n"));
	DUMP(integrity_sequence_extent());
	// \todo dump partition_maps
}


//----------------------------------------------------------------------
// udf_unallocated_space_descriptor
//----------------------------------------------------------------------

void
udf_unallocated_space_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_unallocated_space_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("vds_number:                  %ld\n", vds_number()));
	PRINT(("allocation_descriptor_count: %ld\n", allocation_descriptor_count()));
	// \todo dump alloc_descriptors
}


//----------------------------------------------------------------------
// udf_terminating_descriptor
//----------------------------------------------------------------------

void
udf_terminating_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_terminating_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
}

//----------------------------------------------------------------------
// udf_file_set_descriptor
//----------------------------------------------------------------------

void
udf_file_set_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS | CF_DUMP, "udf_file_set_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("recording_date_and_time:\n"));
	DUMP(recording_date_and_time());
	PRINT(("interchange_level: %d\n", interchange_level()));
	PRINT(("max_interchange_level: %d\n", max_interchange_level()));
	PRINT(("character_set_list: %ld\n", character_set_list()));
	PRINT(("max_character_set_list: %ld\n", max_character_set_list()));
	PRINT(("file_set_number: %ld\n", file_set_number()));
	PRINT(("file_set_descriptor_number: %ld\n", file_set_descriptor_number()));
	PRINT(("logical_volume_id_character_set:\n"));
	DUMP(logical_volume_id_character_set());
	PRINT(("logical_volume_id:\n"));
	DUMP(logical_volume_id());
	PRINT(("file_set_charspec:\n"));
	DUMP(file_set_charspec());
	PRINT(("file_set_id:\n"));
	DUMP(file_set_id());
	PRINT(("copyright_file_id:\n"));
	DUMP(copyright_file_id());
	PRINT(("abstract_file_id:\n"));
	DUMP(abstract_file_id());
	PRINT(("root_directory_icb:\n"));
	DUMP(root_directory_icb());
	PRINT(("domain_id:\n"));
	DUMP(domain_id());
	PRINT(("next_extent:\n"));
	DUMP(next_extent());
	PRINT(("system_stream_directory_icb:\n"));
	DUMP(system_stream_directory_icb());
}

void
udf_file_id_descriptor::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_VOLUME_OPS, "udf_file_id_descriptor");
	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("version_number:            %d\n", version_number()));
	PRINT(("may_be_hidden:             %d\n", may_be_hidden()));
	PRINT(("is_directory:              %d\n", is_directory()));
	PRINT(("is_deleted:                %d\n", is_deleted()));
	PRINT(("is_parent:                 %d\n", is_parent()));
	PRINT(("is_metadata_stream:        %d\n", is_metadata_stream()));
	PRINT(("id_length:                 %d\n", id_length()));
	PRINT(("icb:\n"));
	DUMP(icb());
	PRINT(("implementation_use_length: %d\n", is_parent()));
	PRINT(("id: `"));
	for (int i = 0; i < id_length(); i++)
		SIMPLE_PRINT(("%c", id()[i]));
	SIMPLE_PRINT(("'\n"));
}

void
udf_icb_entry_tag::dump()
{
	DUMP_INIT(CF_PUBLIC, "udf_icb_entry_tag");
	PRINT(("prior_entries: %ld\n", prior_recorded_number_of_direct_entries()));
	PRINT(("strategy_type: %d\n", strategy_type()));
	PRINT(("strategy_parameters:\n"));
	DUMP(strategy_parameters());
	PRINT(("entry_count: %d\n", entry_count()));
	PRINT(("file_type: %d\n", file_type()));
	PRINT(("parent_icb_location:\n"));
	DUMP(parent_icb_location());
	PRINT(("all_flags: %d\n", flags()));
	
/*
	uint32 prior_recorded_number_of_direct_entries;
	uint16 strategy_type;
	array<uint8, 2> strategy_parameters;
	uint16 entry_count;
	uint8 reserved;
	uint8 file_type;
	udf_logical_block_address parent_icb_location;
	union {
		uint16 all_flags;
		struct {
			uint16	descriptor_flags:3,			
					if_directory_then_sort:1,	//!< To be set to 0 per UDF-2.01 2.3.5.4
					non_relocatable:1,
					archive:1,
					setuid:1,
					setgid:1,
					sticky:1,
					contiguous:1,
					system:1,
					transformed:1,
					multi_version:1,			//!< To be set to 0 per UDF-2.01 2.3.5.4
					is_stream:1,
					reserved_icb_entry_flags:2;
		} flags;
	};

*/

}

void
udf_icb_header::dump()
{
	DUMP_INIT(CF_PUBLIC | CF_DUMP, "udf_icb_header");

	PRINT(("tag:\n"));
	DUMP(tag());
	PRINT(("icb_tag:\n"));
	DUMP(icb_tag());
	
}
