OpenMPT-Wiki:Werkstatt/MPTPatterns/MPTPatterns.class.php
Aus OpenMPT-Wiki
Zur Navigation springenZur Suche springen
MPTPatterns.class.php[Bearbeiten]
<?php
###############################################################################
# MPTPatterns class
# (c)opyleft 2009,2011 cubaxd
###############################################################################
class MPTPatterns {
var $env=array(); // Settings
var $attr=array(); // Attributes
var $mod=array(); // Pattern data
var $seq=array(); // Input data
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* Pattern($input, $args, $parser)
*
* 'main' function
*/
function Pattern($input, $args, $parser) {
// Read attributes
$this->readAttributes($args);
// The pattern data split at the new line char
$this->seq = explode("\n", htmlspecialchars($input));
// Determine module format
$this->getFormat();
// Write input data into an array for HTML conversion
$this->readPattern();
// Convert data to HTML
return $this->printPattern();
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* readAttributes()
*
* Reads all attributes of the pattern tag.
* If an attribute X is empty/not set, the corresponding
* attr['X'] variable is set to null by the function getAttribute(),
* otherwise it will be set to the attribute's value, whith
* argument 2 ($option) of getAttribute() determining
* how to assign the value:
* 0 (null): assign value unchanged,
* 1 ($numeric): assign value only if numeric
* 2 ($lowercase): convert to lowercase letters
* 3 ($uppercase): convert to uppercase letters
*/
private function readAttributes($args) {
$numeric=1; $lowercase=2; $uppercase=3;
$this->attr['title'] = $this->getAttribute($args[$this->env['attribute']['title']]);
$this->attr['format'] = $this->getAttribute($args[$this->env['attribute']['format']], $uppercase);
$this->attr['identifier']= $this->getAttribute($args[$this->env['attribute']['identifier']], $lowercase);
$this->attr['float'] = $this->getAttribute($args[$this->env['attribute']['float']], $lowercase);
$this->attr['highlight'] = $this->getAttribute($args[$this->env['attribute']['highlight']], $numeric);
$this->attr['width'] = $this->getAttribute($args[$this->env['attribute']['width']], $numeric);
$this->attr['css'] = $this->getAttribute($args[$this->env['attribute']['css']]);
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* getAttribute($val, $option)
*
* Checks an attribute of the pattern tag, converts its value if needed
* and returns the data
*
* @param any $val
* value of the given attribute
* @param integer $option
* 0: off
* 1: check if numeric
* 2: return value in lowercase letters
* 3: return value in uppercase letters
*/
private function getAttribute($val, $option=null) {
$numeric=1; $lowercase=2; $uppercase=3;
if (isset($val)) {
switch ($option) {
case $numeric:
return ( is_numeric($val) ) ? $val : null;
case $lowercase:
return strtolower(htmlspecialchars($val));
case $uppercase:
return strtoupper(htmlspecialchars($val));
}
return htmlspecialchars($val);
}
// Attribute not set
return null;
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* getFormat()
*
* Search for identifier string in pattern, or if the search was
* unsuccessful, try to read the 'format' attribute.
* This is neccessary as we must know which effect commands to highlight
* in which color. If the format was not determined, the standard format
* will be used, which defaults to the Impulse Tracker format, but can be
* changed in the settings to any other format in the list.
*
* $attr['format'] will contain the short identifier (i.e: "S3M"), and
* $mod['format'] the long one ("ModPlug Tracker S3M")
*/
private function getFormat() {
// try to read Identifier string
// (which usually begins with "ModPlug Tracker ")
$this->mod['format']="";
for ($i=0; $i<count($this->seq); $i++) {
// compare current $input line with ID strings
for ($j=0; $j<count($this->env['format_long']); $j++) {
if ($this->seq[$i] == $this->env['format_long'][$j]) {
$this->mod['format']=$this->env['format_long'][$j];
break;
}
}
// Identifier found, we can abort the loop now
if ($this->mod['format'] && $this->mod['format']!="")
break;
// as soon as the first '|' character appears, it is unlikely for
// the identifier string to appear since it is usually at the top
// of the copied pattern.
if (substr($this->seq[$i], 0, 1) == $this->env['divider'])
break;
}
// ... the identifier string couldn't be found in the pattern.
// Let's try it via the format attribute of the <pattern> tag
if (!$this->mod['format'] || $this->mod['format']=="") {
// Look if the "format" attribute is set.
if (!is_null($this->attr['format']) && $this->attr['format']>'') {
// compare the attribute value with all ID strings
for ($i=0; $i<count($this->env['format_long']); $i++) {
// format string valid
if ($this->attr['format'] == $this->env['format_short'][$i]) {
// assign
$this->mod['format']=$this->env['format_long'][$i];
break;
}
}
}
// neither identifier string found, nor format attribute set
// We're going to use the standard format in this case
else
$this->mod['format'] = $this->getLongIdentifier($this->env['standardformat']);
}
// set the short name of the identifier
$this->attr['format'] = $this->getShortIdentifier($this->mod['format']);
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* getLongIdentifier()
* returns the long ID of a short one
* (i.e. "IT" --> "ModPlug Tracker IT" )
*/
private function getLongIdentifier($short) {
for ($i=0; $i<$this->env['format_short']; $i++)
if ($short == $this->env['format_short'][$i])
return $this->env['format_long'][$i];
return null;
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* getShortIdentifier()
*
* the opposite of getLongIdentifier()
*/
private function getShortIdentifier($long) {
for ($i=0; $i<$this->env['format_long']; $i++)
if ($long == $this->env['format_long'][$i])
return $this->env['format_short'][$i];
return null;
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* isSwitchedOff($val)
*
* returns true if an attribute's value is 'off'
*/
private function isSwitchedOff($val) {
if ($val == $this->env['txt']['off'])
return true;
return false;
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* isSwitchedOn($val)
*
* returns true if an attribute's value is 'on'
*/
private function isSwitchedOn($val) {
if ($val == $this->env['txt']['on'])
return true;
return false;
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* space2Nbsp($str)
*
* returns string $str with the first of two successive space characters
* converted to a non-breaking space.
*/
private function space2Nbsp($str) {
return str_replace(' ', "\x26nbsp; ", $str);
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* readPattern()
*
* writes the pattern data into an array
*/
private function readPattern() {
// Read every line
// $zeile ist a line in the input array $this->seq
// $row is a line in the output array $mod['dat']
$row=0;
$count_seq=count($this->seq);
for ($zeile=0; $zeile<$count_seq; $zeile++) {
// Lines shorter than 12 chars and/or lines not beginning with `|'
// are ignored
# limit rows
if ($row>=$this->env['maxrows'])
break;
if ( (strlen($this->seq[$zeile]) >= $this->env['bytesperchannel'] )
&& (substr($this->seq[$zeile], 0, 1) == $this->env['divider']) )
{
// each channel consists of 'bytesperchannel' bytes
$len_row=strlen($this->seq[$zeile]);
for ($pos=0, $channel=0;
$pos<$len_row;
$pos+=$this->env['bytesperchannel'], $channel++)
{
# limit the number of channels
if ($channel>=$this->env['maxchannels'])
break;
// Check if a channel's first byte is the divider char ('|'),
// This way we avoid having nonsense in the output array.
if (substr($this->seq[$zeile], $pos, strlen($this->env['divider']))
== $this->env['divider'])
{
$p=1; // strlen($this->env['divider']);
# Note (3 chars: 1-3 (first char is 0))
$this->mod['dat'][$row][$channel]['note']
= substr($this->seq[$zeile], $pos+$p, $this->env['lennote']);
$p+=$this->env['lennote'];
# Instrument (2 chars: 4-5)
$this->mod['dat'][$row][$channel]['instr']
= substr($this->seq[$zeile], $pos+$p, $this->env['leninstr']);
$p+=$this->env['leninstr'];
# Volume (3 chars: 6-8)
$this->mod['dat'][$row][$channel]['vol']
= substr($this->seq[$zeile], $pos+$p, $this->env['lenvol']);
$p+=$this->env['lenvol'];
# Effect (3 chars: 9-11)
// if there is an effect without param, convert '..' to '00'
$this->mod['dat'][$row][$channel]['eff'] =
preg_replace('/^([0-9A-Z\\#])\.\./', '${1}00',
substr($this->seq[$zeile], $pos+$p, $this->env['lenfx']));
}
}
// $row is the index of the output array. If we would use $zeile
// as its index, every empty or invalid entry of the input array
// would increase the counter and thus give us "false positives"
$row++;
}
}
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* spanTag()
* returns a <span> tag with style and data
*/
private function spanTag($dat, $class) {
//return "<span class=\"$class\">$dat</span>";
return "<p-$class>$dat</p-$class>";
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* getEffectColor($eff)
*
* returns the color for a particular effect
*/
private function getEffectColor($eff, $background='') {
$categs=count($this->env['categories']);
for ($i=0; $i<$categs; $i++)
if (preg_match("/".substr($eff,0,1)."/",
$this->env['fx'][$this->attr['format']][$this->env['categories'][$i]] ) )
return $this->env['class'][$this->env['categories'][$i]].$background;
// Couldn't find effect - use default color
return $this->env['class']['default'].$background;
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* convPattern2Html()
*
* converts the pattern data to html
*/
private function convPattern2Html() {
$ret=null;
// use standard highlight, if attribute highlight was not specified
if ($this->attr['highlight']===null)
$this->attr['highlight']=$this->env['stdhighlight'];
// merge data array
$row_count=count($this->mod['dat']);
for ($row=0; $row<$row_count; $row++) {
$channel_count=count($this->mod['dat'][$row]);
for ($channel=0; $channel<$channel_count; $channel++) {
// Attribute highlight="X": Highlight every Xth line
if ($this->attr['highlight']>0) {
$background = ($row % $this->attr['highlight']==0)
? ' '.$this->env['class']['highlight']
: '';
} else $background = '';
// some shortcuts
$note = $this->mod['dat'][$row][$channel]['note'];
$instr= $this->mod['dat'][$row][$channel]['instr'];
$vol = $this->mod['dat'][$row][$channel]['vol'];
$eff = $this->mod['dat'][$row][$channel]['eff'];
# Divider
$ret.= $this->spanTag($this->env['divider'],
$this->env['class']['divider'].$background);
# Note
$ret.= $this->spanTag($note,
$this->env['class']['note'].$background);
# Instrument
$ret.= $this->spanTag($instr,
(substr($instr,0,1) == '.')
? $this->env['class']['default'].$background
: $this->env['class']['instr'].$background);
# Volume Column
$ret.= $this->spanTag($vol,
(substr($vol,0,1) == '.')
? $this->env['class']['default'].$background
: $this->getEffectColor($vol, $background) );
# Effect Column
$ret.= $this->spanTag($eff,
(substr($eff,0,1) == '.')
? $this->env['class']['default'].$background
: $this->getEffectColor($eff, $background) );
}
$ret.="<br />";
} # for ($row ...
return $ret;
}
/**++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
* printPattern()
*/
private function printPattern() {
$with_title = (is_null($this->attr['title']) || $this->attr['title']=='') ? false : true;
# float
if ($this->attr['float']!==null) {
if ($this->attr['float']!=$this->env['txt']['left']
&& $this->attr['float']!=$this->env['txt']['right'])
$this->attr['float']=null;
else
# add fixed prefix "mpt_float_" to class name
$this->attr['float']=' mpt_float_'.$this->attr['float'];
}
# width
if ($this->attr['width']!==null)
$width=" style=\"max-width:{$this->attr['width']}px;overflow:auto;\"";
else
$width='';
# frame
if ($this->attr['css']===null)
$ret='<div class="mpt_'.$this->env['class']['frame'].$this->attr['float'].'" '.$width.'>';
else
$ret='<div class="mpt_'.$this->attr['css'].$this->attr['float'].'"'.$width.'>';
if ($with_title)
$ret.='<div class="'.$this->env['class']['title'].'">'.$this->attr['title'].'</div>';
# add identifier to the top if 'id' is not switched off.
if (!$this->isSwitchedOff($this->attr['identifier']) )
$ret.='<span class="'.$this->env['class']['id'].'">'.
$this->space2Nbsp($this->mod['format']).'</span><br />';
# add the pattern data itself
$ret.=$this->convPattern2Html();
# close div
$ret.='</div>';
if (MPT_COMMENT_OUT_PHP_WARNINGS) echo " -->\n"; // comment out php error messages
return $ret;
} // private function printPattern()
} // class MPTPatterns
?>