<?php
/*
<http://jjaf.de/casio/exilim/firmware/>

firmware-explorer

Veränderung der Versionsbezeichnung '1.02' => '1.03' an offset 0xB führt zu korrekter Anzeige des geänderten Wertes in 2.
=> keine Prüfsumme bis zu diesem Punkt.

file:
  offset     lentgth    type    description
00000000    00000004    DWORD    compatibility-mask
00000004    00000004    DWORD    segment-table entry-count
[following two entries depending on KX-Model-#, segment-table otherwise]
00000008    00000008    string    version ('1.02'), N/T?
00000018         N/T    struct    segment-table

segment-table:
  offset     lentgth    type    description
00000000    00000004    DWORD    segment-offset. NULL => end of segment-table
00000004    00000004    DWORD    segment-length

Segment:
  offset     lentgth    type    description
00000000    00000010    string    segment-name ('./rom857.hbn')
00000010    00000010    string    date ("JJ.MM.TT.ss.mm")
00000020    00000004    DWORD    compatibility-mask
00000024    00000004    DWORD    file-length
00000028    00000004    DWORD check-sum
0000002C    00000004    DWORD check-xor
00000030    00000010    DWORD    version-/file-name-bitmap (2 half-nibble)
                                        0: version
                                        1: file-name
00000040    00000020    string    version, maybe incl. language ('1.00(JPN)')
00000060    00000020    string    filename ('\menu\best_0.ico')

00000080    file-length    file-contents
*/

define('default_firmware_file'      , 'ex-z4.bin');
define('SegmentTableEntryLength'    , 0x00000008);
define('SegmentHeaderLength'        , 0x00000080);
define('SegmentFileNameEmptyPrefix' , 'unknown-');
define('SegmentFileIconHeaderLength', 0x0000000a);
define('ByteLength'                 , 0x00000001);
define('WordLength'                 , 0x00000002);
define('DoubleWordLength'           , 0x00000004);

$mime_type = array (
    
'avi'     => 'video/avi',
    
'gif'     => 'image/gif',
    
'jpe'     => 'image/jpeg',
    
'jpg'     => 'image/jpeg',
    
'txt'     => 'text/plain',
    
'wav'     => 'audio/x-wav',
    
'default' => 'application/octet-stream'
);

// KX-model-#
$ModelInfo = array (
    
'341'     => array(
        
'Name'               => 'PENTAX Optio S',
        
'SegmentTableOffset' => 0x00000008),
    
'359'     => array(
        
'Name'               => 'PENTAX Optio S4',
        
'SegmentTableOffset' => 0x00000008),
    
'364'     => array(
        
'Name'               => 'PENTAX Optio S4i',
        
'SegmentTableOffset' => 0x00000018),
    
'382'     => array(
        
'Name'               => 'PENTAX Optio S5i',
        
'SegmentTableOffset' => 0x00000018),
    
'716'     => array(
        
'Name'               => 'QV-3000EX',
        
'SegmentTableOffset' => 0x00000008),
    
'792'     => array(
        
'Name'               => 'YC-400',
        
'SegmentTableOffset' => 0x00000018),
    
'800'     => array(
        
'Name'               => 'EX-Z1000',
        
'SegmentTableOffset' => 0x00000018),
    
'806'     => array(
        
'Name'               => 'EX-Z500',
        
'SegmentTableOffset' => 0x00000018),
    
'809'     => array(
        
'Name'               => 'EX-Z600',
        
'SegmentTableOffset' => 0x00000018),
    
'821'     => array(
        
'Name'               => 'QV-4000',
        
'SegmentTableOffset' => 0x00000008),
    
'823'     => array(
        
'Name'               => 'EX-P600',
        
'SegmentTableOffset' => 0x00000018),
    
'824'     => array(
        
'Name'               => 'EX-P700',
        
'SegmentTableOffset' => 0x00000018),
    
'831'     => array(
        
'Name'               => 'EX-P505',
        
'SegmentTableOffset' => 0x00000018),
    
'835'     => array(
        
'Name'               => 'EX-S500',
        
'SegmentTableOffset' => 0x00000018),
    
'836'     => array(
        
'Name'               => 'EX-S600',
        
'SegmentTableOffset' => 0x00000018),
    
'842'     => array(
        
'Name'               => 'EX-Z750',
        
'SegmentTableOffset' => 0x00000018),
    
'844'     => array(
        
'Name'               => 'EX-Z850',
        
'SegmentTableOffset' => 0x00000018),
    
'851'     => array(
        
'Name'               => 'EX-S1',
        
'SegmentTableOffset' => 0x00000008),
    
'852'     => array(
        
'Name'               => 'EX-M1',
        
'SegmentTableOffset' => 0x00000008),
    
'853'     => array(
        
'Name'               => 'EX-S2',
        
'SegmentTableOffset' => 0x00000008),
    
'854'     => array(
        
'Name'               => 'EX-M2',
        
'SegmentTableOffset' => 0x00000008),
    
'855'     => array(
        
'Name'               => 'EX-Z3 (?)',
        
'SegmentTableOffset' => 0x00000008),
    
'856'     => array(
        
'Name'               => 'EX-S3',
        
'SegmentTableOffset' => 0x00000008),
    
'857'     => array(
        
'Name'               => 'EX-Z4/EX-Z4U',
        
'SegmentTableOffset' => 0x00000018),
    
'858'     => array(
        
'Name'               => 'EX-S20/EX-S20U',
        
'SegmentTableOffset' => 0x00000018),
    
'859'     => array(
        
'Name'               => 'EX-M20/EX-M20U',
        
'SegmentTableOffset' => 0x00000018),
    
'861'     => array(
        
'Name'               => 'QV-R3',
        
'SegmentTableOffset' => 0x00000008),
    
'862'     => array(
        
'Name'               => 'QV-R4',
        
'SegmentTableOffset' => 0x00000008),
    
'871'     => array(
        
'Name'               => 'EX-Z40 (?)',
        
'SegmentTableOffset' => 0x00000018),
    
'877'     => array(
        
'Name'               => 'EX-Z30 (?)',
        
'SegmentTableOffset' => 0x00000018),
    
'default' => array(
        
'Name'               => '[unknown!]',
        
'SegmentTableOffset' => 0x00000018),
);

$menu_checksum = array(
    
'ALL'   => 0,
    
'LANG0' => 0,
    
'LANG1' => 0,
    
'LANG2' => 0,
    
'LANG3' => 0,
    
'LANG4' => 0,
    
'LANG5' => 0,
    
'JPG'   => 0,
    
'AVI'   => 0,
    
'PRG'   => 0,
    
'DAT'   => 0,
);

$SegmentFileNameInfo = pathinfo($_GET['segmentfilename']);
$DoPrint = (!isset($_GET['segmentfilename']) or ('-ico' == $SegmentFileNameInfo['extension']));

// HTML-HEAD
ConditionalPrint(<<<HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Casio/Pentax Exilim/QV/Optio firmware-explorer</title>
<link rel="stylesheet" type="text/css" media="all" title="jjaf.de" href="//jjaf.de/jjaf.css"/>
<link rel="discussion" title="Digital Photography Review" href="//forums.dpreview.com/forums/read.asp?forum=1015&amp;message=8278207"/>
<link rel="discussion" title="Pixelbroker" href="//pixelbroker.de/board/viewtopic.php?p=11969" hreflang="de"/>
</head>
<body>
HTML)
;
ConditionalPrint(<<<HTML
<h1>Casio Exilim firmware-explorer</h1>
<p>This is a little <a href=".phps">php-script</a> to have a look into the
<a href="ftp://ftp.casio.co.jp/pub/qv/download/en/">firmware-(<code>.bin</code>)-files</a>.</p>
<ol>
    <li>Maybe one could create a <strong>custom firmware</strong>?</li>
</ol>
<dl>
    <dt>script-(<code>.scp</code>)</dt>
    <dd>
        <p>How are the <strong>script-(<code>.scp</code>)-commands</strong> (files <code>C:\Testmode.scp</code>, <code>C:\factory.scp</code>, <code>AutoRun.scp</code>) used?</p>
        <p><a href="//www.27dx.com/?option=com_docman&task=cat_view&gid=46">Service-Manual</a> QV-R3/R4 page 15 gives hints.</p>
    </dd>
    <dt><acronym title="operating-system">OS</acronym>?</dt>
    <dd>
        <p>In <a href="?firmwarefilename=ex-z750-1.02.bin&segmentfilename=unknown-1">EX-Z750 1.02</a> first clue:</p>
        <blockquote>
            Copyright (c) 2000(2003) <a href="//www.renesas.com/">Renesas Technology Corp.</a><br/>
            and Renesas Solutions Corp. All Rights Reserved.<br/>
            <a href="//www.renesas.com/fmwk.jsp?cnt=hi7700_4_tools_product_landing.jsp&amp;fp=/products/tools/os_middleware/u_itron/hi7700_4/">HI7700/4</a>(HS0770ITI41SRx)V1.3.00 2003
        </blockquote>
        <p><a href="//download.renesas.com/eng/edge/05/customer.html">Renesas EDGE on Exilim</a></p>
    <dd>
    <dt>damaged audio-(<code>.wav</code>)-files?</dt>
    <dd><p><a href="http://cmachado.pt.vu/">Carlos Machado</a> finally solved the question of damaged audio-(<code>.wav</code>)-files: The the data-chunks are coded big-<a href="//en.wikipedia.org/wiki/Endian">endian</a> where they should be coded little-endian. A small code-change - with hardcoded-offset though - compensates this. This should also give a clue about endianess in the ROM-files and it seems that EX-Z750-users need <a href="?firmwarefilename=ex-z750-1.02.bin&segmentfilename=\pcm_data\al01.wav&endiancorrected=1">louder alarm</a> than the EX-Z4-users <a href="?firmwarefilename=ex-z4.bin&segmentfilename=\pcm_data\al01.wav&endiancorrected=1">hear</a>.</p></dd>
    <dt>KX-Model-#?<dt>
    <dd>
        <p>
            <a href="//cebit.de/">CeBIT</a> 2004 showed:
            <a href="//www.exilim.de/de/exilimzoom/exz30/">EX-Z30</a> = <a href="//jjaf.de/2004/03/19/164005.jpeg"><code>KX877</code></a>,
            <a href="//www.exilim.de/de/exilimzoom/exz40/">EX-Z40</a> = <a href="//jjaf.de/2004/03/19/164058.jpeg"><code>KX871</code></a>.
        </p>
    </dd>
<!--
    <dt>firmware-files?</dt>
    <dd>
        <p>Firmwares are <a href="//world.casio.com/qv/download/en/">available from Casio</a> for at least the following models:</p>
        <ul class="compact">
            <!-- DirectURL 1.2 -->
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_p505_firmup/form.html?_MODEL=EX-P505&amp;Product_No=0000000">EX-P505</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_p700_firmup/form.html?_MODEL=EX-P700&amp;Product_No=0000000">EX-P700</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_p600_firmup/form.html?_MODEL=EX-P600&amp;Product_No=0000000">EX-P600</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_s20m20_firmup/form.html?_MODEL=EX-S20/EX-M20&amp;Product_No=0000000">EX-S20/EX-M20</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_s20um20u_firmup/form.html?_MODEL=EX-S20U/EX-M20U&amp;Product_No=0000000">EX-S20U/EX-M20U</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_s500_firmup/form.html?_MODEL=EX-S500&amp;Product_No=0000000">EX-S500</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_s600_firmup/form.html?_MODEL=EX-S600&amp;Product_No=0000000">EX-S600</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_z4_firmup/form.html?_MODEL=EX-Z4&amp;Product_No=0000000">EX-Z4</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_z4u_firmup/form.html?_MODEL=EX-Z4U&amp;Product_No=0000000">EX-Z4U</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_z500_firmup/form.html?_MODEL=EX-Z500&amp;Product_No=0000000">EX-Z500</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_z600_firmup/form.html?_MODEL=EX-S500&amp;Product_No=0000000">EX-Z600</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_z750_firmup/form.html?_MODEL=EX-Z750&amp;Product_No=0000000">EX-Z750</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_z850_firmup11b/form.html?_MODEL=EX-Z850&amp;Product_No=0000000">EX-Z850</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_z1000_firmup/form.html?_MODEL=EX-Z1000&amp;Product_No=0000000">EX-Z1000</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_s2m2_firmup/form.html?_MODEL=EX-S2/EX-M2&amp;Product_No=0000000">EX-S2/EX-M2</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/ex_s1m1_firmup/form.html?_MODEL=EX-S1/EX-M1&amp;Product_No=0000000">EX-S1/EX-M1</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/qv_r4r3_firmup/form.html?_MODEL=QV-R3/QV-R4&amp;Product_No=0000000">QV-R3/QV-R4</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/qv4000_firmup/form.html?_MODEL=QV-4000&amp;Product_No=0000000">QV-4000</a></li>
            <li><a href="https://secure.casio.co.jp/qv/download/en/qv3000_firmup/form.html?_MODEL=QV-3000EX(EX/Ir)&amp;Product_No=0000000">QV-3000EX(EX/Ir)</a></li>
        </ul>
    </dd>
-->
</dl>
HTML)
;

// FirmwareFileName
if (isset($_GET['firmwarefilename'])) {
    
$FirmwareFileName = $_GET['firmwarefilename'];
} else {
    
$FirmwareFileName = default_firmware_file;
}

// list available firmwares and check query-string-parameter
ConditionalPrint('<p>available firmwares to analyze:</p>');
ConditionalPrint('<ul class="compact">');
$FirmwareDirectory = dir('.');
$FirmwareFileNameFound = FALSE;
while (
$FirmwareDirectoryFileName = $FirmwareDirectory->read()) {
    if (
is_file($FirmwareDirectoryFileName)) {
        
$FirmwareDirectoryFilePathInfo = pathinfo($FirmwareDirectoryFileName);
        if (
$FirmwareDirectoryFilePathInfo['extension'] == 'bin') {
            
$FirmwareFileNameFound |= ($FirmwareDirectoryFileName == $FirmwareFileName);
            
ConditionalPrint('<li><a href="?firmwarefilename='.$FirmwareDirectoryFileName.'">'.basename($FirmwareDirectoryFileName, '.'.$FirmwareDirectoryFilePathInfo['extension']).'</a></li>');
        }
    }
}
$FirmwareDirectory->close();
ConditionalPrint('</ul>');

ConditionalPrint('<blockquote cite="mid:200504011430.QAA11827@abba." lang="de"><p>&hellip; grossed Lob für den Firmware-Explorer auf Deiner Seite, hat mir recht geholfen meinen <a href="//sassenfeld.de/software/bestman/bestman" title="BestMan">Bestshot Editor</a> zu entwickeln. [<a href="//sassenfeld.de/">Klaus Sassenfeld</a>]</p></blockquote>');

if (!
$FirmwareFileNameFound) {
    
ConditionalPrint('<p><var>'.$FirmwareFileName.'</var> not found!</p>');
    
GenerateDocumentFooter();
    exit();
}

$FirmwareFileHandle = fopen($FirmwareFileName, 'rb');

/*
* Output firmware-information
*/

ConditionalPrint('<h2>'.$FirmwareFileName.'</h2>');

$DoPrint = (!isset($_GET['segmentfilename']) && !('-ico' == $SegmentFileNameInfo['extension']));

ConditionalPrint('<table summary="firmware segments">');
ConditionalPrint('<caption>');
ConditionalPrint('file: '                 .$FirmwareFileName.'; ');
$KXModel = dechex(ReadDoubleWord($FirmwareFileHandle, 0x00000000));
ConditionalPrint('compatibility-mask: KX-'.$KXModel.' ('.ModelInfo($KXModel, 'Name').'); ');
ConditionalPrint('segments: '             .ReadDoubleWord($FirmwareFileHandle, 0x00000004).'; ');
ConditionalPrint('version: ');
$SegmentTableOffsetCurrent = ModelInfo($KXModel, 'SegmentTableOffset');
if (
ModelInfo($KXModel, 'SegmentTableOffset') > 8) {
    
ConditionalPrint(ReadNullTerminatedString($FirmwareFileHandle, 0x00000008, 0x00000008));
} else {
    
// TBD. from first segment?
    
$SegmentOffset     =           ReadDoubleWord($FirmwareFileHandle,  $SegmentTableOffsetCurrent              );
    
$SegmentVersion    = ReadNullTerminatedString($FirmwareFileHandle, ($SegmentOffset + 0x00000040), 0x00000020);
    
ConditionalPrint('<span class="tbd">'.$SegmentVersion.'</span>');
}
ConditionalPrint('</caption>');
ConditionalPrint(<<<HTML
<col span="3" style="text-align: right;"/>
<col span="2"/>
<col span="2" style="text-align: right;"/>
<col span="2"/>
<col span="6" style="text-align: right;"/>
HTML)
;
ConditionalPrint(<<<HTML
<thead>
<tr>
<th scope="col"                        >#            </th>
<th scope="col"                        >offset       </th>
<th scope="col"                        >length       </th>
<th scope="col"                        >name         </th>
<th scope="col"                        >date         </th>
<th scope="col"                        >compatibility</th>
<th scope="col"                        >file-length  </th>
<th scope="col"                        >version      </th>
<th scope="col"                        >file-name    </th>
<th scope="col" class="tbd"            >bitmap       </th>
<th scope="col"                        >check-sum    </th>
<th scope="col"                        >check-xor    </th>
<th scope="col" class="tbd" colspan="3">TBD          </th>
</tr>
</thead>
HTML)
;

ConditionalPrint('<tbody>');
$SegmentTableIndex = 0;
$SegmentFileNameEmptyCounter = 0;
while (
ReadDoubleWord($FirmwareFileHandle, $SegmentTableOffsetCurrent)) {
    
ConditionalPrint('<tr>');

    
$SegmentTableIndex += 1;
    
$SegmentOffset     =           ReadDoubleWord($FirmwareFileHandle,  $SegmentTableOffsetCurrent              );
    
$SegmentFileName   = ReadNullTerminatedString($FirmwareFileHandle, ($SegmentOffset + 0x00000060), 0x00000020);
    
$SegmentFileLength = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x00000024)                        );

    
// Semantic?
    
$SegmentBitmap     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x00000030)                        );

    
// ROM
    
if (empty($SegmentFileName)) {
        if (
$SegmentBitmap & 1) {
            
$SegmentFileNameEmptyCounter++;
            
$SegmentFileName = SegmentFileNameEmptyPrefix.$SegmentFileNameEmptyCounter;
            
$SCPcommands = fscanPartial($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength), $SegmentFileLength, '/\000POWER\000.*?\000\000/');
        } else {
            
$SegmentFileName = ReadNullTerminatedString($FirmwareFileHandle, ($SegmentOffset + 0x00000040), 0x00000020);
//!            $SegmentVersion  = ReadNullTerminatedString($FirmwareFileHandle, ($SegmentOffset + 0x00000060), 0x00000020);
        
}    
    }

    if (
$DoPrint || (isset($_GET['segmentfilename']) && ($_GET['segmentfilename'] == $SegmentFileName))) {

        
$SegmentLength     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentTableOffsetCurrent + 0x00000004)            );
        
$SegmentName       = ReadNullTerminatedString($FirmwareFileHandle,  $SegmentOffset                          , 0x00000010);
        
$SegmentDate       = ReadNullTerminatedString($FirmwareFileHandle, ($SegmentOffset + 0x00000010)            , 0x00000010);
        
$SegmentCompat     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x00000020)                        );
        
$SegmentCRC[0]     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x00000028)                        );
        
$SegmentCRC[1]     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x0000002C)                        );

        
$SegmentTBD[0]     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x00000034)                        );
        
$SegmentTBD[1]     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x00000038)                        );
        
$SegmentTBD[2]     = ReadDoubleWord($FirmwareFileHandle          , ($SegmentOffset + 0x0000003C)                        );

        
$SegmentVersion    = ReadNullTerminatedString($FirmwareFileHandle, ($SegmentOffset + 0x00000040), 0x00000020);
        
$SegmentVersionInternal = fscanPartial($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength), $SegmentFileLength, '/Copyright.*?\000/');

        
$SegmentFilePathInfo = pathinfo($SegmentFileName);
        
$SegmentCRCcalculated = fcrc32Partial($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength), $SegmentFileLength);

        
// calculate overall chesums to compare with TEST MODE ("secret menu")
        
$menu_checksum['ALL'] = (($menu_checksum['ALL'] + $SegmentCRC[0]) & 0xFFFFFFFF);
        if (
SegmentFileNameEmptyPrefix.$SegmentFileNameEmptyCounter == $SegmentFileName) {
            
$menu_checksum['PRG'] = (($menu_checksum['PRG'] + $SegmentCRC[0]) & 0xFFFFFFFF);
        }
        if (
'avi' == $SegmentFilePathInfo['extension']) {
            
$menu_checksum['AVI'] = (($menu_checksum['AVI'] + $SegmentCRC[0]) & 0xFFFFFFFF);
        }
        if ((
'jpe' == $SegmentFilePathInfo['extension']) || ('jpg' == $SegmentFilePathInfo['extension'])) {
            
$menu_checksum['JPG'] = (($menu_checksum['JPG'] + $SegmentCRC[0]) & 0xFFFFFFFF);
        }
        
        
ConditionalPrint('<td><code>'.$SegmentTableIndex                            .'</code></td>');
        
ConditionalPrint('<td><code>'.FormattedDoubleWord($SegmentOffset)           .'</code></td>');
        
ConditionalPrint('<td><code>'.FormattedDoubleWord($SegmentLength)           .'</code></td>');
        
ConditionalPrint('<td><code>'.$SegmentName                                  .'</code></td>');
        
ConditionalPrint('<td><code>'.$SegmentDate                                  .'</code></td>');
        
ConditionalPrint('<td><code>'.FormattedDoubleWord($SegmentCompat)           .'</code></td>');
        
ConditionalPrint('<td><code>'.number_format($SegmentFileLength, 0, ',', '.').'</code></td>');

        
ConditionalPrint('<td><code>'.$SegmentVersion);
        if (
'' != $SegmentVersionInternal) {
            
ConditionalPrint('<br/>'.$SegmentVersionInternal);
        }
        
ConditionalPrint('</code></td>');

        
ConditionalPrint('<td><a href="?firmwarefilename='.$FirmwareFileName.'&amp;segmentfilename='.$SegmentFileName.'"><code>'.$SegmentFileName.'</code></a>');
        if (isset(
$SegmentFilePathInfo['extension']) && ('audio/x-wav' == MIMEtype($SegmentFilePathInfo['extension']))) {
            
ConditionalPrint(' <a href="?firmwarefilename='.$FirmwareFileName.'&amp;segmentfilename='.$SegmentFileName.'&endiancorrected=1"><code>Endian-corrected</code></a>');
        }
        
ConditionalPrint('</td>');
        
ConditionalPrint('<td><code>'.FormattedBin($SegmentBitmap, 4).'</code></td>');
        if (
$SegmentCRC[0] == $SegmentCRCcalculated['ChkSum32']) {
            
ConditionalPrint('<td><code>'.FormattedDoubleWord($SegmentCRC[0]).'</code></td>');
        } else {
            
ConditionalPrint('<td class="tbd"><code>'.FormattedDoubleWord($SegmentCRC[0]).'<br/>'.
                
FormattedDoubleWord($SegmentCRCcalculated['ChkSum32']).'</code></td>');
        }
        if (
$SegmentCRC[1] == $SegmentCRCcalculated['ChkXor32']) {
            
ConditionalPrint('<td><code>'.FormattedDoubleWord($SegmentCRC[1]).'</code></td>');
        } else {
            
ConditionalPrint('<td class="tbd"><code>'.FormattedDoubleWord($SegmentCRC[1]).'<br/>'.
                
FormattedDoubleWord($SegmentCRCcalculated['ChkXor32']).'</code></td>');
        }
        foreach (
$SegmentTBD as $TBDElement) {
            
ConditionalPrint('<td><code>'.FormattedDoubleWord($TBDElement).'</code></td>');
        }
    }

    
// output segment-file if current
    
if (isset($_GET['segmentfilename']) && ($_GET['segmentfilename'] == $SegmentFileName)) {

        
$DoPrint = (('-ico' == $SegmentFilePathInfo['extension']) && !isset($_GET['segmentfileiconindex']));

        
// except for 'ico(n)'?-segments which will be extra parsed
        
if ('-ico' == $SegmentFilePathInfo['extension']) {
            
ConditionalPrint(<<<HTML
<table>
<thead>
<tr>
<th scope="col"                        >#      </th>
<th scope="col"                        >offset </th>
<th scope="col"                        >length </th>
<th scope="col" class="tbd" colspan="3">TBD    </th>
<th scope="col"                        >content</th>
<th scope="col"                        >CRC    </th>
</tr>
</thead>
</tbody>
HTML            )
;

            
// read the first offset from the table but ignore the rest of it but use "offset + length"-algorithm
            // to find next entry. '.ico'-format seems to be old because the table can only cope with offsets
            // up to 0xFFFF kB and therefore holding entries in this range only!
            
$SegmentFileIconOffset = ReadDoubleWord($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength));
            
$SegmentFileIconLength = 0;
            
$SegmentFileIconIndex  = 0;
            while ((
$SegmentFileIconOffset + $SegmentFileIconLength + SegmentFileIconHeaderLength) < $SegmentFileLength) {

                
// read icon-header
                
$SegmentFileIconOffset = ($SegmentFileIconOffset + $SegmentFileIconLength);
                
$SegmentFileIconTBD[0] = ReadNBytes    ($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength +  $SegmentFileIconOffset     ) , ByteLength);
                
$SegmentFileIconTBD[1] = ReadNBytes    ($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength + ($SegmentFileIconOffset +  1)), ByteLength);
                
$SegmentFileIconTBD[2] = ReadDoubleWord($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength + ($SegmentFileIconOffset +  2)));
                
$SegmentFileIconLength = ReadDoubleWord($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength + ($SegmentFileIconOffset +  6)));
                
$SegmentFileIconSample = ReadNBytes    ($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength + ($SegmentFileIconOffset + 10)), min(DoubleWordLength, ($SegmentFileIconLength - SegmentFileIconHeaderLength)));

                
// output segment-file-icon-index if current
                
if
                (
                       isset(
$_GET['segmentfileiconindex'])
                    && (
$_GET['segmentfileiconindex'] == $SegmentFileIconIndex)
                ) {
                    
fhexPartial(
                        
$FirmwareFileHandle,
                        (
$SegmentOffset + SegmentHeaderLength + $SegmentFileIconOffset + SegmentFileIconHeaderLength),
                        (
$SegmentFileIconLength - SegmentFileIconHeaderLength),
                        
0x03  // TBD!
                    
);
                    exit;
                } else {

                    
// output icon-header
                    
ConditionalPrint('<tr>');
                    
ConditionalPrint('<td><a href="?firmwarefilename='.$FirmwareFileName.'&amp;segmentfilename='.$SegmentFileName.'&amp;segmentfileiconindex='.$SegmentFileIconIndex.'"><code>'.$SegmentFileIconIndex.'</code></a></td>');
                    
ConditionalPrint('<td><code>'.FormattedDoubleWord($SegmentFileIconOffset).'</code></td>');
                    
ConditionalPrint('<td><code>'.FormattedDoubleWord($SegmentFileIconLength - SegmentFileIconHeaderLength).'</code></td>');
                    
ConditionalPrint('<td><code>'.FormattedHex($SegmentFileIconTBD[0], ByteLength).'</code></td>');
                    
ConditionalPrint('<td><code>'.FormattedHex($SegmentFileIconTBD[1], ByteLength).'</code></td>');
                    
ConditionalPrint('<td><code>'.FormattedHex($SegmentFileIconTBD[2], DoubleWordLength).'</code></td>');
                    
ConditionalPrint('<td><code>'.FormattedHex($SegmentFileIconSample, min(DoubleWordLength, ($SegmentFileIconLength - SegmentFileIconHeaderLength))).'&hellip;</code></td>');
                    
ConditionalPrint('<td><code>'.FormattedHex(fcrc32Partial($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength + $SegmentFileIconOffset + SegmentFileIconHeaderLength), ($SegmentFileIconLength - SegmentFileIconHeaderLength)), DoubleWordLength).'</code></td>');
                    
ConditionalPrint('</tr>');
                    
$SegmentFileIconIndex++;

                }
            }
            print
ConditionalPrint('</tbody></table>');
            exit;
        } else {
            
header('Content-Type: '.MIMEtype($SegmentFilePathInfo['extension']));
            
header('Content-Disposition: inline; filename='.$SegmentFileName);
            
header('Content-Length: '.$SegmentFileLength);
            
header('Last-Modified: '.$SegmentDate);
            
header('Cache-Control: public');
            
header('ETag: '.FormattedDoubleWord($SegmentCRC[0]).'-'.FormattedDoubleWord($SegmentCRC[1]));

            if (isset(
$_GET['endiancorrected'])) {
                
// RIFF may be damaged in data-chunk due to incorrect endianess
                // thx to Carlos Machado <http://cmachado.pt.vu/>
                
fpassthroughPartial               ($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength     ),                       44 );
                
fpassthroughPartialChangeEndianess($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength + 44), ($SegmentFileLength - 44));
            } else {
                
fpassthroughPartial($FirmwareFileHandle, ($SegmentOffset + SegmentHeaderLength), $SegmentFileLength);
            }

            exit;
        }
    }

    
ConditionalPrint('</tr>');

    
$SegmentTableOffsetCurrent += SegmentTableEntryLength;
}
fclose($FirmwareFileHandle);

ConditionalPrint('</tbody>');
ConditionalPrint('</table>');
ConditionalPrint('<h3><code>MENU CHECKSUM</code></h3>');
ConditionalPrint('<p>If a <a href="?firmwarefilename=ex-z4.bin">full-update</a>, not a <a href="?firmwarefilename=ex-s770-1.01.bin">partial</a> one the <q>TEST MODE</q> <code>7 :ROM UPDATE</code>|<code>6:MENU CHECKSUM</code> should read:</p>');
ConditionalPrint('<pre>');
ConditionalPrint(' MENU CHECKSUM'."\n");
ConditionalPrint("\n");
ConditionalPrint('  JPG='.strtoupper(FormattedDoubleWord($menu_checksum['JPG'])).' (could be 0x00000000)'."\n");
ConditionalPrint('  AVI='.strtoupper(FormattedDoubleWord($menu_checksum['AVI']))."\n");
ConditionalPrint('  PRG='.strtoupper(FormattedDoubleWord($menu_checksum['PRG']))."\n");
ConditionalPrint('</pre>');
ConditionalPrint('<code>'.strtoupper(FormattedDoubleWord($menu_checksum['ALL'])).'</code>');

ConditionalPrint('<h3>script-(<code>.scp</code>)-commands</h3>');
ConditionalPrint('<code>'.str_replace(chr(0), '<br />', htmlspecialchars($SCPcommands)).'</code>');

/*
ConditionalPrint('<ul>');
foreach ($SCPCommands as $SCPCommand) {
    ConditionalPrint('<li><code>'.htmlspecialchars($SCPcommand).'</code></li>');
}
ConditionalPrint('</ul>');
print_r($SCPCommands);
print_r($temp);
*/

GenerateDocumentFooter();

/*
*  functions
*/
function GenerateDocumentFooter() {
    
ConditionalPrint('</body>');
    
ConditionalPrint('</html>');
}

function
ReadNullTerminatedString($FirmwareFileHandle, $offset, $length) {
    
fseek($FirmwareFileHandle, $offset);
    return
rtrim(fread($FirmwareFileHandle, $length));
}

function
ReadNBytes($FirmwareFileHandle, $offset, $length) {
    
fseek($FirmwareFileHandle, $offset);
    return
hexdec('0x'.bin2hex(fread($FirmwareFileHandle, $length)));
}

function
ReadWord($FirmwareFileHandle, $offset) {
    return (
ReadNBytes($FirmwareFileHandle, $offset, WordLength) & 0xFFFF);
}

function
ReadDoubleWord($FirmwareFileHandle, $offset) {
    return (
ReadNBytes($FirmwareFileHandle, $offset, DoubleWordLength) & 0xFFFFFFFF);
}

function
FormattedDoubleWord($DoubleWord) {
    return
'0x'.str_pad(dechex($DoubleWord), 8, '0', STR_PAD_LEFT);
}

function
FormattedHex($value, $length) {
    return
'0x'.str_pad(dechex($value), ($length * 2), '0', STR_PAD_LEFT);
}

function
FormattedBin($value, $length) {
    
$temp = chunk_split(str_pad(decbin($value), ($length * 8), '0', STR_PAD_LEFT), 8, '-');
    return
substr($temp, 0, (strlen($temp) - 1));
}

function
ConditionalPrint($text) {

    global
$DoPrint;

    if (
$DoPrint) {
        print
$text;
    }
}

function
fcrc32Partial($FirmwareFileHandle, $offset, $length) {

    global
$SegmentBitmap;

    
$ChkSum32 = 0;
    
$ChkXor32 = 0;

    
fseek($FirmwareFileHandle, $offset);
    foreach (
unpack('N*', fread($FirmwareFileHandle, $length)) as $UINT32) {
        
$ChkSum32 = ($ChkSum32 + $UINT32);
        
$ChkXor32 = ($ChkXor32 ^ $UINT32);
    }

    return array(
        
'ChkSum32' => ($ChkSum32 & 0xFFFFFFFF),
        
'ChkXor32' => ($ChkXor32 & 0xFFFFFFFF)
    );

}

function
fpassthroughPartial($FirmwareFileHandle, $offset, $length) {

    
header('X-CRC32: '.sprintf('%u', fcrc32Partial($FirmwareFileHandle, $offset, $length)));

    
fseek($FirmwareFileHandle, $offset);
    print
fread($FirmwareFileHandle, $length);

}

function
fscanPartial($FirmwareFileHandle, $offset, $length, $pattern) {

    
fseek($FirmwareFileHandle, $offset);
    
preg_match($pattern, fread($FirmwareFileHandle, $length), $matches);
    if (isset(
$matches[0])) {
        return
$matches[0];
    } else {
        return
NULL;
    }

}

function
fpassthroughPartialChangeEndianess($FirmwareFileHandle, $offset, $length) {

    
fseek($FirmwareFileHandle, $offset);
    foreach (
unpack('n*', fread($FirmwareFileHandle, $length)) as $short) {
        print
pack('v', $short);
    }

}

function
fhexPartial($FirmwareFileHandle, $offset, $length, $width) {
    
fseek($FirmwareFileHandle, $offset);
    print
'<blockquote><code>'.chunk_split(chunk_split(bin2hex(fread($FirmwareFileHandle, $length)), 2, ' '), $width * 3, '<br/>').'</code></blockquote>';
}

function
ModelInfo($Model, $Info) {

    global
$ModelInfo;

    if (isset(
$ModelInfo[$Model][$Info])) {
        return
$ModelInfo[$Model][$Info];
    } else {
        if (isset(
$ModelInfo['default'][$Info])) {
            return
$ModelInfo['default'][$Info];
        } else {
            return
NULL;
        }
    }

}

function
MIMEtype($file_extension) {

    global
$mime_type;

    
$file_extension = strtolower($file_extension);

    if (isset(
$mime_type[$file_extension])) {
        return
$mime_type[$file_extension];
    } else {
        if (isset(
$mime_type['default'])) {
            return
$mime_type['default'];
        } else {
            return
NULL;
        }
    }

}

/*
1 ---



   VER 1.00
2 ---
PROGRAM UPDATE

     YES
     NO_

NEW VERSION IS
VER 1.02
3 ---



   NOW LOADING

(grün und rot blinken)
4 ---



   COMPLETE
5 ---

    ++ KX857 ++
PR:04.01.05.17.29
LD:1.45
MI:19
6 ---
KX857 Version  M

  Firm:
    04.01.05.17.29
    1.02

  Micon:
    19

  Loader:
    03.08.02.17.25
    1.45
7 --- a modification in a file causing a bad check-sum/-xor gives the following instead of #2


   FILE ERROR!
    0X1003
*/
?>