note
	description: "A package file."
	author: "Louis Marchand"
	date: "Thu, 02 Apr 2015 03:58:25 +0000"
	revision: "2.0"

class 
	CPF_PACKAGE_FILE

inherit
	GAME_FILE
		rename
			make as make_obsolete
		export
			{NONE} append, basic_store, copy_to, create_read_write, extend, fill, force, general_store, independent_store, make_create_read_write, make_obsolete, make_open_append, make_open_read, make_open_read_append, make_open_read_write, make_with_name, make_with_path, new_line, open_append, open_read_append, open_read_write, open_write, prune_all, put, put_boolean, put_character, put_double, put_integer, put_integer_8, put_integer_16, put_integer_32, put_integer_64, put_natural_8, put_natural_16, put_natural_32, put_natural_64, put_managed_pointer, put_natural, put_natural_16_big_endian, put_natural_16_little_endian, put_natural_32_big_endian, put_natural_32_little_endian, put_natural_64_big_endian, put_natural_64_little_endian, put_new_line, put_real, putreal, putdouble, putbool, putchar, putint, putstring, recreate_read_write, reopen_append, reopen_read_append, reopen_read_write, reopen_write
			{FILE} put_string
		redefine
			prunable
		end

create 
	make,
	make_thread_safe,
	make_open,
	make_open_tread_safe

feature {NONE} -- Initialization

	make (a_filename: READABLE_STRING_GENERAL)
			-- Initialisation of the package file
		do
			make_with_name (a_filename)
			create {ARRAYED_LIST [POINTER]} cpf_infos.make (0)
			create {ARRAYED_LIST [TUPLE [pos: INTEGER_32; length: INTEGER_32]]} sub_files_infos.make (0)
			is_thread_safe := False
			is_valid := False
		end

	make_thread_safe (a_filename: READABLE_STRING_GENERAL)
			-- Initialisation of the package file with thread safe mecanisme
		do
			make (a_filename)
			is_thread_safe := True
		ensure
			make_thread_safe_mutex_valid: internal_mutex.is_set
		end

	make_open (a_filename: READABLE_STRING_GENERAL)
			-- Initialisation and opening of the package file
		do
			make (a_filename)
			open
		end

	make_open_tread_safe (a_filename: READABLE_STRING_GENERAL)
			-- Initialisation and opening of the package file with thread safe mecanisme
		do
			make_thread_safe (a_filename)
			open
		end
	
feature -- Access

	is_valid: BOOLEAN
			-- Is Current a valid package file

	open
			-- Open the package file
			-- A package file is always read-only
		local
			l_temp_ptr: POINTER
		do
			open_read
			is_valid := True
			process_cpf_file
			if is_valid then
				select_sub_file (0)
				cpf_infos := create {ARRAYED_LIST [POINTER]}.make (sub_files_count)
				from
					sub_files_infos.start
				until
					sub_files_infos.exhausted
				loop
					l_temp_ptr := l_temp_ptr.memory_alloc (Custom_package_file_infos_size)
					{CPF_EXTERNAL}.set_custom_package_infos_struct_file_ptr (l_temp_ptr, file_pointer)
					{CPF_EXTERNAL}.set_custom_package_infos_struct_start_offset (l_temp_ptr, sub_files_infos.item.pos.to_integer_64)
					{CPF_EXTERNAL}.set_custom_package_infos_struct_total_size (l_temp_ptr, sub_files_infos.item.length.to_integer_64);
					cpf_infos.extend (l_temp_ptr);
					sub_files_infos.forth
				end
				select_sub_file (0)
			end
		end

	select_sub_file (a_index: INTEGER_32)
			-- In Current, go to the sub-file identified by a_index
		require
			cpf_file_is_valid: is_valid
		do
			if a_index = 0 then
				current_sub_file_first_position := 0
				current_sub_file_count := count
				go_in_current_sub_file (0)
			else
				current_sub_file_first_position := sub_files_infos.at (a_index).pos
				current_sub_file_count := sub_files_infos.at (a_index).length
				go_in_current_sub_file (0)
			end
		end

	current_sub_file_index: INTEGER_32
			-- Retreive the index of the sub-file that the cursor is presently in.
		require
			cpf_file_is_valid: is_valid
		local
			l_is_found: BOOLEAN
		do
			Result := 0
			from
				l_is_found := False;
				sub_files_infos.start
			until
				l_is_found or else sub_files_infos.exhausted
			loop
				if position >= sub_files_infos.item.pos and then position < sub_files_infos.item.pos + sub_files_infos.item.length then
					Result := sub_files_infos.index
					l_is_found := True
				end;
				sub_files_infos.forth
			end
		end

	is_position_in_selected_sub_file: BOOLEAN
			-- True if the present position in Current is in the sub-file that is selected
		require
			cpf_file_is_valid: is_valid
		do
			Result := position >= current_sub_file_first_position and then position <= current_sub_file_last_position
		end

	current_sub_file_position: INTEGER_32
			-- Return the current stream offset (position) in the file.
		require
			cpf_file_is_valid: is_valid
			file_index_is_in_file: is_position_in_selected_sub_file
		do
			Result := position - current_sub_file_first_position
		end

	go_in_current_sub_file (a_offset: INTEGER_32)
			-- Place the stream offset at offset position after the begining of the file.
			-- The offset value must be posifive.
		require
			cpf_file_is_valid: is_valid
			file_seek_from_begining_offset_positive: a_offset >= 0 and then a_offset <= current_sub_file_last_position
		do
			go (a_offset + current_sub_file_first_position)
		ensure
				position = a_offset
		end

	move_in_current_sub_file (a_offset: INTEGER_32)
			-- Place the stream offset at offset position after (or before if offset is negative) the index.
		require
			cpf_file_is_valid: is_valid
			file_seek_from_index_is_in_file: is_position_in_selected_sub_file
			file_seek_from_index__is_valid: (a_offset >= 0 and then a_offset <= current_sub_file_last_position - position) or else (a_offset < 0 and then a_offset.abs <= position)
		do
			go_in_current_sub_file (a_offset + current_sub_file_first_position)
		end

	recede_in_current_sub_file (a_offset: INTEGER_32)
			-- Place the stream offset at offset position before the end of the file.
			-- The offset value must be negative.
		require
			custom_file_seek_from_end_offset_negative: a_offset <= 0
			cpf_file_is_valid: is_valid
		do
			go (current_sub_file_last_position + a_offset)
		end

	current_sub_file_first_position: INTEGER_32
			-- The position in Current that the present sub-file start

	current_sub_file_last_position: INTEGER_32
			-- The position in Current that the present sub-file end
		require
			cpf_file_is_valid: is_valid
		do
			Result := current_sub_file_first_position + current_sub_file_count - 1
		end

	current_sub_file_count: INTEGER_32
			-- The number of byte of the present sub-file

	file_index: INTEGER_32
			-- The index identifier of the presently selected sub-file
	
feature -- CPF informations

	sub_files_infos: LIST [TUPLE [pos: INTEGER_32; length: INTEGER_32]]
			-- Position and length of every sub files in the package file

	sub_files_count: INTEGER_32
			-- The number of sub-file inside Current
		require
			cpf_file_is_valid: is_valid
		do
			Result := sub_files_infos.count
		end

	prunable: BOOLEAN
			-- Is there an item to be removed?
		do
			Result := False
		end
	
feature {CPF_RESSOURCE_MANAGER} -- The C pointer to the file infos structure

	internal_pointer: POINTER
			-- The internal pointer of the package file C structure
		require
			cpf_file_is_valid: is_valid
				current_sub_file_index /= 0
		do
			Result := cpf_infos.at (current_sub_file_index)
		end
	
feature {NONE} -- Implementation - Routine

	process_cpf_file
			-- Valid that Current is a valid CPF file and retreive the informations
			-- of the file.
		local
			nbr: NATURAL_16
			pos, length: NATURAL_32
			i: INTEGER_32
		do
			go (0)
			read_natural_8
			if last_natural_8 /= 67 then
				is_valid := False
			end
			read_natural_8
			if last_natural_8 /= 80 then
				is_valid := False
			end
			read_natural_8
			if last_natural_8 /= 70 then
				is_valid := False
			end
			if can_read_16 then
				read_natural_16_big_endian
				nbr := last_natural_16
				sub_files_infos := create {ARRAYED_LIST [TUPLE [pos: INTEGER_32; length: INTEGER_32]]}.make (nbr.to_integer_32)
				from
					i := 1
				until
					i > nbr.to_integer_32 or not is_valid
				loop
					if can_read_32 then
						read_natural_32_big_endian
						pos := last_natural_32
						if can_read_32 then
							read_natural_32_big_endian
							length := last_natural_32;
							sub_files_infos.extend ([pos.to_integer_32, length.to_integer_32])
							i := i + 1
						else
							is_valid := False
						end
					else
						is_valid := False
					end
				end
			else
				is_valid := False
			end
		end
	
feature {NONE} -- Implemetntation - Variables

	cpf_infos: LIST [POINTER]
			-- The internal CustomPackageFileInfos C structures

	Custom_package_file_infos_size: INTEGER_32
			-- The size in byte of a CustomPackageFileInfos C structure
		once
			Result := {CPF_EXTERNAL}.c_sizeof_custom_package_file_infos
		end
	
invariant
	file_stream_ptr_not_null: not file_pointer.is_default_pointer

end -- class CPF_PACKAGE_FILE

Generated by ISE EiffelStudio