目前共有12篇帖子。
【函數庫】PHP RXData(RPGXP數據文件) 解析庫 V1.0
1樓 巨大八爪鱼 2016-2-8 17:55
文件名:rxdata_library.php
<?php  
/**
** PHP RXData 解析庫 V1.0
**  
** 本函數庫是巨大八爪魚編寫的
** 使用和轉載時請保留此信息
**/
 
define('MARSHAL_MAJOR', 4);
define('MARSHAL_MINOR', 8);
 
define('RBT_NIL', '0');
define('RBT_TRUE', 'T');
define('RBT_FALSE', 'F');
define('RBT_FIXNUM', 'i');
define('RBT_EXTENDED', 'e');
define('RBT_UCLASS', 'C');
define('RBT_OBJECT', 'o');
define('RBT_DATA', 'd');
define('RBT_USERDEF', 'u');
define('RBT_USRMARSHAL', 'U');
define('RBT_FLOAT', 'f');
define('RBT_BIGNUM', 'l');
define('RBT_STRING', '"');
define('RBT_REGEXP', '/');
define('RBT_ARRAY', '[');
define('RBT_HASH', '{');
define('RBT_HASH_DEF', '}');
define('RBT_STRUCT', 'S');
define('RBT_MODULE_OLD', 'M'); // 該類型為老版格式,已棄用
define('RBT_CLASS', 'c');
define('RBT_MODULE', 'm');
define('RBT_SYMBOL', ':'); // 符號對象
define('RBT_SYMLINK', ';'); // 鏈接到符號對象
define('RBT_IVAR', 'I');
define('RBT_LINK', '@'); // 鏈接到非符號對象
 
define('ONIG_OPTION_IGNORECASE', 0x01);
define('ONIG_OPTION_EXTEND', 0x02);
define('ONIG_OPTION_MULTILINE', 0x04);
 
define('SIZEOF_CHAR', 1);
define('SIZEOF_DOUBLE', 8);
define('SIZEOF_INT', 2);
define('SIZEOF_LONG', 4); // 在C語言中,int和long都是4個位元組...
define('SIZEOF_LONGLONG', 8);
 
class RubyClass {
    public $name;
}
 
class RubyModule extends RubyClass {}
 
class RubyNil {}
 
class RubyObject {
    public $_name = '';
    public $_length = 0;
    public $_extension = '';
    public $_data = array();
    public function __get($property) {
        if (is_numeric($this->_length)) {
            return $this->_data[$property];
        } else {
            if (isset($this->_data['extension'][$property])) {
                return $this->_data['extension'][$property];
            } else if (isset($this->data['base'][$property])) {
                return $this->_data['base'][$property];
            } else {
                return NULL;
            }
        }
    }
}
 
class RubySymbol {
    public $name;
    public function getMember(RubyObject $obj) {
        $member = $this->getMemberName();
        return $obj->_data[$member];
    }
    public function getMemberName() {
        if ($this->isMember()) {
            return substr($this->name, 1);
        } else {
            return $this->name;
        }
    }
    // 判斷該符號對象是不是一個類的屬性名稱
    public function isMember() {
        return ($this->name{0} == '@');
    }
    public function setMember(RubyObject $obj, $value) {
        $member = $this->getMemberName();
        $obj->_data[$member] = $value;
    }
}
 
/* 根據文件名打開文件並讀取一個項目,返回讀取的對象並關閉文件 */
function rxdata_load($filename) {
    $file = fopen($filename, 'rb');
    if ($file === false) {
        return NULL;
    }
    $data = rxdata_load_one($file);
    fclose($file);
    return $data;
}
 
/* 根據文件名打開文件並讀取全部項目,通過數組返回後關閉文件 */
function rxdata_load_all($filename) {
    $file = fopen($filename, 'rb');
    if ($file === false) {
        return NULL;
    }
    $data = array();
    while ($obj = rxdata_load_one($file)) {
        array_push($data, $obj);
    }
    return $data;
}
 
/* 從打開的文件中讀取一個項目 */
function rxdata_load_one($file) {
    $data = NULL;
    if (rxdata_read_header($file)) {
        rxdata_reset(); // 移除之前的符號表
        $data = rxdata_read($file);
    }
    return $data;
}
 
 
 
 
/* 讀取文件中接下來的符號對象或符號鏈接所指向的符號對象,以該符號的名稱作為對象名,創建並註冊對象,返回所創建的對象 */
function rxdata_prepare_object($file, $has_length = true) {
    // 在Ruby中,類名和變量名都是符號,且同名符號只能在內存中存在一次
    $sym = rxdata_read_symbol_block($file);
    $obj = new RubyObject();
    $obj->_name = $sym->name;
    if ($has_length) {
        $obj->_length = rxdata_read_long($file);
    }
    rxdata_register_object($obj); // 創建對象後立即註冊對象(的引用),以便於之後能夠鏈接到該對象
    return $obj;
}
 
function rxdata_read($file) {
    $type = fgetc($file);
    switch ($type) {
    case RBT_ARRAY:
        return rxdata_read_array($file);
    case RBT_BIGNUM:
        return rxdata_read_bignum($file);
    case RBT_CLASS:
        return rxdata_read_class($file);
    case RBT_EXTENDED:
        return rxdata_read_extended($file);
    case RBT_FALSE:
        return false;
    case RBT_FIXNUM:
        return rxdata_read_fixnum($file);
    case RBT_FLOAT:
        return rxdata_read_float($file);
    case RBT_HASH:
        return rxdata_read_hash($file);
    case RBT_HASH_DEF:
        return rxdata_read_hash($file, true);
    case RBT_IVAR:
        return rxdata_read_ivar($file);
    case RBT_LINK:
        return rxdata_read_link($file);
    case RBT_MODULE:
        return rxdata_read_module($file);
    case RBT_NIL:
        return new RubyNil();
    case RBT_OBJECT:
    case RBT_STRUCT:
        return rxdata_read_object($file);
    case RBT_REGEXP:
        return rxdata_read_regexp($file);
    case RBT_TRUE:
        return true;
    case RBT_STRING:
        return rxdata_read_string($file, true);
    case RBT_SYMBOL:
        return rxdata_read_symbol($file);
    case RBT_SYMLINK:
        return rxdata_read_symlink($file);
    case RBT_UCLASS:
        return rxdata_read_uclass($file);
    case RBT_USERDEF:
        return rxdata_read_userdef($file);
    case RBT_USRMARSHAL:
        return rxdata_read_usrmarshal($file);
    default:
        trigger_error(sprintf('文件中0x%x處含有無法識別的Ruby數據類型: \'%s\' (0x%02X)', ftell($file), $type, ord($type)), E_USER_WARNING);
        return NULL;
    }
}
 
function rxdata_read_array($file) {
    $arr = array();
    rxdata_register_object($arr);
    $len = rxdata_read_long($file);
    for ($i = 0; $i < $len; $i++) {
        $arr[$i] = rxdata_read($file);
    }
    return $arr;
}
 
/* 從文件中讀取一個位元組 */
/* 此函數讀出來的位元組一定是無符號十六進制整數 */
function rxdata_read_byte($file) {
    $ch = ord(fgetc($file));
    return $ch;
}
 
function rxdata_read_bignum($file) {
    $sign = fgetc($file);
    $n = rxdata_read_long($file);
    $num = rxdata_read_ulong($file, 2 * $n);
    if ($sign == '-') {
        $num = -$num;
    }
    rxdata_register_object($num);
    return $num;
}
 
function rxdata_read_class($file) {
    $class = new RubyClass();
    rxdata_register_object($class);
    $class->name = rxdata_read_string($file);
    return $class;
}
 
function rxdata_read_extended($file) {
    $ex = rxdata_read_symbol_block($file);
    $obj = rxdata_read($file);
    if (is_object($obj)) {
        $obj->_extension = $ex->name;
        return $obj;
    } else {
        $package = new RubyObject();
        $package->_length = count($obj);
        $package->_extension = $ex->name;
        $package->_data = $obj;
        rxdata_registry_replace($obj, $package); // 重定向連接表中的註冊信息
        return $package;
    }
}
 
/*
注意:
    使用rxdata_read_fixnum讀取一個Fixnum對象
    使用rxdata_read_long讀取任意對象原始二進制數據中的長整數值(例如String對象中表示字符串長度的長整數)
*/
function rxdata_read_fixnum($file) {
    // 在Ruby Marshal中,Fixnum類型的數據無需註冊
    return rxdata_read_long($file);
}
 
function rxdata_read_float($file) {
    $str = rxdata_read_string($file);
    if (in_array($str, array('nan', 'inf', '-inf'))) {
        $data = $str;
    } else {
        $data = (float)$str;
    }
    rxdata_register_object($data);
    return $data;
}
 
function rxdata_read_hash($file, $hasDefaultValue = false) {
    $len = rxdata_read_long($file);
    $hash = array();
    if ($hasDefaultValue) {
        $obj = array(&$hash);
    } else {
        $obj = &$hash;
    }
    rxdata_register_object($obj);
    for ($i = 0; $i < $len; $i++) {
        $key = rxdata_read($file);
        $hash[$key] = rxdata_read($file);
    }
    if ($hasDefaultValue) {
        $obj[1] = rxdata_read($file);
    }
    return $obj;
}
 
/* 讀取Marshal數據頭 */
function rxdata_read_header($file) {
    $oldpos = ftell($file);
    $a = rxdata_read_byte($file);
    $b = rxdata_read_byte($file);
    if ($a == MARSHAL_MAJOR && $b == MARSHAL_MINOR) {
        return true; // 如果讀取成功則返回true,且指針移動到數據頭的後面
    } else {
        fseek($file, $oldpos); // 如果讀取失敗,則退回到讀取前的位置,並返回false
        return false;
    }
}
 
function rxdata_read_ivar($file) {
    $obj = rxdata_read($file);
    $base_len = $obj->_length;
    $base_data = $obj->_data;
    $obj->_length = rxdata_read_long($file);
    $obj->_data = array();
    
    rxdata_read_object($file, $obj);
    $obj->_length = array('base' => $base_len, 'extension' => $obj->_length);
    $obj->_data = array('base' => $base_data, 'extension' => $obj->_data);
    return $obj;
}
 
/* 鏈接到位元組流中的非符號對象 */
function rxdata_read_link($file) {
    $id = rxdata_read_long($file);
    return rxdata_fetch_object($id);
}
 
/* 讀取一個經過Ruby Marshal特殊處理的有符號長整數(不含類型標記) */
function rxdata_read_long($file) {
    $ch = to_signed(rxdata_read_byte($file));
    if ($ch == 0) {
        return 0;
    }
    if ($ch > 0) {
        if ($ch > 4 && $ch < 128) {
            return $ch - 5;
        }
        $x = 0;
        for ($i = 0; $i < $ch; $i++) {
            $x |= to_signed(rxdata_read_byte($file), SIZEOF_LONG) << (8 * $i);
        }
    } else {
        if ($ch > -129 && $ch < -4) {
            return $ch + 5;
        }
        $ch = -$ch;
        $x = -1;
        for ($i = 0; $i < $ch; $i++) {
            $x &= ~(0xff << (8 * $i));
            $x |= to_signed(rxdata_read_byte($file), SIZEOF_LONG) << (8 * $i);
        }
    }
    return $x;
}
 
function rxdata_read_module($file) {
    $module = new RubyModule();
    rxdata_register_object($module);
    $module->name = rxdata_read_string($file);
    return $module;
}
 
// 如果指定了參數$obj,則此函數隻讀取成員變量
function rxdata_read_object($file, $obj = NULL) {
    if (is_null($obj)) {
        $obj = rxdata_prepare_object($file);
    }
    for ($i = 0; $i < $obj->_length; $i++) {
        $memsym = rxdata_read_symbol_block($file);
        $memsym->setMember($obj, rxdata_read($file, $obj));
    }
    return $obj;
}
 
function rxdata_read_regexp($file) {
    $reg = '/' . rxdata_read_string($file) . '/';
    $options = rxdata_read_byte($file);
    if ($options & ONIG_OPTION_IGNORECASE) {
        $reg .= 'i';
    }
    if ($options & ONIG_OPTION_MULTILINE) {
        $reg .= 'm';
    }
    if ($options & ONIG_OPTION_EXTEND) {
        $reg .= 'x';
    }
    rxdata_register_object($reg);
    return $reg;
}
 
function rxdata_read_string($file, $register = false) {
    $len = rxdata_read_long($file);
    if ($len > 0) {
        $str = fread($file, $len);
    } else {
        $str = '';
    }
    if ($register) {
        rxdata_register_object($str);
    }
    return $str;
}
 
function rxdata_read_symbol($file) {
    $symbol = new RubySymbol();
    $symbol->name = rxdata_read_string($file);
    rxdata_register_symbol($symbol); // 註冊該符號,以便於之後鏈接回來。因為在內存中,同名符號對象只允許存在一次
    return $symbol;
}
function rxdata_read_symbol_block($file) {
    $type = fgetc($file);
    if ($type == RBT_SYMBOL) {
        return rxdata_read_symbol($file);
    } else if ($type == RBT_SYMLINK) {
        return rxdata_read_symlink($file);
    } else {
        return NULL;
    }
}
function rxdata_read_symlink($file) {
    $id = rxdata_read_long($file);
    return rxdata_fetch_symbol($id);
}
 
function rxdata_read_uclass($file) {
    $obj = rxdata_prepare_object($file, false);
    $next_registry = count($GLOBALS['_RBOBJLIST']);
    $obj->_data = rxdata_read($file);
    rxdata_unregister_object($next_registry); // 由於$obj已經註冊,所以需要刪除重複註冊的$obj->_data
    $obj->_length = count($obj->_data);
    return $obj;
}
 
/* 獲取一個由固定n個位元組(小端序)表示的無符號整數 */
function rxdata_read_ulong($file, $n = SIZEOF_LONG) {
    $num = 0;
    for ($i = 0; $i < $n; $i++) {
        $v = rxdata_read_byte($file);
        $num += ($v << ($i * 8));
    }
    return $num;
}
 
// 注意:由於$obj已經註冊,所以返回時必須返回$obj,不能返回其他變量
function rxdata_read_userdef($file) {
    $obj = rxdata_prepare_object($file);
    switch ($obj->_name) {
    case 'Table':
        // Table是RGSS自帶的內部類,不是Ruby自帶的類
        // 地圖圖層的三維數組的結構是: data[x坐標, y坐標, 地圖id]
        $dim = rxdata_read_ulong($file); // 數組的維數
        $xsize = rxdata_read_ulong($file);
        $ysize = rxdata_read_ulong($file);
        $zsize = rxdata_read_ulong($file);
        $total = rxdata_read_ulong($file);
        
        $arr = array();
        for ($i = 0; $i < $total; $i++) {
            $value = to_signed(rxdata_read_ulong($file, SIZEOF_INT), SIZEOF_INT);
            if ($dim == 1) {
                $arr[$i] = $value;
            } else if ($dim == 2) {
                $x = $i % $xsize;
                $y = (int)($i / $xsize);
                if (!isset($arr[$x])) {
                    $arr[$x] = array();
                }
                $arr[$x][$y] = $value;
            } else if ($dim == 3) {
                $x = $i % $xsize;
                $y = (int)($i % ($xsize * $ysize) / $xsize);
                $z = (int)($i / ($xsize * $ysize));
                if (!isset($arr[$x])) {
                    $arr[$x] = array();
                }
                if (!isset($arr[$x][$y])) {
                    $arr[$x][$y] = array();
                }
                $arr[$x][$y][$z] = $value;
            }
        }
        $obj->_data = $arr;
        break;
    case 'Color':
    case 'Tone':
        $format = 'dred/dgreen/dblue/d';
        if ($obj->_name == 'Color') {
            $format .= 'alpha';
        } else {
            $format .= 'gray';
        }
        $data = fread($file, 4 * SIZEOF_DOUBLE);
        $obj->_data = unpack($format, $data);
        break;
    default:
        /* 輸出範例:
        ** Array ( [0] => RubyObject Object ( [_name] => Color [_length] => 32 [_data] => Array ( ) [data] => 00 00 00 00 00 C0 58 40 00 00 00 00 00 00 59 40 00 00 00 00 00 C0 62 40 00 00 00 00 00 E0 6F 40 ) )  
        **/
        $data = fread($file, $obj->_length);
        trigger_error("無法解析的自定義對象類型\"{$obj->_name}\"", E_USER_NOTICE);
        $obj->_data = '';
        for ($i = 0; $i < $obj->_length; $i++) {
            $obj->_data .= sprintf("%02X ", ord($data{$i}));
        }
        $obj->_data = rtrim($obj->_data);
    }
    return $obj;
}
 
function rxdata_read_usrmarshal($file) {
    $obj = rxdata_prepare_object($file, false);
    $obj->_length = 1;
    $obj->_data = rxdata_read($file);
    return $obj;
}
 
/* Ruby鏈接列表相關函數 */
$_RBOBJLIST = array(); // 當前位元組流中出現的所有非符號對象
$_RBSYMLIST = array(); // 當前位元組流中出現的所有符號對象
 
// 獲取已註冊的非符號對象
function rxdata_fetch_object($id) {
    global $_RBOBJLIST;
    if (isset($_RBOBJLIST[$id])) {
        return $_RBOBJLIST[$id];
    } else {
        trigger_error(sprintf('引用未註冊的非符號對象ID: %d,已註冊的最大ID為: %d', $id, count($_RBOBJLIST) - 1), E_USER_WARNING);
        return NULL;
    }
}
 
// 獲取已註冊的符號對象
function rxdata_fetch_symbol($id) {
    global $_RBSYMLIST;
    if (isset($_RBSYMLIST[$id])) {
        return $_RBSYMLIST[$id];
    } else {
        trigger_error(sprintf('引用未註冊的符號對象ID: %d,已註冊的最大ID為: %d', $id, count($_RBSYMLIST) - 1), E_USER_WARNING);
        return NULL;
    }
}
 
// 註冊非符號對象
function rxdata_register_object(&$obj) {
    global $_RBOBJLIST;
    $id = count($_RBOBJLIST);
    $_RBOBJLIST[$id] = &$obj;
    return $id;
}
 
// 註冊符號對象
function rxdata_register_symbol(RubySymbol $sym) {
    global $_RBSYMLIST;
    $id = count($_RBSYMLIST);
    $_RBSYMLIST[$id] = $sym; // $sym本身就是一個php對象,所以這裡無需加上引用符號
    return $id;
}
 
// 替換非符號對象註冊信息
function rxdata_registry_replace(&$old, &$new) {
    global $_RBOBJLIST;
    foreach ($_RBOBJLIST as $i => $v) {
        if ($v === $old) {
            $_RBOBJLIST[$i] = &$new;
        }
    }
}
 
// 清空上一個位元組流的所有鏈接列表
function rxdata_reset() {
    $GLOBALS['_RBOBJLIST'] = array();
    $GLOBALS['_RBSYMLIST'] = array();
}
 
function rxdata_unregister_object($id = NULL) {
    global $_RBOBJLIST;
    if (is_null($id)) {
        // 取消註冊最近註冊的對象
        array_pop($_RBOBJLIST);
    } else {
        // 取消註冊指定序號的對象
        unset($_RBOBJLIST[$id]);
        $_RBOBJLIST = array_values($_RBOBJLIST);
    }
}
 
 
 
/* 將無符號十六進制數轉換為有符號十進制整數, 參數$bytes規定了有符號整數類型的位元組數 */
/* 在marshal.c中,宏SIGN_EXTEND_CHAR(n)就相當於這裡的to_signed(n) */
function to_signed($hex, $bytes = SIZEOF_CHAR) {
    $mask = 1 << ($bytes * 8 - 1);
    return ($hex ^ $mask) - $mask;
}
 
/* 將有符號十進制整數轉換為無符號十六進制整數 */
function to_unsigned($dex, $bytes = SIZEOF_CHAR) {
    $mask = 1 << ($bytes * 8 - 1);
    return ($dex + $mask) ^ $mask;
}
 
// 在64位php中,整數1的十六進制是0x01,而整數-1的十六進制數是0xffffffffffffffff
// 因此,如果想要char a = -1的十六進制值,只需在php中寫上-1 & 0xff即可
// echo '0x', dechex(-1 & 0xff); // 輸出0xff
// 使用to_unsigned函數也能達到同樣的目的: echo to_unsigned(-1, SIZEOF_CHAR);
// 此外,php還提供了unpack函數讀取二進制字符串表示的無符號整數
2樓 巨大八爪鱼 2016-2-8 18:00
函數庫中的三個重要函數:
rxdata_load($filename)
根據文件名打開文件並讀取一個項目,返回讀取的對象並關閉文件。

rxdata_load_all($filename)
根據文件名打開文件並讀取全部項目,通過數組返回後關閉文件。
如果文件打開失敗,返回NULL。如果文件打開成功但讀取失敗,則返回空數組。

rxdata_load_one($file)
從打開的文件中讀取一個項目,$file為fopen打開的文件。

注意:
如果讀到的是Ruby的nil對象,函數返回PHP的RubyNil對象。
如果讀取失敗或遇到文件結束,則返回NULL。
3樓 巨大八爪鱼 2016-2-8 18:03
一般情況下,RMXP工程中的Data文件夾中的每個rxdata文件都只含有一個項目,而Save存檔的rxdata文件中則含有多個項目。
讀取Scripts.rxdata時,如果需要獲取其中的Ruby腳本內容,只需執行php的gzuncompress函數即可完成解碼。
4樓 巨大八爪鱼 2016-2-8 18:10
5樓 巨大八爪鱼 2016-2-8 18:16

【示例代碼1:讀取一個存檔文件的內容】
<?php
include_once('rxdata_library.php');
$maps = rxdata_load_all('project/Save1.rxdata');
print_r($maps);
?>
【運行結果】

6樓 巨大八爪鱼 2016-2-8 18:18
【示例代碼2:讀取Data文件夾下一個普通的rxdata文件】
<?php
include_once('rxdata_library.php');
$maps = rxdata_load('project/Data/MapInfos.rxdata');
print_r($maps);
?>
【運行結果】

7樓 巨大八爪鱼 2016-2-8 18:21
【示例代碼3:打開MapInfos.rxdata文件並輸出其中存儲的RPGXP工程中所有地圖的名稱】
<?php
include_once('rxdata_library.php');
$maps = rxdata_load('project/Data/MapInfos.rxdata');
foreach ($maps as $map) {
 echo $map->name, '<br>';
}
?>
【運行結果】

8樓 巨大八爪鱼 2016-2-8 18:29

【示例代碼4:輸出所有地圖的名稱和大小等信息】
<?php
include_once('rxdata_library.php');
$maps = rxdata_load('project/Data/MapInfos.rxdata');
foreach ($maps as $map_id => $map) {
 $map_info = rxdata_load(sprintf('project/Data/Map%03d.rxdata', $map_id));
 $events_num = count($map_info->events);
 if (isset($map_info->events[1])) {
  $first_event_name = $map_info->events[1]->name;
 } else {
  $first_event_name = '無';
 }
 printf('地圖: %s, 尺寸: %dx%d, 事件數: %d, 第一個事件的名稱: %s<br>', $map->name, $map_info->width, $map_info->height, $events_num, $first_event_name);
}
?>

【運行結果】

9樓 巨大八爪鱼 2016-2-8 18:36
【示例代碼5:以表格的形式輸出工程中的所有地圖】
<?php
include_once('rxdata_library.php');
$maps = rxdata_load('Data/MapInfos.rxdata');
?>
<!doctype html>
<html>
<head>
<title>地圖列表</title>
<style>
body {
 font-family: Arial, Helvetica;
 font-size: 14px;
}
</style>
</head>
<body>
<table border="1">
  <tr>
    <td>地圖ID</td>
    <td>地圖名稱</td>
    <td>地圖尺寸</td>
    <td>事件數量</td>
  </tr>
<?php
$i = 0;
foreach ($maps as $id => $map) {
 $mapfile = rxdata_load(sprintf("Data/Map%03d.rxdata", $id));
?>
  <tr>
    <td><?=$id?></td>
    <td><?=$map->name?></td>
    <td><?=$mapfile->width?>x<?=$mapfile->height?></td>
    <td><?=count($mapfile->events)?></td>
  </tr>
<?php } ?>
  <tr>
    <td colspan="4">共有<span style="color:red"><?=count($maps)?></span>張地圖。</td>
  </tr>
</table>
</body>
</html>
【運行結果】
10樓 巨大八爪鱼 2016-2-8 18:40

【示例代碼6:讀取腳本內容】
<?php
include_once('rxdata_library.php');
$scripts = rxdata_load('project/Data/Scripts.rxdata');
$txt = gzuncompress($scripts[1][2]);
echo nl2br($txt); // 把\n轉換為<br>
?>
【運行結果】

回復帖子

內容:
用戶名: 您目前是匿名發表
驗證碼:
 
 
©2010-2024 Arslanbar [手機版] [桌面版]
除非另有聲明,本站採用創用CC姓名標示-相同方式分享 3.0 Unported許可協議進行許可。