implementation module piObjectToDisk;

// platform independent Object to Disk

import StdEnum;

import PlatformLinkOptions, WriteState, LinkerMessages;
import ExtInt, ExtFile;

import DebugUtilities;

Analyse :: !*(!Int,!Int,!*State,!*PlatformLinkOptions,!*Files) !Int  -> !*(!Int,!Int,!*State,!*PlatformLinkOptions,!*Files);
Analyse (fp,start_rva,state=:{n_library_symbols,n_xcoff_symbols,n_libraries,library_list},platform_link_options,files) i		
	#! (is_virtual_section,i_section_header,s_virtual_data,alignment,s_raw_data,state,platform_link_options,files)
		= apply_compute_section i start_rva fp platform_link_options state files;

	// check for link errors	
	#! (ok,state)
		= IsErrorOccured state;
	| not ok
		= (fp,start_rva,state,platform_link_options,files);
		
	#! start_rva
		= start_rva + (if (s_raw_data == 0) (roundup_to_multiple s_virtual_data 4096) (roundup_to_multiple s_raw_data 4096));
	#! (fp,platform_link_options)
		= case is_virtual_section of {
			True
				-> (fp,platform_link_options);				
			False
//				| F ("Analyse fp: " +++ toString fp +++ " section: " +++ toString i) True
				// alignment
				#! s_raw_data_512
					= case s_raw_data of {
						0
							-> roundup_to_multiple s_virtual_data alignment;
						_
							// section containing raw data, but no virtual size e.g. resource section
							-> roundup_to_multiple s_raw_data alignment;
					};
				#! platform_link_options
					= plo_set_s_raw_data s_raw_data_512 i_section_header platform_link_options;
				#! platform_link_options
					= plo_set_fp_section fp i_section_header platform_link_options;
				-> (fp + s_raw_data_512,platform_link_options);
		}	
	= (fp,start_rva,state,platform_link_options,files);
	
check_fp :: !Int !Int !String !*File -> !*File; 
check_fp computed_fp i s pe_file
	# (actual_fp,pe_file)
		= fposition pe_file;
	| computed_fp <> actual_fp
		# error
			= "Generate; " +++ s +++ "\ncomputed_fp: " +++ toString computed_fp +++ " , actual_fp: " +++ toString actual_fp +++ " , section: " +++ toString i;
		= abort error;
		= pe_file;
		

Generate (pe_file,state,files,platform_link_options) i

	// debug
	# (computed_start_fp,platform_link_options)
		= plo_get_section_fp i platform_link_options;
//	# pe_file
//		= check_fp computed_start_fp i "start 1" pe_file;
		
	# (is_virtual_section,s_virtual_data,s_raw_text_section,pe_file,platform_link_options,state,files)
		= apply_generate_section i pe_file platform_link_options state files;
		
	// check for link errors	
	#! (ok,state)
		= IsErrorOccured state;
	| not ok
		= abort "Generate: errors set"; //(pe_file,state,files,platform_link_options);
		
	# (computed_end_fp,platform_link_options)
		= plo_get_section_fp i platform_link_options;
//	# pe_file
//		= check_fp (computed_end_fp + s_virtual_data) i "end (unaligned) 2" pe_file;
		
	# pe_file
		= case (is_virtual_section || s_virtual_data == 0) of {
			True
				-> pe_file;
			False
				-> write_zero_bytes_to_file (s_raw_text_section - s_virtual_data) pe_file;
		}
	
	# (computed_end_fp,platform_link_options)
		= plo_get_section_fp i platform_link_options;
//	# pe_file
//		= check_fp (computed_end_fp + s_raw_text_section) i "end (unaligned) 3" pe_file;
		
		
	= (pe_file,state,files,platform_link_options);

/*
OLD
write_object_to_disk :: !Bool .{#Char} Int Int Int .LibraryList Int *{#Bool} *{#Int} *{#*Xcoff} *Files !*NamesTable !PlatformLinkOptions
 -> (/*!Bool,*/!*State,!PlatformLinkOptions,*Files);
write_object_to_disk normal_static_link application_file_name  n_xcoff_files n_libraries n_library_symbols library_list0 
		 n_xcoff_symbols marked_bool_a offset_a xcoff_a1 
		 files namestable platform_link_options
	#! state
		= { EmptyState &
			application_name = application_file_name,
			n_libraries = n_libraries,
			n_xcoff_symbols = n_xcoff_symbols,
			n_library_symbols = n_library_symbols,
			library_list = library_list0,
			namestable = namestable,
		
			n_xcoff_files = n_xcoff_files,
			marked_bool_a = marked_bool_a,
			marked_offset_a = offset_a,
	//		module_offset_a = module_offset_a,
			xcoff_a = xcoff_a1
		};

	// determine what sections are put into an executable					
	#! (s_section_header_a,platform_link_options)
		= create_section_header_kinds platform_link_options;

	# section_header_index_list
		= [0..dec s_section_header_a];

	// get start filepointer (fp) and relative virtual start address		
	# (start_fp,platform_link_options)
		= plo_get_start_fp platform_link_options;
	# (start_rva,platform_link_options)
		= plo_get_start_rva platform_link_options;
		
	// Analyze
	#! (fp,end_rva,state,platform_link_options)
		= foldl Analyse (start_fp,start_rva,state,platform_link_options) section_header_index_list; 	
		
	// check for link errors during analysis	
	#! (ok,state)
		= st_isLinkerErrorOccured state;
	| not ok
		= (state,platform_link_options,files);
		
		
	// set end filepointer (fp) and relative virtual end address
	# platform_link_options
		= plo_set_end_rva end_rva platform_link_options;
	# platform_link_options
		= plo_set_end_fp fp platform_link_options;
		
	// open 
	#! (open_ok,pe_file,files)
		= fopen application_file_name FWriteData files;
	| not open_ok
		# open_error
			= LinkerError ("could not create '" +++ application_file_name +++ "'");
		# state
			= st_addLinkerMessage open_error state;
		= (state,platform_link_options,files);
		
	#! (pe_file,state,files,platform_link_options)
		= case open_ok of {
			True
				#! (pe_file,state,files,platform_link_options)
					= foldl Generate (pe_file,state,files,platform_link_options) section_header_index_list;
					
				// check for link errors during generation	
				#! (ok,state)
					= st_isLinkerErrorOccured state;
				| not ok
					-> (pe_file,state,files,platform_link_options);
		
				// write complement
				#! (state,files)
						= case normal_static_link of {
						 	True	->	(state,files);
						 	False	->	(WriteState state files);
						 };
						 
				// check for link errors during complement generation	
				#! (ok,state)
					= st_isLinkerErrorOccured state;
				| not ok
					-> (pe_file,state,files,platform_link_options);

				-> (pe_file,state,files,platform_link_options);

			False
				-> (pe_file,state,files,platform_link_options);
		};
		
	#! (close_ok,files)
		= fclose pe_file files;
	= (/*open_ok && close_ok,*/ state,platform_link_options,files);
*/


write_object_to_disk :: !PlatformLinkOptions !*State !*Files -> (!*State,!PlatformLinkOptions,!*Files);
write_object_to_disk platform_link_options state=:{normal_static_link,application_name = application_file_name}  files 
/*
	#! state
		= { EmptyState &
			application_name = application_file_name,
			n_libraries = n_libraries,
			n_xcoff_symbols = n_xcoff_symbols,
			n_library_symbols = n_library_symbols,
			library_list = library_list0,
			namestable = namestable,
		
			n_xcoff_files = n_xcoff_files,
			marked_bool_a = marked_bool_a,
			marked_offset_a = offset_a,
	//		module_offset_a = module_offset_a,
			xcoff_a = xcoff_a1
		};
*/

	// determine what sections are put into an executable					
	#! (s_section_header_a,state,platform_link_options)
		= create_section_header_kinds state platform_link_options;

	// check for link errors during creation of section headers	
	#! (ok,state)
		= IsErrorOccured state;
	| not ok
		= (state,platform_link_options,files);
		
	# section_header_index_list
		= [0..dec s_section_header_a];

	// get start filepointer (fp) and relative virtual start address		
	# (start_fp,platform_link_options)
		= plo_get_start_fp platform_link_options;
	# (start_rva,platform_link_options)
		= plo_get_start_rva platform_link_options;
		
	// Analyze
	#! (fp,end_rva,state,platform_link_options,files)
		= foldl Analyse (start_fp,start_rva,state,platform_link_options,files) section_header_index_list;
		
	// check for link errors during analysis	
	#! (ok,state)
		= IsErrorOccured state;
	| not ok
		= (state,platform_link_options,files);
		
		
	// set end filepointer (fp) and relative virtual end address
	# platform_link_options
		= plo_set_end_rva end_rva platform_link_options;
	# platform_link_options
		= plo_set_end_fp fp platform_link_options;
		
	// open 
	#! (open_ok,pe_file,files)
		= fopen application_file_name FWriteData files;
	| not open_ok
		# open_error
			= LinkerError ("could not create '" +++ application_file_name +++ "'");
		# state
			= AddMessage open_error state;
		= (state,platform_link_options,files);
		
	#! (pe_file,state,files,platform_link_options)
		= case open_ok of {
			True
				#! (pe_file,state,files,platform_link_options)
					= foldl Generate (pe_file,state,files,platform_link_options) section_header_index_list;
					
				// check for link errors during generation	
				#! (ok,state)
					= IsErrorOccured state;
				| not ok
					-> (pe_file,state,files,platform_link_options);
		
				// write complement
				#! (state,files)
						= case normal_static_link of {
						 	True	->	(state,files);
						 	False	->	(WriteState state files);
						 };
						 
				// check for link errors during complement generation	
				#! (ok,state)
					= IsErrorOccured state;
				| not ok
					-> (pe_file,state,files,platform_link_options);

				-> (pe_file,state,files,platform_link_options);

			False
				-> (pe_file,state,files,platform_link_options);
		};
		
	#! (close_ok,files)
		= fclose pe_file files;
	= (/*open_ok && close_ok,*/ state,platform_link_options,files);