Inside Mod Manager v12 Formulas

From TNG_Wiki
Jump to navigation Jump to search

File Modification Algorithms

White Space and Buffer Pointers

Three of the top level classes -- Modlister, Modinstaller, Modremover -- use formulas to handle file modifications, depending on the optag.

The following are optag directives that cause a modification to a file in a certain way. They fall into two types: Block and Inline.

Block operations

  • replace
  • insert:before
  • insert:after
  • vinsert:before
  • vinsert:after

Block operations deal with code in a block, from the beginning of the first line to the end of the last line, and include all leading and trailing white space as found in the target file.

To reduce bad target errors, we remove leading and trailing spaces and tabs (not blank lines) from the TNG target text to use as a search string. When found, the start and ending positions of the text in the buffer are adjusted to include leading and trailing spaces and tabs. Then all of the text in between the markers is subject to modification.

Finding the true starting position of a block of trimmed code, we set variable $p to the beginning of the trimmed code in the buffer, then move it backward, checking each character at the buffer[$p] position, until we have included all the white space from the beginning of the line.

$p=$i // beginning of trimmed text
$fragment = false;
while( $p > 0 ) {
   $p--;
   if( $target_file_contents[$p] == "\n" ) {
      $p++;
      break;
   }
   if( $target_file_contents[$p] != " " && $target_file_contents[$p] != "\t" ) {
      $fragment = true;
   }
}
$start = $p;

With standardized line breaks (CRLF=="\r\n"==hex(0D0A)), finding a "\n" means we are at the end of the previous line, so $p++ is the starting position for the whole block of text. If we have not found the ending of the previous line, we continue moving backward, testing each character as we go. If the character is a space or a tab character, we skip it as valid. If we find any other character we log a "fragment not allowed" error and continue processing with the next Mod Manager tag in the parse table.

Locating the ending position is done the same way, except we start at the end of the trimmed code in the buffer and move forward through spaces and tabs until we find a "\r".

$p = $i + strlen( $tngcode ); // end of trimmed target text
while( true ) {
   if( !isset($target_file_contents[$p] ) ) { // EOF - no CRLF
     break;
   }
   if( $target_file_contents[$p] == "\r" ) { // CRLF
     break;
   }
   if( $target_file_contents[$p] != " " && $target_file_contents[$p] != "\t" ) {
      $fragment = true;
   }
   $p++;
}
$end = $p;
$length = ( $end - $start ) + 1;

Trailing spaces and tabs are considered "hidden" white space, which we are now removing from the entire buffer before it is processed, so no white space should be found here anymore.

Inline operations

  • trimreplace
  • triminsert:before
  • triminsert:after

Inline operations work on fragments of text where leading white space must be honored. White space added before a CRLF in the mod file will be removed as "hidden" white space, so it is important not to depend on it. Target should always end with a visible character and applied text should always end with a visible character or CRLF.

Even though Inline ops begin with 'trim', no trimming is done by them.

How it Works

Here we explore in some detail how Mod Manager modifies a target file in response to different optags. For the sake of discussion we refer to the text following a location tag in the mod config file as the target code, and the text following the optag as the mod code.

Preliminary processing may require concatenating mod code with line endings (\r\n), or in the case of inline operations, with the target code, calculating starting and end positions in the buffer, and finding the length of the inserted or replacement text.

The preparation of mod code depends on the optag. If the mod code is to be inserted before the target code, then the insertion text will be mod code + \r\n and it will be inserted at the starting position of the target code during installation. If mod code is to be inserted after the target code, we use \r\n + mod code and insert it at the ending position of the target code. If the mod code is a simple replacement for the target code, the whole block of target code is replaced with the whole block of mod code.

Removal of an installed block of code is simpler; find the code (plus any included \r\n) and delete or replace it. This works because Modinstaller ensures the inserted code is unique before installing it.

A sharp-eyed reader may notice that when restoring a bloc of replaced text, the white space from the mod code may not be exactly the same as the original white space in the target code. To our knowledge this has never caused a problem, but if it does, we will need to use the same pointer manipulation to find the starting and ending positions when we "unreplace" a block of mod code just as we did when installing it.

Optags

The PHP function which actually modifies file buffers is

substr_replace( $target_file_contents, $newcode, $pos, $len );

It is important to understand this function to understand what Mod Manager is doing in preparation to modify a target file.

replace

Replace is a block operation.

When installing the mod we create a trimmed version of the text for the sole purpose of a buffer search. Removing leading and trailing spaces and tabs avoids meaningless bad target errors due to white space mismatch.

$tngcode = $tng_code_trimmed; 
$newcode = $new_code;

$tngcode is the trimmed text (spaces and tabs only) following a %location tag in the mod configuration file. $newcode is the text, including leading and trailing blank lines, following a %replace tag in the mod configuration file.

We mark the start and end positions of the full block of target text in the buffer to include leading and trailing white space. See White Space and Buffer Pointers for details on how we do this.

$start: $tng_code_trimmed + leading white space
$end: $tng_code_trimmed + trailing white space

If additional text is found between the start-end markers, we throw an illegal fragment error. If this is not done, the replaced text that was not included in the target code would be lost and Mod Manager could not restore it.

We calculate the starting position and length of the expanded TNG code to be replaced. Then we make the replacement in the buffer, later to be written back to the target file.

$target_file_contents = substr_replace( $target_file_contents, $newcode, $pos, $len );

To remove the mod, we find the mod code in the buffer and replace it with the target code. Because the installed mod code includes the exact white space provided by the mod config file, they should always match, so there is no need to set start-end markers.


insert:before

Insert:before is a block operation.

When installing a mod we create a trimmed version of the text for the sole purpose of a buffer search. Searching for the trimmed text will reduce meaningless bad target errors due to white space mismatch.

$tngcode = $tng_code_trimmed;
$newcode = $new_code."\r\n";

We mark the start and end positions of the target text in the buffer to include leading and trailing spaces and tabs. See White Space and Buffer Pointers for details on how we do this.

For this operation the target code does not have to be a complete block. The start position will be used as an anchor in front of which to insert new text.

We need to add a line break to the end of the mod code (text+\r\n) to separate it from the target text that follows it.

We calculate the buffer starting point. The length will be zero since we are not replacing any current text. Finally, we insert it into the buffer.

 
$target_file_contents = substr_replace( $target_file_contents, $newcode, $pos, $len );

To remove the mod code, we add an \r\n to the end of it, then replace it in the buffer with nothing.


insert:after

Insert:after is a block operation.

When installing a mod, we create a trimmed version of the text for the sole purpose of a buffer search. Searching for the trimmed text will reduce meaningless bad target errors due to white space mismatch.

$tngcode = $tng_code_trimmed;
$newcode = "\r\n".$new_code;

If installing the mod, we mark the start and end positions of the target text in the buffer to include leading and trailing spaces and tabs. See White Space and Buffer Pointers for details on how we do this.

For this operation the target code does not have to be a complete block. The end position will be used as an anchor after which to insert new text.

We need to add a line break to the beginning of the mod code (\r\n+text) to separate it from the target text that precedes it.

We calculate the buffer starting point. The length will be zero since we are not replacing any current text. Finally, we insert it into the buffer.

 
$target_file_contents = substr_replace( $target_file_contents, $newcode, $pos, $len );

To remove the mod code, we add an \r\n to the beginning of it, then replace it in the buffer with nothing.


vinsert:before

Vinsert:before is a block operation.

To install a variable variable, we use same formula as insert:before.

A mod may have installed one or more variables in a target file:

%vinsert:after%
$var1 = 35;
$var2 = 'mytext';
$var3 = $var1;
$var4 = "one";
%end:%

To completely uninstall inserted variables where the values can be anything, we use the following statement to grab the variable name(s) from the mod config file optag code as shown above:

preg_match_all( "#(\\$[^\s=]+)\s*=+#sm", $newcode, $matches );

The above function provides a list of variable names -- $var1, $var2, $var3, $var4.

For each listed variable in the buffer, if it is followed by a '=', we remove the text starting with the $variable name and include everything beyond the value terminator which could be ';, "; or just ;, depending on the value assigned, through the next \r\n. The variable value may occupy several lines as might be the case for a text definition in a language file like admintext.php.


vinsert:after

Vinsert:after is a block operation.

To install use same formula as insert:after.

A mod may have installed one or more variables in a target file:

%vinsert:after%
$var1 = 35;
$var2 = 'mytext';
$var3 = $var1;
$var4 = "one";
%end:%

To completely uninstall inserted variables where the values can be anything and don't matter, we use the following statement to grab the variable name(s) from the mod config file optag code as shown above:

preg_match_all( "#(\\$[^\s=]+)\s*=+#sm", $newcode, $matches );

The above function provides the list of variable names that were installed by the mod -- $var1, $var2, $var3, $var4.

For each listed variable in the buffer, if it is followed by a '=', we remove the previous \r\n and the variable line(s) through the statement terminator, which could be ';, "; or just ;, depending on the value assigned, then up to, but not including, the next line break (\r\n). The variable value may occupy several lines as might be the case for a text definition in a language file like admintext.php.


trimreplace

Trimreplace is an inline operation.

To install mod code, we we just replace the target code with the mod code, including any leading white space in the form of spaces and tabs. Trailing spaces and tabs in the mod configuration file are ignored, so it is important to select the target text in a way that makes trailing spaces/tabs unnecessary.

$tngcode = $tng_code;
$newcode = $new_code;

Nothing is trimmed.

To restore the target code, we replace the mod code with the target code as found in the mod configuration file.


triminsert:before

Triminsert:before is an inline operation.

This tag is often used to install short pieces of text into other text. To ensure we keep the inserted code unique, we concatenate the mod code + tng code and use it to replace the tng code in the target file.

$tngcode = $tng_code;
$newcode = $new_code.$tng_code;

No line breaks are appended.

We calculate the starting point of the tng code in the buffer and the length of the tng code to be replaced. Finally, we replace $tngcode with $newcode.

$target_file_contents = substr_replace( $target_file_contents, $newcode, $pos, $len );

To restore the target code, we replace mod code + target code with just the target code


triminsert:after

Triminsert:after is an inline operation.

This tag is often used to install short pieces of text into other text. To ensure we keep the inserted code unique, we concatenate the tng code + mod code and replace the tng code in the target file.

$tngcode = $tng_code;
$newcode = $tng_code.$new_code;

No line breaks are appended.

We calculate the starting point of the tng code in the buffer and the length of the tng code to be replaced. Finally, we replace $tngcode with $newcode.

$target_file_contents = substr_replace( $target_file_contents, $newcode, $pos, $len );

To restore the target code, we replace tng code + mod code with just the tng code


Inside Mod Manager v12 Content

Chapters

Inside Mod Manager v12 (Home)

  1. Mod Manager and OOP
  2. Page Headings
  3. Main Script
  4. Class Modbase
  5. Class Modparser
  6. Class Modlister
  7. Class Modinstaller
  8. Class Modeditor
  9. Class Modremover
  10. Class Moddeleter
  11. Formulas
  12. Mod Options
  13. Mod Logging
  14. Tools
  15. Issues