implementation module pdWriteState;

// winOS

import ReadWriteState;
import SymbolTable;
import State;
import ExtFile;
import ExtArray;

loopAonOutput f a output :== loopAonOutput2 f a output
where {
	loopAonOutput2 f a output
		#! (s_a,a)
			= usize a;
		#! output
			= fwritei s_a output;
		= loopA f a output;
}

write_xcoff :: !*Xcoff !*File -> (!*Xcoff,!*File);
write_xcoff xcoff=:{file_name,symbol_table,n_symbols} output
	#! output 
		= fwrites file_name output;
	#! output
		= fwritec '\n' output;
	#! (symbol_table,output)
		= write_symbol_table symbol_table output;
	#! output
		= fwritei n_symbols output;
	= ({xcoff & symbol_table = symbol_table},output);
where {
	write_symbol_table symbol_table=:{text_symbols,data_symbols,bss_symbols,imported_symbols,section_symbol_ns,symbols} output
		#! output = output
			THEN write_symbol_index_list text_symbols
			THEN write_symbol_index_list data_symbols
			THEN write_symbol_index_list bss_symbols
			THEN write_symbol_index_list imported_symbols
			;
		#! (section_symbol_ns,output)
			= loopAonOutput fwritei section_symbol_ns output;
		#! (symbols,output)
			= loopAonOutput write_symbol symbols output;
		= ({symbol_table & section_symbol_ns = section_symbol_ns},output);
	where {
		write_symbol_index_list sil output
			# output
				= fwritei (length sil 0) output;
			= write_symbol_index_list sil output;
			where {
				write_symbol_index_list EmptySymbolIndex output
					= output;
				write_symbol_index_list (SymbolIndex symbol_n sil) output
					= write_symbol_index_list sil (fwritei symbol_n output);
				
				length EmptySymbolIndex i		= i;
				length (SymbolIndex _ sil) 	i	= length sil (inc i);	
			} // write_symbol_index_list
			

	}	

}

/*
	

*/
WriteSymbolTable :: !SSymbolTable !*File -> !*File;
WriteSymbolTable symboltable=:{text_symbols,data_symbols,bss_symbols,imported_symbols,section_symbol_ns,symbols} output
	#! output 
		= WriteSymbolIndexList text_symbols output;
	#! output 
		= WriteSymbolIndexList data_symbols output;
	#! output 
		= WriteSymbolIndexList bss_symbols output;
	#! output 
		= WriteSymbolIndexList imported_symbols output;
	#! output
		= WriteIntArray section_symbol_ns output;
	#! output
		= WriteSymbolArray symbols output;
	= output;

	where	
	{
		WriteSymbolIndexList symbols output
			#! output 
				= fwritei (count 0 symbols) output;
			= write_symbol_index_list symbols output;
			where
			{
			 	count i EmptySymbolIndex 
					= i;
				count i (SymbolIndex _ sil)
					= count (inc i) sil;
					
				write_symbol_index_list EmptySymbolIndex output
					= output;
				write_symbol_index_list (SymbolIndex i sil) output
					#! output
						= fwritei i output;
					= write_symbol_index_list sil output;
			} // WriteSymbolIndexList
					
		WriteIntArray :: !{#Int} !*File -> !*File;
		WriteIntArray array output
			#! output
				= fwritei (size array) output;
			= write_int_array 0 (size array) array output;
		where
		{
			write_int_array i limit array output
				| i == limit
					= output;
					
					#! output
						= fwritei array.[i] output;
					= write_int_array (inc i) limit array output;
		}
					
		WriteSymbolArray :: {!Symbol} !*File -> !*File;
		WriteSymbolArray array output
			= write_symbol_array 0 (size array) array output;
		where
		{
			write_symbol_array :: !Int !Int {!Symbol} !*File -> !*File;
			write_symbol_array i limit array output
				| i == limit
					= output;
					
					#! output
						= WriteSymbol array.[i] output;
					= write_symbol_array (inc i) limit array output;
		}
	}

WriteXCoff :: SXcoff !*File -> !*File;
WriteXCoff xcoff=:{file_name,symbol_table,n_symbols} output
	#! output 
		= fwrites file_name output;
	#! output
		= fwritec '\n' output;
	#! output
		= fwritei n_symbols output;
	= WriteSymbolTable symbol_table output;

	
WriteSymbol :: !Symbol !*File -> !*File;
WriteSymbol (Module i0 i1 i2 i3 i4 i5 s) output
	#! output
		= fwritec (toChar MODULE_SYMBOL) output;
	#! output
		= fwritei i0 output;
	#! output
		= fwritei i1 output;
	#! output
		= fwritei i2 output;
	#! output
		= fwritei i3 output;
	#! output
		= fwritei i4 output;
	#! output
		= fwritei i5 output;
	#! output
		= fwritei (size s) output;
	#! output
		= fwrites s output;	
	= output;
	
WriteSymbol (Label i0 i1 i2) output
	#! output
		= fwritec (toChar LABEL_SYMBOL) output;
	#! output
		= fwritei i0 output;
	#! output
		= fwritei i1 output;
	#! output
		= fwritei i2 output;
	= output;
	
WriteSymbol (SectionLabel i0 i1) output
	#! output
		= fwritec (toChar SECTIONLABEL_SYMBOL) output;
	#! output
		= fwritei i0 output;
	#! output
		= fwritei i1 output;
	= output;

WriteSymbol (ImportLabel s) output
	#! output
		= fwritec (toChar IMPORTLABEL_SYMBOL) output;
	#! output
		= fwritei (size s) output;
	#! output
		= fwrites s output;
	= output;
	
WriteSymbol (ImportedLabel i0 i1) output
	#! output
		= fwritec (toChar IMPORTEDLABEL_SYMBOL) output ;
	#! output
		= fwritei i0 output;
	#! output
		= fwritei i1 output;
	= output;
	
WriteSymbol (ImportedLabelPlusOffset i0 i1 i2) output
	#! output
		= fwritec (toChar IMPORTEDLABELPLUSOFFSET_SYMBOL) output ;
	#! output
		= fwritei i0 output;
	#! output
		= fwritei i1 output;
	#! output
		= fwritei i2 output;
	= output;
	
WriteSymbol (ImportedFunctionDescriptor i0 i1) output
	#! output
		= fwritec (toChar IMPORTEDFUNCTIONDESCRIPTOR_SYMBOL) output ;
	#! output
		= fwritei i0 output;
	#! output
		= fwritei i1 output;
	= output;
	
WriteSymbol (EmptySymbol) output
	#! output
		= fwritec (toChar EMPTYSYMBOL_SYMBOL) output;
	= output;
							
import CommonObjectToDisk;
import LinkerOffsets;
import ExtInt;

import DebugUtilities;

/*
VOOR JOHN:
instance Output !(!*{#Char},!*File)
where {
	WriteOutput  :: !*WriteOutputRecord !*(!*{#Char},!*File) -> *(!*State,*(!*{#Char},!*File));
	WriteOutput {file_or_memory=write_kind,offset, string,state,file_n} (data,pe_file)
		#! aligned_offset
			= roundup_to_multiple offset 4;
		#! (data,pe_file)
			= case write_kind of {
				0
					// .text
					#! delta
						= aligned_offset - offset;
					#! pe_file
						= write_n_bytes delta pe_file;
					#! pe_file
						= fwrites string pe_file;	 
					-> (data,pe_file);
				1
					// .data
					/*
					#! (s_data,data2)
						= usize data;
					#! new_size
						= aligned_offset + size string;
					*/
				
					#! data3
						= case /*(new_size < s_data)*/ True of {
							True
								// buffer big enough
								#! s
									= "hier"; //"required: " +++ toString new_size +++ "old: " +++ toString s_data;
								| F s True
							//	#! (_,data2)
							//		= usize data2;
								-> data; //data2;
							False
							/*
								// buffer too small
	 							#! new_buffer_size
									= min (roundup_to_multiple new_size next_buffer_size_factor) (s_data + next_buffer_size_factor);
								#! (_,data)
									= copy 0 data 0 (createArray new_buffer_size '\0');
							*/
								-> abort "buffer too small"; //data;
						};
					
			
					//#! (_,data)
					//	= copy 0 string aligned_offset data;
					-> (data3,pe_file);
				
			};
		= (state,(data,pe_file));
	where {
		copy :: !Int !{#Char} !Int !*{#Char} -> !(!Int,!*{#Char});
		copy i s j d
			| i == size s
				= (j,d);
				= copy (inc i) s (inc j) {d & [j + 0] = s.[i]};
			
		write_n_bytes :: !Int !*File -> !*File;
		write_n_bytes 0 pe_file
			= pe_file;
		write_n_bytes n pe_file
			= write_n_bytes (dec n) (fwritec '\0' pe_file);
	}
};
*/

instance Output !(!*{#Char},!*File)
where {
	WriteOutput  :: !*WriteOutputRecord !*(!*{#Char},!*File) -> *(!*State,*(!*{#Char},!*File));
	WriteOutput {file_or_memory=write_kind,offset,module_n, string,state,file_n} (data,pe_file)
		// filepointer of module within complement
		#! (module_fp,pe_file)
			= fposition pe_file;
		
		// write to disk or buffer
		#! aligned_offset
			= roundup_to_multiple offset 4;
		#! o
			= case write_kind of {
				0
					// .text
					#! delta
						= aligned_offset - offset;
					#! pe_file
						= write_n_bytes delta pe_file;
					#! pe_file
						= fwrites string pe_file;	 
					-> (state,(data,pe_file));
				1
					// .data
					#! (s_data,data)
						= usize data;
					#! new_size
						= aligned_offset + size string;
				
					#! data
						= case (new_size < s_data) of {
							True
								// buffer big enough
								//#! (_,data)
								//	= usize data;
								-> data; //data2;
							False
								// buffer too small
	 							#! new_buffer_size
									= min (roundup_to_multiple new_size next_buffer_size_factor) (s_data + next_buffer_size_factor);
								#! (_,data)
									= copy 0 data 0 (createArray new_buffer_size '\0');
								-> data;
						};
					#! (_,data)
						= copy 0 string aligned_offset data;
						
					// update current module symbol
					#! (Module i0 i1 i2 i3 offset i5 s, state)
						= sel_symbol file_n module_n state;
				 	#! state
						= update_symbol (Module i0 i1 i2 i3 module_fp i5 s) file_n module_n state; 
					-> (state,(data,pe_file));
				
			};
		= o;
	where {
		copy :: !Int !{#Char} !Int !*{#Char} -> !(!Int,!*{#Char});
		copy i s j d
			| i == size s
				= (j,d);
				= copy (inc i) s (inc j) {d & [j + 0] = s.[i]};
			
		write_n_bytes :: !Int !*File -> !*File;
		write_n_bytes 0 pe_file
			= pe_file;
		write_n_bytes n pe_file
			= write_n_bytes (dec n) (fwritec '\0' pe_file);
	}
};

s_initial_buffer		:== 8192;
next_buffer_size_factor :== 4096;

// write raw data of unmarked symbols
write_raw_data :: !*State !*File !*Files -> (!*State,!*File,!*Files);
write_raw_data state=:{n_xcoff_files} output files
	// first free offset after raw data
	#! output
		= fwritei 0 output;
		
	#! s_virtual_data_section
		= s_initial_buffer;
		
	// write text symbols
	#! ((data,output),(text_end,data_end),state,files)
		= write_code_to_pe_files n_xcoff_files False 0 0 (0,0) state True (createArray s_virtual_data_section '\0',output) files;
	#! output
		= fwrites (data % (0,dec data_end)) output;
	= (state,output,files);	
		
/*
	#! (xcoff_a,state)
		= select_xcoff_a state
	#! xcoff_list 
		= xcoff_array_to_list 0 xcoff_a
 
	#! (module_offset_a,state)
		= select_module_offset_a state
	#! (inverted_marked_bool_a,state)
		= select_marked_bool_a state
	// pc
	#! (inverted_marked_bool_a,text_end,module_offset_a,xcoff_list) 
		= compute_module_offsets Text 0 /* base */ xcoff_list 0 	   0 inverted_marked_bool_a module_offset_a
	#! (inverted_marked_bool_a,data_end,module_offset_a,xcoff_list)
		= compute_module_offsets Data 0 /* base */ xcoff_list text_end 0 inverted_marked_bool_a module_offset_a

	#! state = 
		{ state &
			xcoff_a = xcoff_list_to_array n_xcoff_files xcoff_list, // PC xcoff_list_to_xcoff_array xcoff_list n_xcoff_files,
			module_offset_a = module_offset_a,
			marked_bool_a = inverted_marked_bool_a
			
		}
		// xcoff_list_to_array
	#! output
		= fwritei data_end output

	// moved	
	#! alignment
		= 2;
	#! alignment_mask
		= dec (1 << alignment);
	#! aligned_text_end
		= (text_end + alignment_mask) bitand (bitnot alignment_mask);
	#! delta
		= aligned_text_end - text_end;

	// inserted
	#! nop_byte
		= toChar 0x90;
	#! s_data_section
		= data_end - aligned_text_end;
	

	#! ((data,output),state,files)
		= write_code_to_pe_files n_xcoff_files False 0 0 (0,0) state True ("",output) files

	#! nop_byte
		= toChar 0x90;
	# output
		= fwrites (createArray delta nop_byte) output	
		
	#! (i,output)
		= fposition output
	| i <> (4 + text_end + delta)
		= abort ("WriteState: computed text size does not correspond with file offset" +++ (toString i))
	
	#! output
		= fwrites data output
	#! (i,output)
		= fposition output
	#! required_offset
		= 4 + data_end
	| i <> required_offset
		= abort ("Real: " +++ (toString required_offset) +++ " - " +++ (toString i))
		
	= (state,output,files)

instance Output (!{#Char},!*File)
where
//	WriteOutput :: !WriteOutputRecord /*!Int !Int !{#Char}*/ (!*{#Char},!*File) -> (!*{#Char},!*File);
	WriteOutput {file_or_memory,offset,string} /*0 _ string*/ (data,file)
		= case file_or_memory of {
			0
				-> (data, fwrites string file);
			1
				-> (data +++ string, file);
				
			_
				-> abort "WriteState: internal error";
		};
			
	ChangeState {file_n,module_n,state} pe_file
		#! (Module i0 i1 i2 i3 offset i5 s, state)
			= sel_symbol file_n module_n state	
		/*
		** Retrieve the computed offset of module_n in the file
		*/	
		#! (first_symbol_n,state)
			= selacc_marked_offset_a file_n state
		#! (module_n_offset, state)
			= selacc_module_offset_a (first_symbol_n+module_n) state;	
		
	 	#! state
			= update_symbol (Module i0 i1 i2 i3 (module_n_offset+4) i5 s) file_n module_n state 
		= (state,pe_file);
*/
	