2010年12月24日 星期五

將excel的資料轉到mysql,不使用phpMyAdmin及修改phpExcelReader

上次使用phpMyAdmin將Excel的資料轉到mysql,但使用上,手動的部分還很多,如果一次要轉大量檔案,還是很麻煩。包括必須在mysql建立一個對應到excel的table,必須逐一輸入欄位名稱。 在網頁輸出後還要複製到文字檔,最後再利用phpMyAdmin匯入。

我改寫 phpExcelReader ,使mysql的表格能自動建立,資料能自動轉入,不必再用到phpMyAdmin。您唯一要做的事就是指定好要轉入的excel檔,其他一切就會自動完成了。

必須注意的是:
1. mysql的資料庫必須自己建立,權限及帳號密碼要自行設定(要有drop 及 create table的權限)。
2. 資料庫及網頁輸入使用utf-8編碼,但excel的檔名是big5碼,所以必須先轉成big5碼再讀檔。
$file_big5 = mb_convert_encoding($file1, 'BIG5', 'UTF-8');
$data->read($file_big5);


3. excel表格的第一行將成為mysql的table的欄位名稱。excel的檔名將成為mysql的表格(table)名稱。如果格式不符就轉不進來了。
4. 所有欄位均是text格式。


先下載 phpExcelReader ,置換壓縮檔中的 example.php及excel\reader.php

程式如下:
example.php
<?php
if (!empty($_POST["file1"])){
doProcess($_POST["file1"],$_POST['tb1']);
}

?>

<form id="form1" name="form1" enctype="multipart/form-data" method="post" action="">
<p>
<label>Excel 檔名(完整路徑):
<input name="file1" type="text" id="file1" size="50" />
<br />
</label>
</p>
<p>
<input type="submit" name="submit1" id="submit1" value="送出" />
</p>
</form>

<?php

// Test CVS
function doProcess($file1){
require_once 'Excel/reader.php';

$mysqli = new mysqli("localhost", "root", "xxxxxxxx","db1") or die($mysqli->error);


// ExcelFile($filename, $encoding);
$data = new Spreadsheet_Excel_Reader();


// Set output Encoding.
$data->setOutputEncoding('utf-8');

/***
* if you want you can change 'iconv' to mb_convert_encoding:
* $data->setUTFEncoder('mb');
*
**/

/***
* By default rows & cols indeces start with 1
* For change initial index use:
* $data->setRowColOffset(0);
*
**/

/***
* Some function for formatting output.
* $data->setDefaultFormat('%.2f');
* setDefaultFormat - set format for columns with unknown formatting
*
* $data->setColumnFormat(4, '%.3f');
* setColumnFormat - set format for column (apply only to number fields)
*
**/

$file_big5 = mb_convert_encoding($file1, 'BIG5', 'UTF-8');
$data->read($file_big5);

/*


$data->sheets[0]['numRows'] - count rows
$data->sheets[0]['numCols'] - count columns
$data->sheets[0]['cells'][$i][$j] - data from $i-row $j-column

$data->sheets[0]['cellsInfo'][$i][$j] - extended info about cell

$data->sheets[0]['cellsInfo'][$i][$j]['type'] = "date" | "number" | "unknown"
if 'type' == "unknown" - use 'raw' value, because cell contain value with format '0.00';
$data->sheets[0]['cellsInfo'][$i][$j]['raw'] = value if cell without format
$data->sheets[0]['cellsInfo'][$i][$j]['colspan']
$data->sheets[0]['cellsInfo'][$i][$j]['rowspan']
*/

error_reporting(E_ALL ^ E_NOTICE);
$pos=mb_strrpos($file1,"\\");
$pos2=mb_strrpos($file1,".");
$len=mb_strlen($file1);
$fileName=mb_substr($file1,$pos+1,($pos2-$pos-1));

for ($j = 1; $j <= $data->sheets[0]['numCols']; $j++) {
$a=trim($data->sheets[0]['cells'][1][$j]);
if (empty($a))
$fields="field".$j;
else
$fields= $data->sheets[0]['cells'][1][$j];

$fields.=" text";

if($j < $data->sheets[0]['numCols'])
$fields.=",";

$fs.=$fields;
}

$mysqli->query("SET NAMES 'utf8'");
$q="DROP TABLE IF EXISTS ".$fileName;

$mysqli->query($q) or die($mysqli->error);

$q="create table ".$fileName."(".$fs.")";
$mysqli->query($q) or die($mysqli->error);


for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) {
$q="insert ".$fileName." values(";
for ($j = 1; $j <= $data->sheets[0]['numCols']; $j++) {
$q.="\"".addslashes($data->sheets[0]['cells'][$i][$j]);
if($j < $data->sheets[0]['numCols'])
$q.="\",";
else
$q.="\"";
}
$q.=")";
// echo $q;
$mysqli->query($q) or die($mysqli->error);
}

}

?>

excel\reader.php

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
* A class for reading Microsoft Excel Spreadsheets.
*
* Originally developed by Vadim Tkachenko under the name PHPExcelReader.
* (http://sourceforge.net/projects/phpexcelreader)
* Based on the Java version by Andy Khan (http://www.andykhan.com). Now
* maintained by David Sanders. Reads only Biff 7 and Biff 8 formats.
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Spreadsheet
* @package Spreadsheet_Excel_Reader
* @author Vadim Tkachenko <vt@apachephp.com>
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: reader.php 19 2007-03-13 12:42:41Z shangxiao $
* @link http://pear.php.net/package/Spreadsheet_Excel_Reader
* @see OLE, Spreadsheet_Excel_Writer
*/


//require_once 'PEAR.php';
//require_once 'Spreadsheet/Excel/Reader/OLERead.php';
require_once 'oleread.inc';
//require_once 'OLE.php';

define('SPREADSHEET_EXCEL_READER_BIFF8', 0x600);
define('SPREADSHEET_EXCEL_READER_BIFF7', 0x500);
define('SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS', 0x5);
define('SPREADSHEET_EXCEL_READER_WORKSHEET', 0x10);

define('SPREADSHEET_EXCEL_READER_TYPE_BOF', 0x809);
define('SPREADSHEET_EXCEL_READER_TYPE_EOF', 0x0a);
define('SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET', 0x85);
define('SPREADSHEET_EXCEL_READER_TYPE_DIMENSION', 0x200);
define('SPREADSHEET_EXCEL_READER_TYPE_ROW', 0x208);
define('SPREADSHEET_EXCEL_READER_TYPE_DBCELL', 0xd7);
define('SPREADSHEET_EXCEL_READER_TYPE_FILEPASS', 0x2f);
define('SPREADSHEET_EXCEL_READER_TYPE_NOTE', 0x1c);
define('SPREADSHEET_EXCEL_READER_TYPE_TXO', 0x1b6);
define('SPREADSHEET_EXCEL_READER_TYPE_RK', 0x7e);
define('SPREADSHEET_EXCEL_READER_TYPE_RK2', 0x27e);
define('SPREADSHEET_EXCEL_READER_TYPE_MULRK', 0xbd);
define('SPREADSHEET_EXCEL_READER_TYPE_MULBLANK', 0xbe);
define('SPREADSHEET_EXCEL_READER_TYPE_INDEX', 0x20b);
define('SPREADSHEET_EXCEL_READER_TYPE_SST', 0xfc);
define('SPREADSHEET_EXCEL_READER_TYPE_EXTSST', 0xff);
define('SPREADSHEET_EXCEL_READER_TYPE_CONTINUE', 0x3c);
define('SPREADSHEET_EXCEL_READER_TYPE_LABEL', 0x204);
define('SPREADSHEET_EXCEL_READER_TYPE_LABELSST', 0xfd);
define('SPREADSHEET_EXCEL_READER_TYPE_NUMBER', 0x203);
define('SPREADSHEET_EXCEL_READER_TYPE_NAME', 0x18);
define('SPREADSHEET_EXCEL_READER_TYPE_ARRAY', 0x221);
define('SPREADSHEET_EXCEL_READER_TYPE_STRING', 0x207);
define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA', 0x406);
define('SPREADSHEET_EXCEL_READER_TYPE_FORMULA2', 0x6);
define('SPREADSHEET_EXCEL_READER_TYPE_FORMAT', 0x41e);
define('SPREADSHEET_EXCEL_READER_TYPE_XF', 0xe0);
define('SPREADSHEET_EXCEL_READER_TYPE_BOOLERR', 0x205);
define('SPREADSHEET_EXCEL_READER_TYPE_UNKNOWN', 0xffff);
define('SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR', 0x22);
define('SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS', 0xE5);

define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS' , 25569);
define('SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904', 24107);
define('SPREADSHEET_EXCEL_READER_MSINADAY', 86400);
//define('SPREADSHEET_EXCEL_READER_MSINADAY', 24 * 60 * 60);

//define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%.2f");
define('SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT', "%s");


/*
* Place includes, constant defines and $_GLOBAL settings here.
* Make sure they have appropriate docblocks to avoid phpDocumentor
* construing they are documented by the page-level docblock.
*/

/**
* A class for reading Microsoft Excel Spreadsheets.
*
* Originally developed by Vadim Tkachenko under the name PHPExcelReader.
* (http://sourceforge.net/projects/phpexcelreader)
* Based on the Java version by Andy Khan (http://www.andykhan.com). Now
* maintained by David Sanders. Reads only Biff 7 and Biff 8 formats.
*
* @category Spreadsheet
* @package Spreadsheet_Excel_Reader
* @author Vadim Tkachenko <vt@phpapache.com>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version Release: @package_version@
* @link http://pear.php.net/package/PackageName
* @see OLE, Spreadsheet_Excel_Writer
*/
class Spreadsheet_Excel_Reader
{
/**
* Array of worksheets found
*
* @var array
* @access public
*/
var $boundsheets = array();

/**
* Array of format records found
*
* @var array
* @access public
*/
var $formatRecords = array();

/**
* todo
*
* @var array
* @access public
*/
var $sst = array();

/**
* Array of worksheets
*
* The data is stored in 'cells' and the meta-data is stored in an array
* called 'cellsInfo'
*
* Example:
*
* $sheets --> 'cells' --> row --> column --> Interpreted value
* --> 'cellsInfo' --> row --> column --> 'type' - Can be 'date', 'number', or 'unknown'
* --> 'raw' - The raw data that Excel stores for that data cell
*
* @var array
* @access public
*/
var $sheets = array();

/**
* The data returned by OLE
*
* @var string
* @access public
*/
var $data;

/**
* OLE object for reading the file
*
* @var OLE object
* @access private
*/
var $_ole;

/**
* Default encoding
*
* @var string
* @access private
*/
var $_defaultEncoding;

/**
* Default number format
*
* @var integer
* @access private
*/
var $_defaultFormat = SPREADSHEET_EXCEL_READER_DEF_NUM_FORMAT;

/**
* todo
* List of formats to use for each column
*
* @var array
* @access private
*/
var $_columnsFormat = array();

/**
* todo
*
* @var integer
* @access private
*/
var $_rowoffset = 1;

/**
* todo
*
* @var integer
* @access private
*/
var $_coloffset = 1;

/**
* List of default date formats used by Excel
*
* @var array
* @access public
*/
var $dateFormats = array (
// 0xe => "d/m/Y",
0xe => "Y/m/d",
0xf => "d-M-Y",
0x10 => "d-M",
0x11 => "M-Y",
0x12 => "h:i a",
0x13 => "h:i:s a",
0x14 => "H:i",
0x15 => "H:i:s",
0x16 => "d/m/Y H:i",
0x2d => "i:s",
0x2e => "H:i:s",
0x2f => "i:s.S");

/**
* Default number formats used by Excel
*
* @var array
* @access public
*/
var $numberFormats = array(
0x1 => "%1.0f", // "0"
0x2 => "%1.2f", // "0.00",
0x3 => "%1.0f", //"#,##0",
0x4 => "%1.2f", //"#,##0.00",
0x5 => "%1.0f", /*"$#,##0;($#,##0)",*/
0x6 => '$%1.0f', /*"$#,##0;($#,##0)",*/
0x7 => '$%1.2f', //"$#,##0.00;($#,##0.00)",
0x8 => '$%1.2f', //"$#,##0.00;($#,##0.00)",
0x9 => '%1.0f%%', // "0%"
0xa => '%1.2f%%', // "0.00%"
0xb => '%1.2f', // 0.00E00",
0x25 => '%1.0f', // "#,##0;(#,##0)",
0x26 => '%1.0f', //"#,##0;(#,##0)",
0x27 => '%1.2f', //"#,##0.00;(#,##0.00)",
0x28 => '%1.2f', //"#,##0.00;(#,##0.00)",
0x29 => '%1.0f', //"#,##0;(#,##0)",
0x2a => '$%1.0f', //"$#,##0;($#,##0)",
0x2b => '%1.2f', //"#,##0.00;(#,##0.00)",
0x2c => '$%1.2f', //"$#,##0.00;($#,##0.00)",
0x30 => '%1.0f'); //"##0.0E0";

// }}}
// {{{ Spreadsheet_Excel_Reader()

/**
* Constructor
*
* Some basic initialisation
*/
function Spreadsheet_Excel_Reader()
{
$this->_ole =& new OLERead();
$this->setUTFEncoder('iconv');
}

// }}}
// {{{ setOutputEncoding()

/**
* Set the encoding method
*
* @param string Encoding to use
* @access public
*/
function setOutputEncoding($encoding)
{
$this->_defaultEncoding = $encoding;
}

// }}}
// {{{ setUTFEncoder()

/**
* $encoder = 'iconv' or 'mb'
* set iconv if you would like use 'iconv' for encode UTF-16LE to your encoding
* set mb if you would like use 'mb_convert_encoding' for encode UTF-16LE to your encoding
*
* @access public
* @param string Encoding type to use. Either 'iconv' or 'mb'
*/
function setUTFEncoder($encoder = 'iconv')
{
$this->_encoderFunction = '';

if ($encoder == 'iconv') {
$this->_encoderFunction = function_exists('iconv') ? 'iconv' : '';
} elseif ($encoder == 'mb') {
$this->_encoderFunction = function_exists('mb_convert_encoding') ?
'mb_convert_encoding' :
'';
}
}

// }}}
// {{{ setRowColOffset()

/**
* todo
*
* @access public
* @param offset
*/
function setRowColOffset($iOffset)
{
$this->_rowoffset = $iOffset;
$this->_coloffset = $iOffset;
}

// }}}
// {{{ setDefaultFormat()

/**
* Set the default number format
*
* @access public
* @param Default format
*/
function setDefaultFormat($sFormat)
{
$this->_defaultFormat = $sFormat;
}

// }}}
// {{{ setColumnFormat()

/**
* Force a column to use a certain format
*
* @access public
* @param integer Column number
* @param string Format
*/
function setColumnFormat($column, $sFormat)
{
$this->_columnsFormat[$column] = $sFormat;
}


// }}}
// {{{ read()

/**
* Read the spreadsheet file using OLE, then parse
*
* @access public
* @param filename
* @todo return a valid value
*/
function read($sFileName)
{
$res = $this->_ole->read($sFileName);

// oops, something goes wrong (Darko Miljanovic)
if($res === false) {
// check error code
if($this->_ole->error == 1) {
// bad file
die('The filename ' . $sFileName . ' is not readable');
}
// check other error codes here (eg bad fileformat, etc...)
}

$this->data = $this->_ole->getWorkBook();

$this->_parse();
}


// }}}
// {{{ _parse()

/**
* Parse a workbook
*
* @access private
* @return bool
*/
function _parse()
{
$pos = 0;

$code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8;
$length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8;

$version = ord($this->data[$pos + 4]) | ord($this->data[$pos + 5])<<8;
$substreamType = ord($this->data[$pos + 6]) | ord($this->data[$pos + 7])<<8;
//echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n";

if (($version != SPREADSHEET_EXCEL_READER_BIFF8) &&
($version != SPREADSHEET_EXCEL_READER_BIFF7)) {
return false;
}

if ($substreamType != SPREADSHEET_EXCEL_READER_WORKBOOKGLOBALS){
return false;
}

//print_r($rec);
$pos += $length + 4;

$code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8;
$length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8;

while ($code != SPREADSHEET_EXCEL_READER_TYPE_EOF) {
/*$code:儲存格的格式;這裡不是parse儲存格的地方,而是parse整體變數的地方。
224 0xe0 SPREADSHEET_EXCEL_READER_TYPE_XF
*/
// echo "<br> parse_code:$code";
switch ($code) {
case SPREADSHEET_EXCEL_READER_TYPE_SST:
//echo "Type_SST\n";
$spos = $pos + 4;
$limitpos = $spos + $length;
$uniqueStrings = $this->_GetInt4d($this->data, $spos+4);
$spos += 8;
for ($i = 0; $i < $uniqueStrings; $i++) {
// Read in the number of characters
if ($spos == $limitpos) {
$opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
if ($opcode != 0x3c) {
return -1;
}
$spos += 4;
$limitpos = $spos + $conlength;
}
$numChars = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8);
//echo "i = $i pos = $pos numChars = $numChars ";
$spos += 2;
$optionFlags = ord($this->data[$spos]);
$spos++;
$asciiEncoding = (($optionFlags & 0x01) == 0) ;
$extendedString = ( ($optionFlags & 0x04) != 0);

// See if string contains formatting information
$richString = ( ($optionFlags & 0x08) != 0);

if ($richString) {
// Read in the crun
$formattingRuns = ord($this->data[$spos]) | (ord($this->data[$spos+1]) << 8);
$spos += 2;
}

if ($extendedString) {
// Read in cchExtRst
$extendedRunLength = $this->_GetInt4d($this->data, $spos);
$spos += 4;
}

$len = ($asciiEncoding)? $numChars : $numChars*2;
if ($spos + $len < $limitpos) {
$retstr = substr($this->data, $spos, $len);
$spos += $len;
}else{
// found countinue
$retstr = substr($this->data, $spos, $limitpos - $spos);
$bytesRead = $limitpos - $spos;
$charsLeft = $numChars - (($asciiEncoding) ? $bytesRead : ($bytesRead / 2));
$spos = $limitpos;

while ($charsLeft > 0){
$opcode = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$conlength = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
if ($opcode != 0x3c) {
return -1;
}
$spos += 4;
$limitpos = $spos + $conlength;
$option = ord($this->data[$spos]);
$spos += 1;
if ($asciiEncoding && ($option == 0)) {
$len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength);
$retstr .= substr($this->data, $spos, $len);
$charsLeft -= $len;
$asciiEncoding = true;
}elseif (!$asciiEncoding && ($option != 0)){
$len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength);
$retstr .= substr($this->data, $spos, $len);
$charsLeft -= $len/2;
$asciiEncoding = false;
}elseif (!$asciiEncoding && ($option == 0)) {
// Bummer - the string starts off as Unicode, but after the
// continuation it is in straightforward ASCII encoding
$len = min($charsLeft, $limitpos - $spos); // min($charsLeft, $conlength);
for ($j = 0; $j < $len; $j++) {
$retstr .= $this->data[$spos + $j].chr(0);
}
$charsLeft -= $len;
$asciiEncoding = false;
}else{
$newstr = '';
for ($j = 0; $j < strlen($retstr); $j++) {
$newstr = $retstr[$j].chr(0);
}
$retstr = $newstr;
$len = min($charsLeft * 2, $limitpos - $spos); // min($charsLeft, $conlength);
$retstr .= substr($this->data, $spos, $len);
$charsLeft -= $len/2;
$asciiEncoding = false;
//echo "Izavrat\n";
}
$spos += $len;

}
}
$retstr = ($asciiEncoding) ? $retstr : $this->_encodeUTF16($retstr);
// echo "Str $i = $retstr\n";
if ($richString){
$spos += 4 * $formattingRuns;
}

// For extended strings, skip over the extended string data
if ($extendedString) {
$spos += $extendedRunLength;
}
//if ($retstr == 'Derby'){
// echo "bb\n";
//}
$this->sst[]=$retstr;
}
/*$continueRecords = array();
while ($this->getNextCode() == Type_CONTINUE) {
$continueRecords[] = &$this->nextRecord();
}
//echo " 1 Type_SST\n";
$this->shareStrings = new SSTRecord($r, $continueRecords);
//print_r($this->shareStrings->strings);
*/
// echo 'SST read: '.($time_end-$time_start)."\n";
break;

case SPREADSHEET_EXCEL_READER_TYPE_FILEPASS:
return false;
break;
case SPREADSHEET_EXCEL_READER_TYPE_NAME:
//echo "Type_NAME\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_FORMAT:
$indexCode = ord($this->data[$pos+4]) | ord($this->data[$pos+5]) << 8;

if ($version == SPREADSHEET_EXCEL_READER_BIFF8) {
$numchars = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8;
if (ord($this->data[$pos+8]) == 0){
$formatString = substr($this->data, $pos+9, $numchars);
} else {
$formatString = substr($this->data, $pos+9, $numchars*2);
}
} else {
$numchars = ord($this->data[$pos+6]);
$formatString = substr($this->data, $pos+7, $numchars*2);
}

$this->formatRecords[$indexCode] = $formatString;
// echo "Type.FORMAT\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_XF:
//global $dateFormats, $numberFormats;
$indexCode = ord($this->data[$pos+6]) | ord($this->data[$pos+7]) << 8;

// echo "<br>Type.XF ".count($this->formatRecords['xfrecords'])."code: $indexCode ";

if (array_key_exists($indexCode, $this->dateFormats)) {
// echo "<br>Type.XF ".count($this->formatRecords['xfrecords'])."code: $indexCode ";
// echo "<br>isdate ".$this->dateFormats[$indexCode];
$this->formatRecords['xfrecords'][] = array(
'type' => 'date',
'format' => $this->dateFormats[$indexCode]
);
}elseif (array_key_exists($indexCode, $this->numberFormats)) {
// echo "<br>isnumber ".$this->numberFormats[$indexCode];
$this->formatRecords['xfrecords'][] = array(
'type' => 'number',
'format' => $this->numberFormats[$indexCode]
);
}else{
$isdate = FALSE;
if ($indexCode > 0){
if (isset($this->formatRecords[$indexCode]))
$formatstr = $this->formatRecords[$indexCode];
//echo '.other.';
//echo "\ndate-time=$formatstr=\n";
if ($formatstr)
if (preg_match("/[^hmsday\/\-:\s]/i", $formatstr) == 0) { // found day and time format
$isdate = TRUE;
$formatstr = str_replace('mm', 'i', $formatstr);
$formatstr = str_replace('h', 'H', $formatstr);
//echo "\ndate-time $formatstr \n";
}
}

if ($isdate){
$this->formatRecords['xfrecords'][] = array(
'type' => 'date',
'format' => $formatstr,
);
}else{
$this->formatRecords['xfrecords'][] = array(
'type' => 'other',
'format' => '',
'code' => $indexCode
);
}
}
//echo "\n";
// echo "<br>type1:".$this->formatRecords['xfrecords'][count($this->formatRecords['xfrecords'])-1]['type'];
//$this->formatRecords['xfrecords'][$xfindex]['type'] == 'number'
break;
case SPREADSHEET_EXCEL_READER_TYPE_NINETEENFOUR:
//echo "Type.NINETEENFOUR\n";
$this->nineteenFour = (ord($this->data[$pos+4]) == 1);
break;
case SPREADSHEET_EXCEL_READER_TYPE_BOUNDSHEET:
//echo "Type.BOUNDSHEET\n";
$rec_offset = $this->_GetInt4d($this->data, $pos+4);
$rec_typeFlag = ord($this->data[$pos+8]);
$rec_visibilityFlag = ord($this->data[$pos+9]);
$rec_length = ord($this->data[$pos+10]);

if ($version == SPREADSHEET_EXCEL_READER_BIFF8){
$chartype = ord($this->data[$pos+11]);
if ($chartype == 0){
$rec_name = substr($this->data, $pos+12, $rec_length);
} else {
$rec_name = $this->_encodeUTF16(substr($this->data, $pos+12, $rec_length*2));
}
}elseif ($version == SPREADSHEET_EXCEL_READER_BIFF7){
$rec_name = substr($this->data, $pos+11, $rec_length);
}
$this->boundsheets[] = array('name'=>$rec_name,
'offset'=>$rec_offset);

break;

}

//echo "Code = ".base_convert($r['code'],10,16)."\n";
$pos += $length + 4;
$code = ord($this->data[$pos]) | ord($this->data[$pos+1])<<8;
$length = ord($this->data[$pos+2]) | ord($this->data[$pos+3])<<8;

//$r = &$this->nextRecord();
//echo "1 Code = ".base_convert($r['code'],10,16)."\n";
}

foreach ($this->boundsheets as $key=>$val){
$this->sn = $key;
$this->_parsesheet($val['offset']);
}
return true;

}

/**
* Parse a worksheet
*
* @access private
* @param todo
* @todo fix return codes
*/
function _parsesheet($spos)
{
$cont = true;
// read BOF
$code = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;

$version = ord($this->data[$spos + 4]) | ord($this->data[$spos + 5])<<8;
$substreamType = ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8;

if (($version != SPREADSHEET_EXCEL_READER_BIFF8) && ($version != SPREADSHEET_EXCEL_READER_BIFF7)) {
return -1;
}

if ($substreamType != SPREADSHEET_EXCEL_READER_WORKSHEET){
return -2;
}
//echo "Start parse code=".base_convert($code,10,16)." version=".base_convert($version,10,16)." substreamType=".base_convert($substreamType,10,16).""."\n";
$spos += $length + 4;
//var_dump($this->formatRecords);
//echo "code $code $length";
while($cont) {
//echo "mem= ".memory_get_usage()."\n";
// $r = &$this->file->nextRecord();
$lowcode = ord($this->data[$spos]);
if ($lowcode == SPREADSHEET_EXCEL_READER_TYPE_EOF) break;
$code = $lowcode | ord($this->data[$spos+1])<<8;
$length = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
$spos += 4;
$this->sheets[$this->sn]['maxrow'] = $this->_rowoffset - 1;
$this->sheets[$this->sn]['maxcol'] = $this->_coloffset - 1;
//echo "Code=".base_convert($code,10,16)." $code\n";
unset($this->rectype);
$this->multiplier = 1; // need for format with %
// echo "<br>parsesheet_code:$code ";
/*
$code:
253 0xfd SPREADSHEET_EXCEL_READER_TYPE_LABELSST G/通用格式
638 0x27e SPREADSHEET_EXCEL_READER_TYPE_RK2 日期或unknow格式
*/

switch ($code) {
case SPREADSHEET_EXCEL_READER_TYPE_DIMENSION:
//echo 'Type_DIMENSION ';
if (!isset($this->numRows)) {
if (($length == 10) || ($version == SPREADSHEET_EXCEL_READER_BIFF7)){
$this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+2]) | ord($this->data[$spos+3]) << 8;
$this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+6]) | ord($this->data[$spos+7]) << 8;
} else {
$this->sheets[$this->sn]['numRows'] = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8;
$this->sheets[$this->sn]['numCols'] = ord($this->data[$spos+10]) | ord($this->data[$spos+11]) << 8;
}
}
//echo 'numRows '.$this->numRows.' '.$this->numCols."\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_MERGEDCELLS:
$cellRanges = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
for ($i = 0; $i < $cellRanges; $i++) {
$fr = ord($this->data[$spos + 8*$i + 2]) | ord($this->data[$spos + 8*$i + 3])<<8;
$lr = ord($this->data[$spos + 8*$i + 4]) | ord($this->data[$spos + 8*$i + 5])<<8;
$fc = ord($this->data[$spos + 8*$i + 6]) | ord($this->data[$spos + 8*$i + 7])<<8;
$lc = ord($this->data[$spos + 8*$i + 8]) | ord($this->data[$spos + 8*$i + 9])<<8;
//$this->sheets[$this->sn]['mergedCells'][] = array($fr + 1, $fc + 1, $lr + 1, $lc + 1);
if ($lr - $fr > 0) {
$this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['rowspan'] = $lr - $fr + 1;
}
if ($lc - $fc > 0) {
$this->sheets[$this->sn]['cellsInfo'][$fr+1][$fc+1]['colspan'] = $lc - $fc + 1;
}
}
//echo "Merged Cells $cellRanges $lr $fr $lc $fc\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_RK:
case SPREADSHEET_EXCEL_READER_TYPE_RK2:
//echo 'SPREADSHEET_EXCEL_READER_TYPE_RK'."\n";
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
$rknum = $this->_GetInt4d($this->data, $spos + 6);
$numValue = $this->_GetIEEE754($rknum);
//echo $numValue." ";
if ($this->isDate($spos)) {
list($string, $raw) = $this->createDate($numValue);
}else{
$raw = $numValue;
if (isset($this->_columnsFormat[$column + 1])){
$this->curformat = $this->_columnsFormat[$column + 1];
}
$string = sprintf($this->curformat, $numValue * $this->multiplier);
//$this->addcell(RKRecord($r));
}
//hwjane
$this->addcell($row, $column, $string, $raw);
//echo "Type_RK $row $column $string $raw {$this->curformat}\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_LABELSST:
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
$xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5])<<8;
$index = $this->_GetInt4d($this->data, $spos + 6);
//var_dump($this->sst);
$this->addcell($row, $column, $this->sst[$index]);
//echo "LabelSST $row $column $string\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_MULRK:
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$colFirst = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
$colLast = ord($this->data[$spos + $length - 2]) | ord($this->data[$spos + $length - 1])<<8;
$columns = $colLast - $colFirst + 1;
$tmppos = $spos+4;
for ($i = 0; $i < $columns; $i++) {
$numValue = $this->_GetIEEE754($this->_GetInt4d($this->data, $tmppos + 2));
if ($this->isDate($tmppos-4)) {
list($string, $raw) = $this->createDate($numValue);
}else{
$raw = $numValue;
if (isset($this->_columnsFormat[$colFirst + $i + 1])){
$this->curformat = $this->_columnsFormat[$colFirst + $i + 1];
}
$string = sprintf($this->curformat, $numValue * $this->multiplier);
}
//hwjane
//$rec['rknumbers'][$i]['xfindex'] = ord($rec['data'][$pos]) | ord($rec['data'][$pos+1]) << 8;
$tmppos += 6;
$this->addcell($row, $colFirst + $i, $string, $raw);
//echo "MULRK $row ".($colFirst + $i)." $string\n";
}
//MulRKRecord($r);
// Get the individual cell records from the multiple record
//$num = ;

break;
case SPREADSHEET_EXCEL_READER_TYPE_NUMBER:
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
$tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent
if ($this->isDate($spos)) {
list($string, $raw) = $this->createDate($tmp['double']);
// $this->addcell(DateRecord($r, 1));
}else{
//$raw = $tmp[''];
if (isset($this->_columnsFormat[$column + 1])){
$this->curformat = $this->_columnsFormat[$column + 1];
}
$raw = $this->createNumber($spos);
$string = sprintf($this->curformat, $raw * $this->multiplier);
}
// echo "number exist?";
$this->addcell($row, $column, $string, $raw);
//echo "Number $row $column $string\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_FORMULA:
case SPREADSHEET_EXCEL_READER_TYPE_FORMULA2:
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
if ((ord($this->data[$spos+6])==0) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) {
//String formula. Result follows in a STRING record
//echo "FORMULA $row $column Formula with a string<br>\n";
} elseif ((ord($this->data[$spos+6])==1) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) {
//Boolean formula. Result is in +2; 0=false,1=true
} elseif ((ord($this->data[$spos+6])==2) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) {
//Error formula. Error code is in +2;
} elseif ((ord($this->data[$spos+6])==3) && (ord($this->data[$spos+12])==255) && (ord($this->data[$spos+13])==255)) {
//Formula result is a null string.
} else {
// result is a number, so first 14 bytes are just like a _NUMBER record
$tmp = unpack("ddouble", substr($this->data, $spos + 6, 8)); // It machine machine dependent
if ($this->isDate($spos)) {
list($string, $raw) = $this->createDate($tmp['double']);
// $this->addcell(DateRecord($r, 1));
}else{
//$raw = $tmp[''];
if (isset($this->_columnsFormat[$column + 1])){
$this->curformat = $this->_columnsFormat[$column + 1];
}
$raw = $this->createNumber($spos);
$string = sprintf($this->curformat, $raw * $this->multiplier);

// $this->addcell(NumberRecord($r));
}
//hwjane
$this->addcell($row, $column, $string, $raw);
//echo "Number $row $column $string\n";
}
break;
case SPREADSHEET_EXCEL_READER_TYPE_BOOLERR:
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
$string = ord($this->data[$spos+6]);
$this->addcell($row, $column, $string);
//echo 'Type_BOOLERR '."\n";
break;
case SPREADSHEET_EXCEL_READER_TYPE_ROW:
case SPREADSHEET_EXCEL_READER_TYPE_DBCELL:
case SPREADSHEET_EXCEL_READER_TYPE_MULBLANK:
break;
case SPREADSHEET_EXCEL_READER_TYPE_LABEL:
$row = ord($this->data[$spos]) | ord($this->data[$spos+1])<<8;
$column = ord($this->data[$spos+2]) | ord($this->data[$spos+3])<<8;
$this->addcell($row, $column, substr($this->data, $spos + 8, ord($this->data[$spos + 6]) | ord($this->data[$spos + 7])<<8));

// $this->addcell(LabelRecord($r));
break;

case SPREADSHEET_EXCEL_READER_TYPE_EOF:
$cont = false;
break;
default:
//echo ' unknown :'.base_convert($r['code'],10,16)."\n";
break;

}
$spos += $length;
}

if (!isset($this->sheets[$this->sn]['numRows']))
$this->sheets[$this->sn]['numRows'] = $this->sheets[$this->sn]['maxrow'];
if (!isset($this->sheets[$this->sn]['numCols']))
$this->sheets[$this->sn]['numCols'] = $this->sheets[$this->sn]['maxcol'];

}

/**
* Check whether the current record read is a date
*
* @param todo
* @return boolean True if date, false otherwise
*/
function isDate($spos)
{
//$xfindex = GetInt2d(, 4);

$a=ord($this->data[$spos+4]);
$b=ord($this->data[$spos+5]);
// echo "spos:$spos data1:$a data2:$b " ;
$xfindex = ord($this->data[$spos+4]) | ord($this->data[$spos+5]) << 8;
//echo 'check is date '.$xfindex.' '.$this->formatRecords['xfrecords'][$xfindex]['type']."\n";
//var_dump($this->formatRecords['xfrecords'][$xfindex]);
// echo "<br>xfindex:$xfindex ";
if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'date') {
$this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format'];
$this->rectype = 'date '.$xfindex;
// echo "rectype-date:$this->rectype ";
return true;
} else {
if ($this->formatRecords['xfrecords'][$xfindex]['type'] == 'number') {
$this->curformat = $this->formatRecords['xfrecords'][$xfindex]['format'];
$this->rectype = 'number '.$xfindex;
// echo "rectype-number:$this->rectype ";
if (($xfindex == 0x9) || ($xfindex == 0xa)){
$this->multiplier = 100;
}
}else{
$this->curformat = $this->_defaultFormat;
$this->rectype = 'unknown '.$xfindex;
// echo "rectype-unknown:$this->rectype ";
}
return false;
}
}

//}}}
//{{{ createDate()

/**
* Convert the raw Excel date into a human readable format
*
* Dates in Excel are stored as number of seconds from an epoch. On
* Windows, the epoch is 30/12/1899 and on Mac it's 01/01/1904
*
* @access private
* @param integer The raw Excel value to convert
* @return array First element is the converted date, the second element is number a unix timestamp
*/
function createDate($numValue)
{
if ($numValue > 1) {
$utcDays = $numValue - ($this->nineteenFour ? SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS1904 : SPREADSHEET_EXCEL_READER_UTCOFFSETDAYS);
$utcValue = round(($utcDays+1) * SPREADSHEET_EXCEL_READER_MSINADAY);
// $string = date ($this->curformat, $utcValue);
$string = date ($this->curformat, $utcValue - 1 * 24 * 60 * 60);
$raw = $utcValue;
} else {
$raw = $numValue;
$hours = floor($numValue * 24);
$mins = floor($numValue * 24 * 60) - $hours * 60;
$secs = floor($numValue * SPREADSHEET_EXCEL_READER_MSINADAY) - $hours * 60 * 60 - $mins * 60;
$string = date ($this->curformat, mktime($hours, $mins, $secs));
}

return array($string, $raw);
}

function createNumber($spos)
{
$rknumhigh = $this->_GetInt4d($this->data, $spos + 10);
$rknumlow = $this->_GetInt4d($this->data, $spos + 6);
//for ($i=0; $i<8; $i++) { echo ord($this->data[$i+$spos+6]) . " "; } echo "<br>";
$sign = ($rknumhigh & 0x80000000) >> 31;
$exp = ($rknumhigh & 0x7ff00000) >> 20;
$mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
$mantissalow1 = ($rknumlow & 0x80000000) >> 31;
$mantissalow2 = ($rknumlow & 0x7fffffff);
$value = $mantissa / pow( 2 , (20- ($exp - 1023)));
if ($mantissalow1 != 0) $value += 1 / pow (2 , (21 - ($exp - 1023)));
$value += $mantissalow2 / pow (2 , (52 - ($exp - 1023)));
//echo "Sign = $sign, Exp = $exp, mantissahighx = $mantissa, mantissalow1 = $mantissalow1, mantissalow2 = $mantissalow2<br>\n";
if ($sign) {$value = -1 * $value;}
return $value;
}

function addcell($row, $col, $string, $raw = '')
{
//echo "ADD cel $row-$col $string\n";
$this->sheets[$this->sn]['maxrow'] = max($this->sheets[$this->sn]['maxrow'], $row + $this->_rowoffset);
$this->sheets[$this->sn]['maxcol'] = max($this->sheets[$this->sn]['maxcol'], $col + $this->_coloffset);
$this->sheets[$this->sn]['cells'][$row + $this->_rowoffset][$col + $this->_coloffset] = $string;
if ($raw)
$this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['raw'] = $raw;
if (isset($this->rectype))
$this->sheets[$this->sn]['cellsInfo'][$row + $this->_rowoffset][$col + $this->_coloffset]['type'] = $this->rectype;
// echo "type:".$this->rectype."<br>";
}


function _GetIEEE754($rknum)
{
if (($rknum & 0x02) != 0) {
$value = $rknum >> 2;
} else {
//mmp
// first comment out the previously existing 7 lines of code here
// $tmp = unpack("d", pack("VV", 0, ($rknum & 0xfffffffc)));
// //$value = $tmp[''];
// if (array_key_exists(1, $tmp)) {
// $value = $tmp[1];
// } else {
// $value = $tmp[''];
// }
// I got my info on IEEE754 encoding from
// http://research.microsoft.com/~hollasch/cgindex/coding/ieeefloat.html
// The RK format calls for using only the most significant 30 bits of the
// 64 bit floating point value. The other 34 bits are assumed to be 0
// So, we use the upper 30 bits of $rknum as follows...
$sign = ($rknum & 0x80000000) >> 31;
$exp = ($rknum & 0x7ff00000) >> 20;
$mantissa = (0x100000 | ($rknum & 0x000ffffc));
$value = $mantissa / pow( 2 , (20- ($exp - 1023)));
if ($sign) {$value = -1 * $value;}
//end of changes by mmp

}

if (($rknum & 0x01) != 0) {
$value /= 100;
}
return $value;
}

function _encodeUTF16($string)
{
$result = $string;
if ($this->_defaultEncoding){
switch ($this->_encoderFunction){
case 'iconv' : $result = iconv('UTF-16LE', $this->_defaultEncoding, $string);
break;
case 'mb_convert_encoding' : $result = mb_convert_encoding($string, $this->_defaultEncoding, 'UTF-16LE' );
break;
}
}
return $result;
}

function _GetInt4d($data, $pos)
{
$value = ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | (ord($data[$pos+3]) << 24);
if ($value>=4294967294)
{
$value=-2;
}
return $value;
}

}

/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* c-hanging-comment-ender-p: nil
* End:
*/

?>

Read more...

2010年12月23日 星期四

將excel的資料轉到mysql,使用phpMyAdmin及phpExcelReader

因為工作需要,必須將excel的資料輸出到mysql的資料庫,本來以為很簡單的一件事,卻弄了一整天,已經近半年沒更新的部落格,不得已再記錄一下將excel的資料轉到mysql的心得。

首先先找到一個工具,叫做 phpExcelReader ,直接跑下載回來的壓縮檔中的 example.php。
不過要注意:

Excel\reader.php 中的下面這行要修改
將 require_once 'Spreadsheet/Excel/Reader/OLERead.php';
改為 require_once 'oleread.inc';

再將example.php 中
修改 $data->setOutputEncoding('CP1251');
為 $data->setOutputEncoding('CP950');
否則中文會有問題。

修改 $data->read('jxlrwtest.xls');
為自己的 excel 檔名。

再視情況修改下面這段:
for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) {
for ($j = 1; $j <= $data->sheets[0]['numCols']; $j++) {
echo "\"".$data->sheets[0]['cells'][$i][$j]."\",";
}
echo "\n";
}

我把它改成下列:
for ($i = 1; $i <= $data->sheets[0]['numRows']; $i++) {
for ($j = 1; $j <= $data->sheets[0]['numCols']; $j++) {
echo "\"".$data->sheets[0]['cells'][$i][$j];

if($j < $data->sheets[0]['numCols'])
echo "\",";
else
echo "\"";

}
echo "<br>";

第一段紅字功能:原來的程式每一列最後會多一個逗號,這會讓phpMyAdmin認為多一個欄位,而無法正常轉excel檔到mysql。這一段會拿掉最後的逗號。

第二段紅字( echo "<br>";):會讓每一列自動跳行。
上面的結果會直接顯示在瀏覽器上,必須複製到文字檔,再用phpMyAdmin的工具,載到mysql中(phpMyAdmin使用方法請自行搜尋參考)。

不過以上的工具,手動的部分還很多,例如必須在mysql建立一個對應到excel的table,能不能自動在mysql建立一個表,自動將excel轉進去呢,下次有時間想來自己寫一個看看。


參考
以 PHP 讀取 Excel 檔案的資料
http://www.cnblogs.com/phpzxh/archive/2009/09/16/1568133.html
http://wefong.idv.tw/xkellydz/viewthread.php?tid=576

Read more...

2010年12月5日 星期日

巧克力戚風蛋糕

材料:
全蛋5顆,砂糖(蛋白) 81克,砂糖(蛋黃) 28克,水50克,沙拉油50克,鹽1克,低粉60克,小蘇打粉1.5克,可可粉13克。


先將蛋白蛋黃分開

蛋黃部分

1.缸盆放入水,沙拉油,塩,一起攪拌(用白色的)。
2.再加入砂糖一起攪拌。
3.再加入低粉,小蘇打,可可粉混合過篩,再放入攪拌。
4.加蛋黃再攪拌。


蛋白部分

1.蛋白放入另一缸盆,加少許塔塔粉,用打蛋器快速打。
2.蛋白產生紋路,加三分之一的糖。
3.紋路明顯,再加三分之一的糖。
4.快完成時,再加三分之一的糖。
(全部過程不超過十分鐘,當蛋白呈勾狀,不會掉下來即可,打太久會太硬)

將三分之一的蛋白,倒到含蛋黃的缸盆,攪拌均勻。再倒回含蛋白的缸盆,再攪拌均勻,倒入活動模約八九分滿。倒入後敲一下,再開始烤。180/170 約烤三十五分鐘,烤完吐司後,關烤箱烤四十五分鐘。

Read more...

2010年6月15日 星期二

將wii化身為多媒體播放器-MPlayerCE安裝及使用

因為最近購買了一台投影機,想說可以實現在家中看電影的想法。但是投影機因為要同時接筆電播影片,並且還要玩wii。接線有些麻煩,且要頻繁的插拔訊號線,而且筆電除了播放影片之外,還有其它用途,每次看影片,筆電就不能使用,且必須搬來搬去,實在是佷麻煩。

上網作功課,發現wii可以透過一些軟體,變成多媒體播放器,於是花了功夫研究相關知識,並且動手安裝軟體。然後很順利的把wii變成多媒體播放器,這樣的好處除了省下了一台筆電的空間,免去頻繁的插拔訊號線之外,更棒的是wii的手把控制器,同時變成了影片播放的遙控器,可以控制暫停,前進,倒退,音量等等,不必像使用筆電時,還要去操作鍵盤及滑鼠。

目前我看到兩款可以在wii播影片的軟體,MPlayerCE和GeexBox,據說MPlayerCE的效能比GeexBox好,於是我直接選擇安裝MPlayerCE,而安裝完成MPlayerCE之後,效能果然不錯,雖然有人說wii的多媒體播放效能不佳,但我看來至少播放rmvb檔案相當流暢。也因此我就沒有再嘗試GeexBox,因此也不知GeexBox的播放效能如何了。

MPlayerCE是為Wii量身定做的一款播放器,它的功能如下:

  1. 支援多種多媒體格式播放,如MP3、AVI、RMVB、MOV、3GP、FLV、ASF、 WMV等。
  2. MPlayerCE的功能是十分強大,不僅僅支援從SD卡播放影片,而且可以直接播放DVD,更出色的是MPlayerCE還可以讀取USB設備,支援從ftp伺服器播放。
  3. 支援fat及ntfs檔案格式。


MPlayerCE安裝步驟

MPlayerCE是運行在HBC頻道的軟體,因此我們需要一台裝好HBC頻道的Wii,除此之外還需要SD卡一張。

安裝herms 的cIOS
這裡要注意,因為mplayer-ce需要cIOS202、222、223、224的支援,如果你的wii沒有安裝這些cIOS,請先安裝。

cIOS202、222、223、224 v5下載(擇一下載):

http://www.mediafire.com/file/mnjmmtmdliz/herms_IOS_rev5.zip

這些cIOS的安裝方法,網路有佷多說法,有的人使用cios_installer安裝,還有人說要wii連上網路才可安裝。不過我是直接使用wad manager直接離線安裝wad文件。我的WII版本是4.2J,其他不同版本的WII是否有不同安裝法,我個人並不清楚。另外我第一次是安裝herms 的cIOS version 4的版本,安裝正常。可是執行MPlayerCE的時候不能正常撥放。後來改安裝version 5的版本,才可正常使用。


安裝MPlayerCE

擇一下載MPlayerCE 0.77
http://www.mediafire.com/file/mdztjeqytro/mplayer-ce.zip


下載MPlayerCE_V0.77,解壓後把 mplayer_ce內文件複製到SD卡的apps目錄下,apps是wii的hbc頻道預設的執行目錄,沒有apps文件夾,請新建文件夾並重命名為apps。這樣我們就完成了安裝。


使用MPlayerCE


1.選擇HBC頻道,進入頻道。
2.在選單選擇MPlayerCE,選擇Load啟動。

這時會出現wii sd, wii usb, wii dvd等選單,此時選擇Wii SD則瀏覽存在SD卡上的檔案,MPlayerCE對中文名稱支援較差,無法正確顯示中文,但播放時並不會有問題。其它wii usb ,wii dvd分別對應不同的儲存媒體文件,如果我們想要播放DVD,Wii DVD,則放入光碟。

以下這篇文章,有關於MPlayerCE 的更多說明及用法,請參考。
http://www.wiibrew.org/wiki/MPlayer_CE

Read more...

2010年6月7日 星期一

wii的IOS,cIOS資料整理

以下的文章都是參考自網路加以整理,加上自己的理解及心得,因為有很多人引用這些資料,原創者有些已不可考,僅在文末列出處較可信的來源。

什麼是IOS

說到IOS,肯定有人會問,什麼是IOS?這裡我把它理解為Input Output System。其實就是系統內核和一系列的驅動程序集,作用是操縱Wii的硬體,提供了usb鍵盤,光驅,按鍵,sd卡,文件系統,安全等等介面,其特點之一就是模組化。
隨著系統的硬體升級和新遊戲的推出,wii需要安裝新的IOS來支援新遊戲和新硬體,但是舊的IOS還是繼續存在系統中,而不是被新IOS覆蓋,因為舊的遊戲還需要他們。
這樣的軟體架構所造成的問題就是,隨著系統的不斷升級,最終Flash資源會被用盡。目前Flash容量大約在512MB左右,用了大約100多MB。 Wii大約有20個左右不同的IOS。每個遊戲都需要使用其中的一個IOS。所有的IOS裡面還有DVD的驅動程式,原本是用來播放DVD用的,但是Wii並沒有使用他們,後來被破解用來讀取備份光碟。
IOS30,50,60是被系統選單使用的,如果操作不當的話,就可能會變磚,所以很危險,不要輕易動它。(請別任意刪除或反安裝整數的IOS檔)


什麼是cIOS

cIOS就是IOS的破解檔,像是個人電腦裡的破解檔一樣。IOS 像是原來軟體的執行檔(*.exe) ,而cIOS 像是執行遊戲被破解過後的執行檔。所有的cIOS都是根據官方的IOS修改而來,安裝cIOS不會更動原來的IOS,舉例來說,Custom IOS36及Custom IOS38根據IOS36及IOS38修改而來,安裝後變成IOS249,而不會更動原來的IOS36及IOS38。

一般而言,只要不涉及系統關鍵的IOS,例如IOS30、IOS60等等,其他的像cIOS36、cIOS38可以任意安裝沒有多大問題,不過建議還是安裝之前先仔細閱讀相關IOS安裝說明,上面一般都有注意事項,免得造成不必要的損失。

wii的漏洞(cIOS的原理)

Wii裡面有一個著名的bug,它用了strcmp()來compare signature,但是strcmp遇到0x00就退出了,駭客就利用這個bug來修改數據,使得signature的第一位為0x00。有一些駭客如 WiiGater,Waninkoko等利用reverse engineering,反彙編IOS,寫了這些補丁程序,這樣backup launcher這樣的loader就強制光碟遊戲用IOS249+補丁程序Rev10來執行。但是這個漏洞卻在3.3版的升級中被nintendo補上了,但是有一個IOS卻漏掉了,那就是IOS16,它出自於nintendo service CD),他還有strcmp bug,所以可以利用IOS16來安裝其他的patch過的IOS.所以3.3版以上都要安裝這個IOS16先才能裝CIOS249Rev10等其他CIOS。

IOS的版本

IOS是以 IOSxxx-Vyyyyy存在的,前面的xxx是編號,範圍限制在4-255之間(其中100為BC,101為MIOS),也就是4#-255#,4#之前被系統佔用,而後面的yyyyy才是IOS的版本,例如IOS30的版本有1042、2816等……IOS的這裡的IOS 包括了官方IOS和所有cIOS,也就是說,在wii的系統裡無法從字面辨識何者為IOS,何者為cIOS,但可以從IOS的編號得知,例如IOS202、IOS222、IOS223、IOS224、IOS249、IOS250都是cIOS

如何查看wii裡面有那些ios,cios

1 Softchip來看。
2 CFM中Nand的Systeme Channel裡面看。
3 用AnyTitle Lister查看,它在SD卡裡生成TITLES.TXT文件,裡面就是你所有的ios。


幾個常見的cIOS

IOS249
就是cIOS36及cIOS38,是由waninkoko釋出的,根據wii官方的IOS36及IOS38修改而來,安裝後會放在IOS249的位置。(Rev10以上的版本會再生成一個備份IOS250),其與官方IOS最主要的區別就是DIP模塊,即DVD Interface,用於IOS與光碟之間的數據交換。眾所周知,官方的IOS是不能讀取備份碟的,而IOS249則由自製的DIP模塊,取代了官方的IOS36及IOS38,使其能夠讀取備份碟。 cIOS38 為 cIOS36 的新版本. 安裝後不會修改 IOS36 與 IOS38 的內容,建議安裝 CIOS38。

IOS202/IOS222/IOS223
Kwiirk開發了另一套cIOS--IOS202/IOS222/IOS223,首先提供了對USB2.0的支援。而Herms在uLoader中包含了新的IOS202/IOS222/IOS223,其實在Kwiirk的cIOS基礎上吸收了Waninkoko的優點所開發的。IOS202可以播放 DVD並支援 USB2.0 ,IOS222/IOS223支援多種USB介面。一款在wii上面的播放軟體MPlayerCE,必須安裝IOS202才可以使用。有些遊戲,如怪物獵人3 需要IOS202 IOS222 和223的支援。


升級

  遊戲執行前會首先檢查光碟中的文件,即Title metadata中指定的IOS版本,如果一樣,則直接執行遊戲;如果不一樣,則先加載指定的IOS,然後再執行遊戲。與其類似的還有頻道,wiiware等東西。
  這裡就牽涉到升級,如果光碟中update分區的IOS版本新於主機裡的IOS,則會開始升級(這就是為什麼有的高版本的機器放入帶低版本升級文件的盤也會提示升級原因);如果有主機裡沒有的IOS,也會開始升級。

  這裡參考第一段,當我們執行一個去升級的遊戲時,如果主機裡沒有所指定的IOS,則會導致遊戲無法執行,這時只需安裝指定的IOS。這也是為什麼要補全ios的原因有些IOS是很相似的,例如所有的IOS都支持WiFi和USB功能,而且有些能夠被替換。而wii的每次升級則增加新的IOS,升級舊有的IOS,而並不代替舊有IOS,為什麼這樣做?其實我們可以這樣理解,每一個遊戲光碟,wiiware都會指定其使用的IOS,如果升級覆蓋掉了原有的IOS,那麼遊戲便無法執行。

  再次強調一點,wii 升級不是覆蓋原有的ios,而是增加新的ios,系統中的每個ios都是有用的,隨便刪除ios非常容易導致無解的全磚


深入了解


光碟,頻道,wiiware都有其指定的IOS來執行,如 BKL,SC這樣的程序可以強制使用IOS249,那SysMenu呢?
SysMenu同樣在特定的IOS上執行,3.4使用IOS50,3.4以前使用IOS30,4.0使用ios60同樣的,SysMenu也可以指定其使用的IOS,如果我們指定其使用IOS249,那麼我們就可以直接從光碟頻道進入遊戲了。
  然而問題也隨之到來,在光碟頻道中我們無法指定其使用的IOS,這樣即使備份碟能夠正常顯示出,但是仍然無法遊戲,當然我們可以用如IOS patcher等軟體指定遊戲使用IOS249,但我想沒有多少人願意再重新刻錄一次吧。當然問題總有解決的辦法,如果我們把所有官方IOS都替換上自制 DIP模塊,那麼我們就可以直接從光碟頻道執行備份碟了,這也是另一種cIOS。 當然這樣做仍然有缺點,只有同區的遊戲才能在光碟頻道顯示。

關於IOS和SysMenu的關係

上面談到,IOS是wii系統的內核,而SysMenu是相應的用戶界面。系統內核是必須存在的,但是用戶界面則並不強制要求,在主機內存在高系統版本的IOS時,低版本的SysMenu也同樣允許使用,這也是系統選單可以降級的基礎,同樣也是IOS補全而不用升級系統版本原理。但是,當系統中不存在SysMenu所需要的IOS時,系統就會出現各種問題(半磚)甚至無法執行(全磚),這也就是為什麼IOS不能隨便刪除的原因。

某些頻道也是需要系統IOS支持的(4.0下購物頻道需要IOS61),所以當相應IOS不存在時,該頻道也無法執行。
在通常情況下,我們是無法對IOS進行操作的,因為用戶界面(SysMenu)沒有賦予我們這個權限,正是由於高手們對SysMenu的研究,發現了各種漏洞,使我們破解了wii的系統,獲得了對IOS進行操作的能力,才讓我們對wii的應用得到了極大地擴展。但是魔高一尺道高一丈,任天堂也在盡力的封堵這些漏洞,所以系統才出現了官方版和破解版,IOS也出現了有漏洞(+trucha、freethebug)和無漏洞(-trucha)。

官方的升級(主機選單「本體更新」,聯網、進頻道、玩遊戲)時,是將IOS和 SysMenu同時更新的,而官方所更新的全都是無漏洞的版本,缺少漏洞會導致自製程序無法安裝無法執行,所以建議不要通過官方更新,為了玩遊戲(遊戲不要求SysMenu版本)用頻道可以補全IOS,為了獲得新的系統特性可以安裝破解版。

什麼是cIOSCORP

cIOSCORP主要是針對軟改的玩家開發的一款可以讓光碟頻道讀取遊戲的自製軟體,安裝之後原本不能從光碟頻道讀取遊戲的主機,現在大多數遊戲你將可以從光碟頻道讀取了。除此之外,使用cIOSCORP,軟體會自動安裝多個cIOS到你的主機內,可以加強你的主機對於遊戲的兼容性。

總結一點就是,cIOSCORP可以給你帶來以下好處:
1、光碟頻道現在可以讀取Wii以及NGC遊戲。
2、提升吉他英雄5、怪物獵人3、新超級馬力歐兄弟Wii等遊戲的兼容性。
3、其它特性

有些情況下安裝cIOSCORP會變磚,要特別留意,作足功課再安裝。(http://www.91wii.com/thread-25429-1-1.html)


常用的IOS列表

4、9、10、11、12、13、14、 15、16、17、20、21、22、22、28、30、31、33、34、35、36、3738、50、51、5355、60、61、202(裝給MPlayerCE用的)、222(v5 版)223(v5版)249(CIOS38)250(CIOS38)、254。 粉紅色的IOS是大部分遊戲所需的。


參考:
http://www.360gq.com/wii-hbc/wii-ios.htm
http://s90304a123.pixnet.net/blog/post/28072480
http://bbs.yyjoy.com/viewthread.php?tid=144696

Read more...

2010年5月30日 星期日

選購微型投影機心得筆記

投影機的新選擇-微型投影機

很久以來,就一直想買一台投影機,但傳統的投影機,不但耗電,而且燈泡使用壽命不長,燈泡的價格又很貴,所以一直遲遲無法決定是否購買。直到幾年前看到所謂的微型投影機進入市場,又多了一種選購的參考。

微型投影機可嵌入在各種行動裝置,或是只是獨立的當作投影機。由於內建在行動裝置的概念相當具有吸引力,吸引不少廠商投入,特別是行動電話的業者,例如Motorola等皆計劃推出具有微型投影功能的移動電話。但在目前為止,發展較快是以外接方式的獨立型微型投影機。它透過連接線,可以與手機、數位相機、多媒體播放器、筆記型電腦、電玩遊樂器等連接

但這類型的微型投影機,最大的缺點,就是投影的亮度,無法與傳統的投影機相比,通常只能在不開燈的室內使用,而且投影的尺寸也比較小。而優點則是用電池供電,輕薄短小,省電環保且易於攜帶,而且led燈泡的壽命通常在20000小時以上,正常使用的情況下,每天使用五個小時,十年內都不用更換燈泡。有些微型投機還內建記憶體或能外接usb儲存裝置或sd卡,配合本身內建的影音播放的功能,即使不帶筆記型電腦,一片小小的sd卡就能播放,更增加其便利性

另一些特點是,大部分的微型投影機都不需要待機,所以開關機時間都很快,通常只要幾秒鐘而已,因此用來接家庭用的影音設備也很方便,打開影像來源,再開投影機,就好像開電視一樣,按下電源開關畫面就出來了,而關機也是直接關掉就好了。而且微型投影機很多機種也沒有風扇的設計,這是因為它耗電量少,產生的熱量也少,所以不需要風扇來降溫,也因此沒有擾人的噪音問題



通常再買3C產品以前,我都會做足功課,特別是高單價的產品,選購微型投影要注意那些事情呢?

LED光源技術


LED投影機主要有DLP技術、LCOS技術(又可以細分為CF- LCOS技術與CS-LCOS技術),還有少部分的LCD技術產品。 目前我最常看到的是LCOS,而LCD比較少,據我所知是技術層次較低的產品。

亮度

參考:微型投影機的亮度單位說明
目前LED微型投影機最大的缺憾就是亮度不足。所以對於LED投影機,消費者最關注的是亮度指標。但市場宣傳中,計算亮度的單位有些混亂,有的只有標示10流明、15流明;有的卻出現動輒宣稱800流明、1000流明。其實流明是一種光通量的計算單位,所謂光通量,指的是光源在單位時間內發射出的光量總合,稱為光源的發光通量。 光通量與距離無關,它是光源發出光能的總和。如果只使用光通量的流明值,它指出了投影機發出的總光能強度,而與螢幕和距離無關 。目前比較常見用來表示微型投影機的亮度單位是「ANSI流明」。

ANSI流明測定環境如下:
1) 投影機與幕之間距離:2.4米。
2) 幕為60英寸。
3) 用測光筆測量布幕"田"字形九個交叉點上的各點照度,乘以面積,得到投影畫面的9個點的亮度。
4) 求出9個點亮度的平均值,就是ANSI流明。

由上面的說明可知ANSI流明是加上距離與布幕的因素,來表示微型投影機的亮度,因此是比較客觀的計算單位目前常見的LED微型投影機以10-15 ANSI流明為主流,所以大部分要在較暗的環境中,才有好的投影效果。
但其中也有一些高亮度的機種,像Optoma的LED微型投影機PK201及PK301,可達20及50ANSI流明,不過價位較高。

USB裝置

很多微型投影機都提供USB的連接裝置,但其中仍有很多要注意的地方,我剛開始接觸微型投影機時,以為有USB連接裝置,就代表可以直接播放USB磁碟內的影片,其實不然,有的USB只是為了透過筆電的USB供電或充電,有的USB是當作訊號的傳輸用途,插入USB就可以瞬間將投影機變成外接螢幕,也就是取代電腦D-SUB的輸出,這種情況下,就不必透過傳統的15PIN的VGA線連接,有些有內建記憶體的微型投影機,也用USB連接來傳送檔案。

直接播放檔案

如果攜帶微型投影機外出,還要帶一台筆電的話,那麼微型投影機的便利性一定大打折扣,所以有些微型投影機是可以直接播放影音、圖片,甚至是word,powerpoint檔案。這種類型的微型投影機,有的是內建1G或2G的記憶體,可以用USB裝置讀入檔案,也有一些是可外接SD卡,直接播放SD卡內的檔案。不過我所看到這些可播放檔案的微型投影機,能播放的影片格式,大都很有限,大部分從網路上下載的rmvb檔或是flv檔,都不支援播放(當然還是有一些高階機種除外),所以如果買微型投影機來當作電影欣賞的話,這個直接播放檔案的功能,有時也不一定用的到,除非您願意花時間先轉檔,轉成它所支援的格式。

輸入裝置

輸入裝置除了上面提到的USB,也有D-SUB來連接傳統電腦的VGA訊號。另外如果要連接一般的家用視聽器材或電視遊樂器,就要注意是否有AV端子的輸入裝置。基本上VGA及AV訊號輸入是必備的,有一些機種也提供USB來接受電腦訊號輸入,但要注意這樣的功能可能僅接受windows 作業系統,如果您用到MAC或LINUX等其他作業系統,購買前應詢問清楚。

解析度

現在大部分的微型投影機解析度都在640x480,這個解析度應該是最基本需求了,因為即使是640x480,也是勉強可辨識Windows上的文字而已,如果低於640x480,例如480x320,用來播放影片應該還勉強可以看,如果是作簡報,要顯示windows系統上的文字,可能看起來眼睛就有點累了。有些比較高階的機種,解析度比較高,例如Optoma PK201 支援854×480,是16:9的顯示比例。還要注意的是有些強調支援Windows 800x600或是1024x768的機種,其功能只是讓你接上該解析度的電腦時,可以從投影機看到畫面,但事實上原生解析度也只有640x480。

耗電

微型投影機可以使用電池供電,可見耗電量是很少的。微型投影機的耗電量,一般都只有幾瓦而已(應該是10瓦以下吧!),如果是傳統的投影機,至少都要100瓦以上。



2010/06/15 更新
後來我買的是Oculon PP920這台微型投影機,它標榜台灣製造,且採用最新3M二代RGB技術,強調色飽和度與亮度絕佳。它沒有風扇設計、無噪音。它的售價在7200左右,且有15ansi流明的亮度,算是比較亮的機種(當然不能和Optoma PK301的50流明比),這台微型投影機的色彩還不錯,我在全暗室投影100吋,看起來亮度也還OK,但可能因為解析度只有640x480,所以投到100吋,看來有些模糊,我覺得最佳的顯示尺寸在50-80吋之間。不過它是比較陽春的機種,但我覺得與其他功能相當的機種相比,CP值還是蠻高的。另外還有一個特點就是他的電池可以與NOKIA BL-5C共用。我看電池規格大小完全一樣,我剛好有一台這樣的手機,把電池放進去手機,使用完全沒問題。

不過我覺的它有一些小缺點,這是購買之前無法預料到的,所以買之前還是看實機比較好。就是投影機放到腳架上面,沒什麼固定的東西,只是卡住而已,很容易不小心碰到就掉下來。另外它附的訊號線實在太短了,大約只有一公尺而已,所以接筆電很麻煩,要離投影機很近才行。如果你的腳架比較高,你的筆電就要找東西墊高,否則就只能再買一條訊號延長線了。它的訊號線還有一個缺點,就是卡進去插槽的地方很淺,容易晃動(如果您有wii或ps2,就知道我在說什麼,wii的訊號線插進去,固定的很穩,不會晃動)。我覺得如果常插拔的話,這部分可能會先故障或接觸不良,還有它的電池充滿電大約只能撐個五十分鐘左右,放一部影片都不夠,所以我是直接接家中的電源。

露天有賣家作了這台微型投影機的介紹影片:http://www.youtube.com/watch?v=MIL-tFrGoss

它有另一款pp980,規格功能就比較優,有支援sd卡播放。除了可以直接放映數位照片、也可放映PowerPoint或是影片。重量僅有 120公克,內建鋰電池,連續工作時間可達二小時(這是別人介紹的,很懷疑能用這麼久?),內建 1Gb 記憶空間和外部高容量SDHC 記憶卡兩種影音資料。我看到pchome 只賣7800,應該是很超值吧!

相關參考文章

全球最小微型投影機Optoma PK101試用
三大投影技術躍進,口袋型與微型投影機紛紛問世
50流明微型投影機 Optoma PK301

Read more...

2010年5月24日 星期一

微型投影機的亮度單位說明

目前LED微型投影機最大的缺憾就是亮度不足。所以對於LED投影機,消費者最為關注的是亮度指標。但市場宣傳中,有的只有標示10流明、15流明;有的卻出現動輒宣稱800流明、1000流明,甚至1500流明。這之間的差異如此之大,難免令人困惑,關於投影機的亮度單位,究竟有何玄機呢?

流明(光通量)

投影機的光通量,指光源在單位時間內發射出的光量稱為光源的發光通量,單位是流明。這樣計算出來旳流明值是一個比較直觀的概念。例如40瓦日光燈產生的光通量為2600流明,光通量與距離無關,它是光源發出光能的總和。如果只使用光通量的流明值,它指出了投影機發出的總光能強度,而與螢幕和距離無關。市面上的led投影機宣稱1500流明,甚至2000流明,指的應是光通量單位,而非與距離有關的亮度單位。

本來有關投影機亮度的問題,用光通量(流明)已可以算是清楚了,它指的是光源發出光能總和,光通量愈大當然就愈亮。但為了明確的表明亮度與距離與投影目標的關係,於是還有兩個計算單位:

峰值流明

有的投影機的「亮度」指標單位為「峰值流明」,這裏雖然用到了流明這個詞,但卻不是標準的光通量,而指的是真正的亮度。其定義是:在螢幕中央以螢幕高度的1/6為邊長取一正方形,測量此面積上的發光強度。所以「峰值流明」不再是光通量單位而真正是不折不扣的亮度單位。

ANSI流明

ANSI是美國國家標準協會的英文縮寫,ANSI流明是各種投影機亮度測試方法中的一種,不過這樣方法比較科學且簡便,所以被全世界大多數廠家所接受

ANSI流明測定環境如下:
1) 投影機與幕之間距離:2.4米。
2) 幕為60英寸。
3) 用測光筆測量布幕"田"字形九個交叉點上的各點照度,乘以面積,得到投影畫面的9個點的亮度。
4) 求出9個點亮度的平均值,就是ANSI流明。

由上面的說明可知ANSI流明比峰值流明的測試方法,更平均更客觀。峰值流明的峰值亮度的測試法,往往數值會比ANSI流明的測試法高出20%至100%,有一些廠家為了粉飾自己的產品從而取悅消費者,往往標註為峰值亮度的數據,所以選購投影機時千萬不可被誤導

以微型投影機而言,10ANSI流明投影機的功率大約在3W(亮度雖低,但可以用電池供電)。如果要達到2000ANSI流明的真實亮度,正常需要大約1500至3000W的LED光源!這是一個幾乎不可能的天文數字。目前常見的微型投影機,ANSI流明大約在10-15之間,少數可達20,30甚至50ANSI流明,就已經是亮度相當高的產品了。像Optoma的LED微型投影機PK301可達50ANSI流明(50流明微型投影機 Optoma PK301),是我看過亮度最高的機種,不過價位也相對的不是太友善。

參考

http://www.mobile01.com/topicdetail.php?f=347&t=40225&p=1&cache=0

http://www.ledyjm.net/shnews1.asp?id=246&ty=1&ac=ledinfo

Read more...

2010年5月10日 星期一

我蒐集並實際測試的windows 7 瘦身法

Windows 7 裝完以後,即使不安裝其他軟體,也可能佔用到10G左右的空間,到底10G的東西,微軟塞了一些什麼東東在裡面呢?大家絕對不要小看微軟製造垃圾的能力,這裡頭一定有很多東西都是不必要的。雖然現在硬碟很大,但對於經常用ghost備份系統的我,C槽的佔用空間太大,時間與空間的浪費,還是一個很擾人的問題。

網路上有很多windows 7的瘦身法。我把它蒐集了一下,稍微分類,也針對winsxs這個資料夾提出自己測試心得。

基本瘦身法

  1. 系統保護功能關閉
    如果常用ghost等軟體來備份系統,那麼先把系統保護功能給關閉。
    打開檔案總管,在「電腦」按右鍵,「內容」,「系統保護」,「設定」,「關閉系保護」。

  2. 轉移虛擬記憶體到C碟以外的地方。
    打開檔案總管,在「電腦」按右鍵,「內容」,「進階系統設定」,在效能的地方按「設定」,再按「進階」的頁面,點下方虛擬記憶體的「變更」。 再把「自動管理所有磁碟的分頁檔大小」的勾拿掉,點C磁碟,再點「沒有分頁檔」,接著再點其他你要轉移虛擬記憶體的磁碟,再點「系統管理大小」。

  3. 關閉休眠功能
    如果您用不到休眠的功能,可以將它關閉。方法是以「系統管理員身份執行」命令列模式,進入命令提示符號以後,手動輸入「powercfg -h off」。

    windows 7經過以上的瘦身之後,依各人的電腦狀況不同,大約可以省下2G以上的空間。

刪除不用的檔案

以下的windows 7瘦身法大都參考自網路,如果可以省下幾百mb的空間,我有實際測試,如果只能省下幾十mb則沒有。

另外如果你刪除以下提到這些檔案的話,有些檔案windows 7會提示你沒有權限,所以我們要先獲得權限。 首先在想刪除的資料夾上點擊右鍵,內容,點「安全性」,「進階」,「擁有者」,接著點「編輯」,如果有看到您想變更的帳戶,直接選擇,否則選「其他使用者或群組」,在輸入選擇的對象名稱裡面輸入你的用戶名,或者點「進階」,「立即尋找」,選擇你的用戶名,「確定」,然後選擇「取代子容器與物件的擁有者」,(不選擇這個的話,我們就只有這個檔案夾的所有權,並沒有他的子檔案夾和裡面的檔案的所有權),然後應用-確定,這樣我們就擁有了這個檔案夾的所有權。

取得擁有權以後,還要變更權限才行, 同上,先在想刪除的資料夾上點擊右鍵,內容,點「安全性」,「進階」,請點選「權限」的頁面,「變更權限」,「新增」,把你的帳號名填入,或者用「進階」,「立即尋找」,「確定」,再點擊你的用戶名,「編輯」,把「完全控制/允許」打勾,「確定」,再把底下「以這個物件的繼承權限取代所有子物件的權限」打勾,然後點「確定」。


以下檔案可以刪除:

  1. C:\Windows\System32\DriverStore\FileRepository的資料夾
    搜索輸入 mdm*.inf (21.6M) 可以刪,現在早已沒人使用。
    搜索輸入ati*.inf (14.6M) nv*.inf(94.9M) (ATI用戶刪NVIDIA、NVIDIA用戶刪ATI)
  2. C:\Boot (13.3M) 這個裡面是不同語言的Windows啟動界面,除zh-TW外均可刪除。
  3. C:\perflogs\System\Diagnostics (9.39M) 這個是系統測試之後的測試記錄文件存放處。
  4. C:\Windows\Help (66.7M) windows 7的說明檔,不看的話可以刪掉。
  5. C:\Windows\IME\IMEJP10 日文輸入法(37.8M)。
  6. C:\Windows\IME\imekr8 韓文輸入法(2.86M)。
  7. C:\Windows\System32\IME 下 也會有這幾項輸入法!刪除方法同上
  8. C:\Windows\Web\Wallpaper
    裡面是Windows內建桌布,不需要的可以刪除掉,或者轉移至其他硬碟(45.5M)
  9. C:\Windows\System32\DriverStore\FileRepository
    這個資料夾中是Window 7內建驅動備份,一般來說都不需要,所以可以刪除(790M)
    (but 當這個資料夾被我刪掉之後,插入隨身硬碟會找不到安裝驅動程式,所以不建議為了windows 7瘦身而刪這個資料夾,會很麻煩的。)
  10. C:\Windows\winsxs\Backup
    這個裡面是一些備份檔案,可以刪除(353M)
  11. C:\Users\Public (或是c:\使用者\公用)
    所有用戶公用檔案夾,裡面有一些示範圖片、影片等,可以刪除之(217M)
    其中
    C:\Users\Public\Recorded TV\Sample Media(公用錄製的節目)為 Windows Media Center的示範電視錄製檔案(143M)
    C:\Users\Public\Music\Sample Music(公用音樂)為示範音樂(59M)
    C:\Users\Public\Pictures\Sample Pictures(公用圖片)為示範圖片(4.7M)
    C:\Users\Public\Videos(公用視訊)為示範影片(9.6M)
  12. C:\Program Files\Microsoft Games
    windows 7自帶遊戲,不玩的可以刪除掉,刪除自帶遊戲的方法如下:
    開始-控制台-程式集-開啟或關閉Windows功能,找到遊樂場,點加號展開,去掉你想刪除的遊戲前面的對勾,然後確定,即可。
    以下這些刪不刪隨便:
    C:\Program Files\Microsoft Games\Chess
      象棋高手(30.3M)
      C:\Program Files\Microsoft Games\FreeCell
      空當接龍(690K)
      C:\Program Files\Microsoft Games\Hearts
      紅心大戰(687K)
      C:\Program Files\Microsoft Games\inkball
      墨球(1.2M)
      C:\Program Files\Microsoft Games\Mahjong
      麻將高手(12.6M)
    C:\Program Files\Microsoft Games\Minesweeper
      掃雷(4.96M)
    C:\Program Files\Microsoft Games\Purble Place
      廚房大賽(3M)
      C:\Program Files\Microsoft Games\Solitaire
      紙牌(696K)
    C:\Program Files\Microsoft Games\SpiderSolitaire
      蜘蛛紙牌(697K)

令人討厭的Winsxs資料夾


注意Winsxs被破壞、移動或刪除,會造成Windows7無法正常開機。
如果是初學者或沒有需要,請勿進行以下所描述的操作。

vista和windows 7都有一個winsxs的資料夾,這個資料夾非常的大,把C磁碟的空間佔用了4G左右,一般提到的windows 7的瘦身,都不會包含此一資料夾, 因為雖然它體積很大,但卻是必要的,無法直接刪除。微軟把需要的或是不需要的東西,都塞進這個資料夾,每次windows update的每個版本的歷史檔案,都在這個資料夾,所以這個討厭的winsxs是一直在成長的。

一般有兩個方法可以對這個winsxs瘦身,一個是對這個資料夾進行壓縮,另一個就是把winsxs資料夾移動到其他磁碟,從而增加C磁碟空間。第一個方法,對使用軟體備份C槽,幫助不大,因為像ghost或trueimage這類的軟體,本身就會對資料進行壓縮了。第二個方法,可以減少C槽空間,但有一個致命的缺點,當使用windows update時,必須用很麻煩的方法,才能更新。

所以基於以上的原因,當我試到最後,幾乎是放棄對winsxs動腦筋了,不過我還是把我的操作過程記錄下來,太無聊的人可以試看看,另外一提的是,網路上移動winsxs的方法,抄來抄去,很多都是錯的,如果照作,根本就無法移動它。

首先需要MoveFile and PendMoves這兩個文件。這裡用到的Movefile 和PendMoves是Microsoft提供用來更改,刪除,重命名windows正在使用的文件的工具,但必須重開機才能更改。


下載點:
http://www.xun6.com/file/98854dd32/PendMoves.zip.html
http://www.mediafire.com/file/mmjhkjkyrmq/PendMoves.zip

請下載,解壓縮以後,複製到c:\windows\system32資料夾下。

第一步:Create Symbolic Directory Link 建立一個資料夾的符號鏈結,這個東西熟悉Linux的人應該很清楚。

請以系統管理員的身份執行「命令列模式」。
本文中的命令都需要系統管理員的權限,所以「命令列模式」和檔案總管(Windows Explorer)都需要以管理員身份進行

輸入 CD\Windows 命令,按enter

輸入: mklink /D winsxs.link winsxs.moved ,按enter

第二步:取得winsxs這個資料夾的權限

打開資源管理器,指向c:\windows\winsxs,右鍵點擊winsxs資料夾,選擇「內容」,在「安全性」頁面點「進階」按鈕,選擇「擁有者」,再點「編輯」,變更擁有者為你自己的帳戶。然後點「取代子容器與物件的擁有者」,點確定。接著您還要取得 winsxs本身及下級資料夾和文件的完全訪問權限(請參考上面藍色文字部分)。 這一步一定要保證對winsxs所有的資料夾和文件都有完全訪問控制權, 不然待會兒在movefile C:\Windows\winsxs C:\Windows\winsxs.moved會出錯的。Error 5。)

第三步:Schedule File Moves

還是在命令列模式下

movefile C:\Windows\winsxs C:\Windows\winsxs.moved 按enter

Movefile v1.0 - copies over an in-use file at boot time
Move successfully scheduled.

movefile C:\Windows\winsxs.link C:\Windows\winsxs 按enter

Movefile v1.0 - copies over an in-use file at boot time
Move successfully scheduled.

然後輸入PendMoves,確認一下是否在計劃中

C:\Windows>pendmoves

PendMove v1.1
Copyright (C) 2004 Mark Russinovich
Sysinternals - wwww.sysinternals.com

Source: C:\Windows\winsxs
Target: C:\Windows\winsxs.moved

Source: C:\Windows\winsxs.link
Target: C:\Windows\winsxs

Time of last update to pending moves key: 2010/5/1 上午 11:09

(如果上一步授權很順利,這一步就會正常通過。但事實上我測試時有很多情況會失敗 ,最後我用F8後進入安全模式,然後把授權重新做了一遍才成功)


第四步:重啟電腦

第五步:複製winsxs到其他資料夾
在命令列模式下,輸入robocopy C:\Windows\winsxs.moved D:\Windows\winsxs /E /COPYALL /SEC 按enter,
如果不是在D磁碟,請替換 (Robocopy會跑很久,大約一個小時)

第六步:重新建立關聯
cd \windows
rmdir winsxs
mklink /D winsxs D:\windows\winsxs

(請注意,這裡起先的winsxs只是一個空link,然後讓這個連接指向你的新winsxs複製資料夾所在地,而原文件還存放在winsxs.moved裡面,會在下一步裡刪除)

第七步:重啟

第八步:刪除C:\Windows\winsxs.moved

至此,已經將winsxs這個佔用空間巨大的資料夾移出C磁碟,收工。

如果不小心把winsxs刪除了,可以這樣恢復,拿個系統磁碟然後在開始運行欄裡輸入sfc /scannow就可以進行修復。這個方法我沒試過,放在這裡供大家不時之需吧。





參考:
NT SERVICE\TrustedInstaller

Windows 7 瘦身優化秘技

安裝完Windows7之後所需的瘦身清理工作

把Win7 裡的winSxS資料夾移到D碟

Read more...

2010年4月23日 星期五

zencart筆記

更改ZenCart文字大小


本文參考http://tgw1029.blogspot.com/2009/06/zencart_21.html ,但補充一些在1.3.8不一樣的部分。

以預設模板為例,來修改全站本文文字大小,進入ZenCart目錄,找到\includes\templates\zccn\css 這個資料夾,裡面有兩個檔案需要更改stylesheet.css及schinese_stylesheet.css
找到
body {
margin: 0;
font-family: verdana, arial, helvetica, sans-serif;
font-size: 75%;
color: #000000;
background-color: #ffffff;
}
把font-size改大就行了,最大到100% 若不知道100%是多大 改成12px這種寫法也行 。


忘記zen cart管理員密碼,怎麼找回密碼

以前測試zen cart曾經有過幾次管理員密碼忘記的情況,一般情況下呢,可以使用後台登陸界面的找回密碼解決,但麻煩的是,有時候郵箱也忘了,怎麼辦呢?
以下是找回管理員密碼的幾種方法(翻譯自zen cart官方FAQ文件):
1. 如果你的zen cart系統有其他的管理賬戶,可以用它登陸後台,通過以下路徑修改管理員密碼Admin->Tools->Admin Settings;

2.如果忘記了管理員賬戶和密碼,首先你可以通過後台登陸界面的Resend Password功能,輸入郵箱來找回密碼;由於某些原因,郵箱可能也忘記了,不要急,還有辦法-通過操作數據庫來創建臨時密碼,具體操作如下: 打開phpMyadmin(一般的虛擬主機後台都有),點擊SQL標籤,執行下面的語句 DELETE FROM admin WHERE admin_name = 『Admin』; INSERT INTO admin (admin_name, admin_email, admin_pass, admin_level) VALUES (『Admin』, 『admin@localhost』, 『351683ea4e19efe34874b501fdbf9792:9b』, 1); 如果你的數據庫表適用了前綴,自然你需要在上面的數據表前加上前綴,例如: 「… FROM/INTO prefix_admin …」 操作成功後,你就可以通過下列賬號密碼登錄後台了: 賬號:Admin 密碼:admin 注意大小寫,一定不能錯。 接下來要做的就是通過 Admin->Tools->Admin Settings刪除臨時賬號,創建新賬號。




更改首頁說明文字
位於ZenCart目錄的incudes/languages/tchinese/html_includes/zccn/define_main_page.php

更改首頁banner
位於ZenCart目錄的images/banners/banner1.jpg 影像大小為750*105
位於ZenCart目錄的images/banners/banner2.jpg 影像大小為420*197

主要頁面編輯
從管理員頁面進入→工具→簡易頁面管理
可以更改一些頁面訊息。



Read more...

2010年4月14日 星期三

使用php在mysql移動記錄指標的方法

習慣用vb的人,都知道移動記錄指標,可以用movefirst,movenext,movelast等指令,在php中使用mysql如何達到同樣效果。

可以用data_seek來做:
$result->data_seek($row_number)跳到$row_number這筆資料記錄
<?php
$s1="(select maSeq,percent as sWeight from scription where dSeq=".$ars['seq'].") a";
$q="select a.*,b.price,b.name from ".$s1." left outer join material b on a.maSeq = b.seq";
$rs1 = $mysqli->query($q) or die($mysqli->error);
$ars1 = $rs1->fetch_array();

$maPrice=0;

//以下迴圈,會使rs1資料集,一直讀到最後一筆
do {
if (!$ars1) break;
$ra=($ars['totWeight']/($ars['loss']/100))/$ars['tWeight'];
$maPrice+=$ars1['sWeight']*$ra*$ars1['price'];
} while ($ars1 = $rs1->fetch_array());

//以下做法會使rs1記錄指標回到第一筆(movefirst),重新再從第一筆讀起
$rs1->data_seek(0);
$ars1 = $rs1->fetch_array(); //相當於movenext
do {

if (!$ars1) break;?>
<tr <?php
$co = $cnt % 2;
if ($co== 0) echo "bgcolor=#E6F2FF";
else echo "bgcolor=#DEE9EB";?>>
<td height="16" bgcolor="#D0DFDB" class="content-xs1"><?php echo $ars1['name']?></td>
<td bgcolor="#D0DFDB" class="content-xs1"><?php echo round($ars1['sWeight']*$ra,2);?></td>
<td bgcolor="#D0DFDB" class="content-xs1"><?php echo $ars1['price'];?></td>
<td bgcolor="#D0DFDB" class="content-xs1"><?php echo round($ars1['sWeight']*$ra*$ars1['price'],2);?></td>
</tr>
<?php } while ($ars1 = $rs1->fetch_array())?>

Read more...

2010年4月9日 星期五

ps2 debugging station免改機直讀備份片

首先不能免俗的聲明一下,以下方法,僅限於在保存原版光碟的情況下,使用備份光碟,避免原版光碟刮傷,請勿使用於非法用途。


在網拍花了大約一千元,買到一台未改機的ps2 debugging station,型號是DTL-H30100,這台是開發用的PS2,我先前找到的資料,是說開發用的PS2可以直接讀取備份片,可是真的買來以後,發現根本不是這麼回事。於是上網找了許多資料。

其中有一段資料這樣寫著:
DTL-H30102U: 120V AC. PAL. Says "TEST" on top. Plays any retail PSX game in PAL format. Plays any burned PSX game in PAL format. Plays any retail PS2 game regardless of region. Plays burned PS2 disks only if patched as master. Comes with Sony Hard Drive. Comes with Network adapter. Offically supports network play. It's a system designed to test european games in america.(http://assemblergames.com/forums/showthread.php?t=7223)

以上找到的資料,與我的型號相近,我的是日規機,而它可能是歐系主機在usa美國的開發版本,可以讀pal系統的燒錄片,而且可以跨區讀取。還有這一段話:

Plays burned PS2 disks only if patched as master

意思是它可以讀燒錄片,但是要patched as master,從這裡找線索,又找了一些資料,發現Loser's PS2 Disc patcher這套軟體,可以將ps2的燒錄片,patch成master,就可以在ps2 的debugging station直讀備份片了。這個檔案相當難找,因為ps2已經是過時的東西了,大部分的連結都失效了,花了我近一天的時間,終於找到了下載點。馬上試用,真的可以讀取。

以下是Loser's PS2 Disc patcher的說明文件,主要是提到它可以patch成不同區域,就是可跨區,也可以play discs on debug ps2 station:

This program can patch psx and ps2 cd and dvd images for different regions.

It also supports the patching of the main exe filename for psx and ps2 cd
and dvd images.

It can patch discs to make them appear to be 'master discs'.
(Lets you play burnt discs on debug PS2s!)

oO Images Types Supported Oo

Psx (mode 2) cd images that are either 2048 or 2352 bytes per sector.
Ps2 (mode 2) cd images that are either 2048 or 2352 bytes per sector.
Ps2 dvd iso images.
Ps2 dvd gi images.
(cd and dvd images with image headers are supported as long as their 'bytes per sector' format is supported)


使用方法如下:

  1. 將原版的ps光碟片,以iso製作工具,製成iso檔。(可參考這裡:介紹及比較兩個免費製作iso檔的工具)
  2. 再將下載下來的Loser's PS2 Disc patcher解壓縮。
    下載點(擇一下載):
    http://www.xun6.com/file/6d0dfbe21/DiscPatcher3.zip.html
    http://www.mediafire.com/file/mzwjwtljjwi/DiscPatcher3.zip
  3. 執行Loser's PS2 Disc patcher。
    在Image Filename的地方,載入上面步驟一所產生的iso檔。載入後,Loser's PS2 Disc patcher會自動判斷ps2光碟的格式及區域等資料。
  4. 請在Master disc的地方打勾。
  5. 在Region的地方,改成您主機的區域。(這裡我不是很確定,我用美國職棒大聯盟 MLB 09 The Show的iso檔,讀出來的區域是America,因為我的ps2主機是日規機,所以我改成Japan,實際上是可以跑的)
  6. Exe Filename這裡,我可是找了很多資料才會填的,這裡指的應是Loser's PS2 Disc patcher要進行patch的執行檔,那ps2光碟裡的執行檔是那一個呢?把ps2原版光碟,放到電腦裡,在根目錄下,裡面有一個SYSTEM的檔案,用記事本打開,可以看到這一行:
    BOOT2=CDROM0:\SCUS_976.44:1
    SCUS_976.44就是執行檔了,所以把它填到Exe Filename的空格裡。
  7. 再按下patch,大約過三分鐘,就完成了。再把patch完成的ISO檔,燒到光碟裡,就可以了。(我是用酒精以DVD+R用最低速燒)

    20100409001.jpg


不過是否每個型號的ps2 debugging station都能這樣使用呢?可能不同型號有不同結果。不過目前我猜想DTL-H30XXX的型號應該是可以用patch master的方式,達到直讀燒錄片的功能。

請參考下文:
I think it depends on what model it is. Some will run burned CDs and DVDs, some won't . The ASSEMblergames.com forums has a section about these units and is reasonably active. The site is mainly for collectors of obscure dev hardware like this, but some of the people on the site actually like to use them. PS2DEV may have information about your TEST unit, but I haven't been on the site much.(http://ask.metafilter.com/36181/What-can-I-do-with-a-Test-Playstation-2)

Read more...

2010年4月3日 星期六

第一次買電蚊拍,一下子就掛了

最近蚊子又變多了,樓下車庫的蚊子,真是多到爆,這些蚊子專叮我家那隻笨笨的黃金獵犬的狗鼻子,本來用補蚊燈,可是只有前一兩天有點效果,接下來的補獲量,愈來愈少,少的可憐。以前從來沒有用過電蚊拍,於是想說買來試試,看網路上很多人推薦勳風牌的電蚊拍。那天本來在市場看到一隻50元的,有一點想買,但是心想也許是大陸貨,電蚊拍這種東西,為了安全,還是買品質好一點的台灣貨。

於是到五金行,買了一支180元的勳風牌的電蚊拍,買回家沒有馬上用,放了幾天,前天拿來小試了一下,拍到了一兩隻蚊子,今天天氣熱,車庫的蚊子又成群結隊的出現。於是我就拿起電蚊拍,開始與蚊子大作戰,一開始戰果豐碩,消滅了不少敵軍。

可是大約十幾分鐘以後,電蚊拍突然失靈了,拍到蚊子了,可是蚊子還是揚長而去,這時我才發現,本來按住電蚊拍會發一種嗡嗡的聲音,現在沒聽到了。想說不會是沒電池了吧!電池才充飽電而已(我是使用充電電池),不管如何,先換電池看看,結果換了剛充飽電的電池,也沒作用。

這時我開始接受電蚊拍故障的事實,本來五金店說七天內有問題,可以換,可是因為我買來沒有馬上使用,因為前幾天,天氣較冷,蚊子並不多。今天拿出來用早已超過七天了。雖然才180元,可是心裡還是很不甘心。

找到勳風的網站,找到客服網頁,反應如下的內容,第一次用firefox發出,出現亂碼,再用ie發一次,然後收到該公司已收到訊息的回應:

本人到五金行,買了一支180元貴公司的電蚊拍,買回家沒有馬上用,放了幾天,前天第一次在室內使用,拍到了一兩隻蚊子,今天使用電蚊拍時,不知是拍太多蚊子(大約三十幾隻吧,在樓下車庫使用!),還是怎樣,大約十幾分鐘以後,電蚊拍突然失靈了,本來按住電蚊拍開關時,會發一種嗡嗡的聲音,卻沒聽到了。但燈會亮,換充飽的電池(我是使用充電電池),也沒作用。
本來五金店說七天內有問題,可以換,可是因為我買來沒有馬上使用,也超過七天了。雖然才180元,可是心裡總覺得,我是在正常使用下故障,有必要反應給貴公司知道,作為貴公司產品改進的參考。


以上的內容大約是二週前寫的,之所以沒有馬上post,是希望該公司有所回覆之後,再把他們回覆的內容,一併post上來,不過到今天4月3日,已經十幾天了,想來該公司是不會有所回應了,當初我留下email及手機,為了怕漏接手機,我連著十天,除了睡覺,手機都保持開機狀態。可是都沒有收到任何回音,只好現在就把文章發布出去了。所以不管是東西的品質問題,或是我操作失誤,都只能由讀者來評斷了,以上內容我只是平實的陳述整個過程,我想東西的品管,不可能百分之百沒瑕疵,我可能就是運氣不好,買到百分之零點零幾的不良品吧,至於電蚊拍的操作問題,我只是按下開關拍蚊子而已,沒摔到或撞到,只是車庫的蚊子真的很多,不知是不是不能連續使用太久,另外我使用的是充電電池,不知有沒有影響。

不過這支電蚊拍,拍到蚊子的聲音,霹靂拍啦的,打蚊子的效果還不錯,網路上很多人,提到有些電蚊拍,打到蚊子,只是擊昏而已。我買的這支一打到蚊子,蚊子馬上在地球消失。這倒是符合網路上很多人推薦這個牌子的原因。


照片 062.jpg

Read more...

2010年3月27日 星期六

wii常用到的軟體工具整理

最近買了一部二手的wii,這段時間都在研究這台wii。單單一台wii如果只是拿來用光碟玩遊戲,實在是沒什麼好說的。但是就有很多人,會把一台wii的功用發揮到極致,於是有人把它拿來改機,什麼是改機呢?就是在原本的wii裡面,加上一個晶片,用來避開原來wii對正版光碟的檢查,這樣這台改過機的wii就可以用來玩所謂的台片。

改機還分軟改及硬改,如同字義,硬改,就是會動到wii的內部硬體,就是將改機晶片,焊到wii的內部,這樣當然要拆開wii的盒子,也同時會失去保固。而軟改則是利用一些軟體工具,將某些程式植入wii的記憶體內。

這篇文章主要的不是要談改機,畢竟這方面,我不是這方面的專家,而且這涉及智慧財產權的問題。寫這篇文章的目的,只是把我知道的wii所有相關軟體工具,整理並列出。因為關於wii網路上的資料相當多,但也相當混亂,老實說,一開始接觸wii,真的被一大堆名詞,軟體,工具弄得頭昏眼花。

wii可以玩的花樣還真不少,例如可以安裝某些工具,利用usb裝置來玩,將你要玩的遊戲,放到usb隨身碟或行動硬碟裡。這樣就可以不必用到wii的光碟機,也一樣可以玩,而且載入程式的速度更快,也不用頻繁的換片。就因為wii可以玩的東西很多,所以相關的工具軟體也特別多,所以我就列出我所知道的相關工具,說明它的用途,並提供下載點。

WiiBrickBlocker
用來「過水」,拿掉光碟片中的自動更新的部分,避免改機的wii,更新到不同區的更新檔,因而「變磚」。

同樣的工具,還有WiiISOUpdateRemover,WiiISOUpdateRemover能讀取的ISO檔比WBB(WiiBrickBlocker)還要多,有部分遊戲片會因為拿掉更新而無法正常進入遊戲,可以先把更新備份,如果不能玩,還可以再還原。如果執行WiiISOUpdateRemover後出現"應用程式正常初始XXXXXX失敗"的相關錯誤訊息,請到微軟網站下載.NET Framework 2.0,安裝完以後就可以正常使用。(以上截自http://kissjojo99.pixnet.net/blog/post/14840849,詳細請參考該教學)

Wii_Rom_checker
用來查看分辨wii遊戲版本的軟體,使用方法:把燒好或者iso檔先掛載到虛擬光碟機,然後開啟此工具程式選擇光碟機後,就可以知道遊戲版本是日版,美版或歐版,同樣可以避免改機的wii,更新到不同區的更新檔,因而「變磚」。

Wiiscrubber+WiiPatcher
有時有些遊戲會出現Error #002,這是因為遊戲需要某些 IOS (e.g. IOS 53, 55, ....),而你部 Wii 無提供,解決Error 002問題的方法如下: (任何一個方法均可)
1.用 hbc 的wad manager 安裝 FreeTheBug版 IOS37、38、53、55
2.以HBC搭配SoftChipR87,即可不經修正ISO檔而進入遊戲
3.以HBC搭配Backup Channel Gamma 002fix,即可不經修正ISO檔而進入遊戲
4.以HBC搭配GeckoOS 1.07b 002fix,即可不經修正ISO檔而進入遊戲

另外一個方法是使用WiiScrubber + WiiPatcher V1.2,這兩個工具軟體來把error #002的檢查拿掉。
先用 WIIScrubber_V13.exe 取出遊戲內的main.dol,再用Generic WiiPatcher.exe 把 Error #002 remove掉。使用方法可參考http://kissjojo99.pixnet.net/blog/post/23190822

WBFSManager3.0
如果你的wii是用硬碟或usb碟來讀取,那就必須用到WBFS 格式的硬碟,WBFS Manager可以對硬碟或usb碟,進行格式化。也可以把wii的遊戲iso檔裝進到這個硬碟或usb碟裡,也可以將之移除。簡而言之,這個工具就是wbfs硬碟的管理軟體。不過我在使用時,有些行動硬碟的外接盒,似乎有相容性的問題,而無法正當在wii中讀取。
用法請參考http://angler.twbbs.org/read.php?80

Read more...

2010年3月19日 星期五

mysql join的效率問題,子查詢與explain分析

以前學習sql server的時候,不知在那兒得知,先在一個很大的table中篩選資料,得到一個暫存的小檔,再用此檔去進行其他運算,如join,效率會比較好。在mysql中,我使用類似的作法,就是先用子查詢,得出一個暫存的table,再進行處理。


EXPLAIN
SELECT a . * , b . seq , b . weight AS tWeight , b . loss
FROM batch_detail a
LEFT OUTER JOIN scription_main b ON a . prodSeq = b . seq
WHERE a . accSeq = 1
AND a . bNo = 3

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE a ref accSeq,bNo bNo 4 const 1 Using where
1 SIMPLE b eq_ref PRIMARY PRIMARY 4 cook.a.prodSeq 1


第二個表使用子查詢,可以看到mysql對子查詢用where篩選,先產生一個暫存檔derived2(DERIVED是一個子查詢的型態)。再和table b join。
EXPLAIN
SELECT a . * , b . seq , b . weight AS tWeight , b . loss
FROM ( SELECT prodSeq , weight AS totWeight
FROM batch_detail
WHERE accSeq = 1
AND bNo = 3 ) a
LEFT OUTER JOIN scription_main b ON a . prodSeq = b . seq


id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> system NULL NULL NULL NULL 1
1 PRIMARY b const PRIMARY PRIMARY 4 const 1
2 DERIVED batch_detail ref accSeq,bNo bNo 4 1 Using where

但使用子查詢,在經過explain分析的效率,感覺並沒有比較好。第一個表中,可以看到mysql自動對where進行優化處理,得出一個row之後,再和另一個表去join。 第二個作法,經explain分析以後,反而多出一個row。



EXPLAIN能夠分析SELECT命令的處理過程。這不僅對於決定是否要為表加上索引很有用,而且對於瞭解MySQL處理複雜連接的過程也很有用。

Read more...

2010年3月7日 星期日

開機光碟製作及軟碟、usb開機碟製作方法及下載

前一陣子,使用還原軟體還原系統,還原之後,電腦又莫名其妙的無法正常開機了,因為這種情況不常發生,所以以前曾經製作的開機光碟,經常是不知丟到那裡去了,找了半天也找不著。於是每次遇到這種情況,都必須用別台電腦,找到開機光碟下載點,再重新製作開機光碟,再開到dos模式,再使用工具來檢查並修復電腦。以前都用nero來燒,但nero需要可開機的軟碟,才可正常燒錄,而我的軟碟機早故障了,現在都是找可開機的iso檔,直接燒開機光碟,可是一時之間總很難找到可下載的iso檔。於是乾脆把所有開機碟的製作方式整理列出來,並提供下載的地方,方便自己也方便別人。

雖然目前的windows系統,從windows 98以後,大概都不支援dos的模式了,可是有些時候,我們還是有在dos命令模式下的作業需求,例如系統毀損,無法正常開機到windows 系統,需要使用dos下的修復工具或還原軟體,如ghost。或者新電腦或新硬碟買來,還沒安裝windows之前,需要先格式化或分割硬碟,要用到如format、fdisk或spfdisk等工具。

因為種種原因,我們還是必須將電腦開到dos模式。這樣就有開機光碟製作,或是usb開機碟製作的需求。本文主要整理出各種儲存媒體開機碟製作的方法。

軟碟機的開機片製作

目前最新的方法,是用usb開機,再早期一點就是開機光碟製作,使用軟式磁碟的電腦,恐怕是很舊很早期的電腦才有。但是您可別認為軟碟機已經從歷史消失了,據我所知,現在很多銀行的薪資轉帳系統,還是靠3吋半的軟碟片,還有一些政府機構的系統,也靠軟碟片來存放憑證資料,因此這裡也附帶提一下製作軟碟機開機磁片的方法,方法相當簡單,如果您的電腦是windows 98的系統,或是更早的dos系統,只要在命令列模式下,將軟碟片放入a槽磁碟機,再執行format a:/s的指令即可。另外還有一個方法,就是下載磁碟影像檔,直接將此影像檔寫入到磁片即可。

軟碟影像檔下載點:
http://www.mediafire.com/file/zif4vhzcmrd/fdboot.zip

將以上載點下載的檔案解壓縮,內有兩個執行檔,分別是dos6.22.exe及Windows98_se.exe,一個是dos6.22的dos版本,一個是windows98 se的dos版本,雙擊任一個執行之。

出現以下視窗,按確定。
20100111000.jpg


將磁片放入軟碟機,再按「確定」。
20100111001.jpg


開機光碟製作

開機光碟製作的方法,以前我都是用nero再加上dos的開機磁片,但現在3.5的磁碟機已經壞掉了,所以直接在網路上找到已經製作好的ISO檔,直接將ISO檔燒到光碟片即可用來開機了。

開機光碟下載:

http://www.mediafire.com/file/euqcntcdmnj/bootdisk.zip

usb開機碟製作

因為usb隨身碟愈來愈普遍,而且不必使用燒錄機即可製作,所以比起製作開機光碟,更方便更容易,但是要注意的是太舊的電腦,可能不支援usb開機的功能。製作usb開機光碟,我常用的兩個工具分別是hp公司出品的hpusbfw及usboot這兩個工具。

hpusbfw使用方法
下載HPUSBFW

將下載下來的檔案解壓縮,執行HPUSBFW.EXE出現如下的畫面。
請在device項目,請選擇你要做成用usb開機的那個隨身碟。
並將"Creat a DOS startup disk"打勾。
然後在底下指定系統開機檔案,將路徑指向任一存在dos開機檔案的資料夾。( hpusbfw.zip解壓縮後,有一個同名的hpusbfw的資料夾,將路徑指定在此一資料夾即可,這個資料夾內含dos開機所需的檔案 。),接著按下 start就開始格式化了。

將格式化好的usb碟,放入電腦,並將電腦的bios開機順序,設定優先使用usb開機,這樣就可以從usb開機了。

1-12.jpg


usboot的使用方法

注意使用時,會有隨身碟容量變小的問題,請小心。如果發生此一情況請參考 http://save-coco.blogspot.tw/2014/01/usb.html


hpusbfw會將usb格式為硬碟的格式,開完機會出現c:,如果執行一般的維護工作,當然是沒有問題,但如果使用在特殊情況,就不如usboot提供更多的模擬方式,例如在沒有光碟機的電腦安裝windows xp時,當然無法用開機光碟來安裝,必須使用磁片安裝,這樣就必須將usb碟格式化成軟碟機的格式才行。usboot,它可以將usb碟格式化成硬碟、zip碟及軟碟的不同模式。

下載usboot繁體中文版:下載


使用步驟如下:

  1. 解壓縮後,執行Usboot170_CHT.exe,會出現警告訊息,請先確定usb隨身碟內是否有重要資料。
  2. 出現如下主畫面之後,請先選取您要格式化的usb碟,千萬不要選錯了,否則下場會很悽慘喔!
  3. 再選擇欲格式化的模式,HDD(硬碟)、ZIP磁碟或FDD(軟碟)模式。
  4. 按下開始,即開始格式化。
    1-13.jpg
  5. 操作過程中,會要求拔出usb碟再插入。
    2009-01-13 08-53-34.jpg
  6. 完成後一個usb開機碟就完成了。
  7. 2009-01-13 08-54-49.jpg

結論

以上是就我所知道的軟碟開機片製作,開機光碟製作,及usb開機碟製作的方法,也許還有我不知道的其他方法。因為有時久久一次,會有使用到DOS的需求,就要到處去找相關的工具及下載點,所以乾脆就寫成一篇文章,提供開機光碟下載點,方便自己下載使用,也方便網友製作開機光碟片等需求。

Read more...

2010年3月3日 星期三

javascript onload事件的用法(個人自習筆記)

onload事件觸發的function,可以寫在body的onload事件中。

例如:
<body onLoad="init();">
<div id="content">It is a book !</div>
</body>


也可以寫在javascript中:
window.onload = onloadHandler

如下:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html><!-- InstanceBegin template="/Templates/main.dwt.php" codeOutsideHTMLIsLocked="false" --><head>
<!-- InstanceBeginEditable name="doctitle" -->
<title>建立原料名稱</title>

<script type="text/javascript" language="javascript">

var LaunchElement = null;
var SortColumn = -1;
var SortOrder = 1;

if (document.addEventListener)
{
document.addEventListener("DOMContentLoaded", onloadHandler, false);
}
else
{
window.onload = onloadHandler;
}

function onloadHandler()
{


tSeq=getQueryString("modseq");

if (!empty(tSeq)){
try
{
parent.document.getElementById("dtable").style.visibility="hidden";
parent.document.getElementById("extable").style.visibility="hidden";
parent.document.getElementById("metable").style.visibility="visible";
parent.document.getElementById("modCancel").style.visibility="visible";
}
catch (ex) {}
} else{
try
{
parent.document.getElementById("dtable").style.visibility="visible";
parent.document.getElementById("extable").style.visibility="visible";
parent.document.getElementById("metable").style.visibility="hidden";
parent.document.getElementById("modCancel").style.visibility="hidden";
}
catch (ex) {}
}

try
{
document.getElementById("TagTable").style.width = window.frameElement.parentNode.clientWidth + "px";
document.getElementById("FileTable").parentNode.style.width = window.frameElement.parentNode.clientWidth + "px";
}
catch (ex) {}

document.getElementById("se_cat").onchange = function (e) {onchangeHandler(e);};
}

function onchangeHandler(e)
{
var srcElement = window.event ? window.event.srcElement : e.target;
if (srcElement.tagName == "SELECT")
{
doSubmit();
}
}

function doSubmit()
{
var QueryString = "?";
QueryString += "se_cat=" + encodeURIComponent(document.getElementById("se_cat").value);
window.location.replace("new_material.php"+QueryString);
}
</script>

Read more...

2010年2月26日 星期五

window.location.href和window.location.replace的區別,以acer網站為例

有3個jsp頁面(1.jsp, 2.jsp, 3.jsp),進系統默認的是1.jsp ,當我進入2.jsp的時候, 2.jsp裡面用window.location.replace("3.jsp");與用window.location.href("3.jsp");從用戶界面來看是沒有什麼區別的,但是當3.jsp頁面有一個「返回」按鈕,調用window.history.go(-1);wondow.history.back();方法的時候,一點這個返回按鈕就要返回2.jsp頁面的話,區別就出來了。

當用window.location.replace("3.jsp");連到3.jsp頁面的話,3.jsp頁面中的調用window.history.go(-1);wondow.history.back();方法是不好用的,會返回到1.jsp 。

當用window.location.href("3.jsp");連到3.jsp頁面的話,3.jsp頁面中的調用window.history.go(-1);wondow.history.back();方法是好用的,會返回2.jsp。

因為window.location.replace("3.jsp");是不向服務器發送請求的跳轉,而window.history.go(-1);wondow.history.back();方法是根據服務器記錄的請求決定該跳到哪個頁面的,所以會跳到系統默認頁面1.jsp 。

window.location.href("3.jsp");是向服務器發送請求的跳轉,window.history.go(-1); wondow.history.back();方法是根據服務器記錄的請求決定該跳到哪個頁面的,所以就可以返回到2.jsp。

以上參考window.location.href和window.location.replace的区别

以上簡單的說,如果使用window.location.replace,使用瀏覽器上一頁的按鈕,是不會回到呼叫window.location.replace的那個頁面,而是回到window.location.replace呼叫前的那個頁面。這個運用在跳頁選單中,每按一次下拉式選單,就會使用window.location.replace跳到一個新頁面(事實上是同一個頁面,只是傳值不同而已),如果用window.location.href來做,那麼如果你很頻繁的使用跳頁選單來查詢,那麼當你按上一頁時,看起來都會是回到同一個頁面,如果使用window.location.replace的話,那麼按上一頁,看起來才像是回到上個頁面中。

如果不能體會的話,看看acer網站的例子
http://www.acer.com.tw/acer/service.do?LanguageISOCtxParam=zh&miu10einu24.current.attN2B2F2EEF=3212&sp=page15e&ctx2.c2att1=238&miu10ekcond13.attN2B2F2EEF=3212&CountryISOCtxParam=TW&ctx1g.c2att92=917&ctx1.att21k=1&CRC=4153026338

你可以查詢acer產品的下載檔案,無論您使用跳頁選單,如何多次查詢(每次查詢,事實上都產生一個新頁面),當按回上一頁時,都能跳出acer的網站,回到您進到此網站的前一個頁面。這就是使用window.location.replace的效果。

Read more...

2010年2月19日 星期五

由javascript中取得get參數的example

在php讀取傳入的get參數很簡單,只要用$_GET就可取得。但在javascript中,卻沒有直接可用的函數,因此只能自定義一個函數。

以下為例:
當連結到某一網頁,如http://www.abc.com/test.php?modseq=100

可以在test.php的網頁的javascript中,使用下列語法,取得modseq的get參數值,即tSeq=100

tSeq=getQueryString("modseq");

function getQueryString( paramName ){
  paramName = paramName .replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]").toLowerCase();
  var reg = "[\\?&]"+paramName +"=([^&#]*)";
  var regex = new RegExp( reg );
  var regResults = regex.exec( window.location.href.toLowerCase() );
  if( regResults == null ) return "";
  else return regResults [1];
}

Read more...

2010年2月10日 星期三

如何在php程式中,用Gmail的mail server 發信(php 5.2.12)

在PHP程式中,利用程式自動發信的作法,可以採用phpMailer這個免費的class。在我的使用環境中,php的版本是win32 5.2.12

phpMailer 官方網站
http://phpmailer.codeworxtech.com/

下載 phpMailer
http://phpmailer.codeworxtech.com/index.php?pg=sf&p=dl

依據您使用的 PHP 版本之不同,來下載適合的class。
下載完畢後請解壓縮到指定資料夾中(我把它放在function的資料夾內),


function phpMail($subject,$body,$to){
require_once("function/class.phpmailer.php");
require_once("function/class.smtp.php");

$mail = new PHPMailer();
$mail->IsSMTP();
$mail->SMTPAuth = true; //設定SMTP需要驗證
$mail->SMTPSecure = "ssl"; // Gmail的SMTP主機需要使用SSL連線
$mail->Host = "smtp.gmail.com"; //Gamil的SMTP主機
$mail->Port = 465; //Gamil的SMTP主機的SMTP埠位為465埠。

$mail->Username = "xxxxxx"; //設定驗證帳號
$mail->Password = "xxxxxx"; //設定驗證密碼
$mail->From = "12345@gmail.com"; //設定寄件者信箱
$mail->FromName = "test"; //設定寄件者姓名

//設定收件者
$mail->AddAddress(33333@abc.com);
//設定密件副本
//$mail->AddBCC("55555@abc.com");

//設定信件字元編碼
$mail->CharSet="utf-8";
//設定信件編碼,大部分郵件工具都支援此編碼方式
$mail->Encoding = "base64";
//設置郵件格式為HTML
$mail->IsHTML(true);
//每50自斷行
$mail->WordWrap = 50;

//傳送附檔
//$mail->AddAttachment("upload/temp/filename.zip");
//傳送附檔的另一種格式,可替附檔重新命名
//$mail->AddAttachment("upload/temp/filename.zip", "newname.zip");

//郵件主題
$mail->Subject="abc";
//郵件內容
$mail->Body = "def";

//附加內容
// $mail->AltBody = '這是附加的信件內容';

//寄送郵件
if(!$mail->Send())
return "郵件無法順利寄出! Mailer Error: ".$mail->ErrorInfo;
}

要注意PHP必須支援SSL,才可用gmail寄信。在win32的php系統中,只要在安裝php的時候,將OpenSSL的dll安裝進來即可。若安裝時未安裝此一dll,再重新執行一次php安裝程式,在repair,remove,change中,選擇change的項目,再把OpenSSL加進來即可。
20100125000.jpg

Read more...

Back to TOP